Skip to content

Commit cf3daf0

Browse files
committed
Bug fixes to the updated to_decimal algorithm.
1 parent e8dce52 commit cf3daf0

File tree

1 file changed

+121
-112
lines changed

1 file changed

+121
-112
lines changed

lexical-write-float/src/algorithm.rs

Lines changed: 121 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -466,8 +466,9 @@ pub fn compute_nearest_shorter<F: RawFloat>(float: F) -> ExtendedFloat80 {
466466
let lower_threshold: i32 = -floor_log5_pow2_minus_log5_3(bits + 4) - 2 - bits;
467467
let upper_threshold: i32 = -floor_log5_pow2(bits + 2) - 2 - bits;
468468

469-
if exponent >= lower_threshold && exponent <= upper_threshold {
470-
significand = RoundMode::Round.break_rounding_tie(significand);
469+
let round_down = RoundMode::Round.prefer_round_down(significand);
470+
if round_down && exponent >= lower_threshold && exponent <= upper_threshold {
471+
significand -= 1;
471472
} else if significand < xi {
472473
significand += 1;
473474
}
@@ -525,24 +526,27 @@ pub fn compute_nearest_normal<F: RawFloat>(float: F) -> ExtendedFloat80 {
525526
// Must be Round since we only use compute_round with a round-nearest direction.
526527
let interval_type = IntervalType::Symmetric(is_even);
527528

528-
// Short-circuit case.
529-
let short_circuit = || {
530-
let (mant, exp) = F::process_trailing_zeros(significand, minus_k + F::KAPPA as i32 + 1);
531-
extended_float(mant, exp)
532-
};
533-
534529
// Check for short-circuit.
530+
// We use this, since the `goto` statements in dragonbox are unidiomatic
531+
// in Rust and lead to unmaintainable code. Using a simple closure is much
532+
// simpler, however, we do store a boolean in some cases to determine
533+
// if we need to short-circuit.
534+
let mut should_short_circuit = true;
535535
if r < deltai {
536536
// Exclude the right endpoint if necessary.
537537
let include_right = interval_type.include_right_endpoint();
538538
if r == 0 && !include_right && is_z_integer {
539539
significand -= 1;
540540
r = big_divisor;
541-
} else {
542-
return short_circuit();
541+
should_short_circuit = false;
543542
}
544-
} else if r == deltai {
543+
} else if r > deltai {
544+
should_short_circuit = false;
545+
} else {
545546
// r == deltai; compare fractional parts.
547+
// Due to the more complex logic in the new dragonbox algorithm,
548+
// it's much easier logically to store if we should short circuit,
549+
// the default, and only mark
546550
let two_fl = two_fc - 1;
547551
let include_left = interval_type.include_left_endpoint();
548552

@@ -553,54 +557,60 @@ pub fn compute_nearest_normal<F: RawFloat>(float: F) -> ExtendedFloat80 {
553557
// x is not an integer, so if z^(f) >= delta^(f) (even parity), we in fact
554558
// have strict inequality.
555559
let parity = F::compute_mul_parity(two_fl, &pow5, beta).0;
556-
if parity {
557-
return short_circuit();
560+
if !parity {
561+
should_short_circuit = false;
558562
}
559563
} else {
560564
let (xi_parity, x_is_integer) = F::compute_mul_parity(two_fl, &pow5, beta);
561-
if xi_parity || x_is_integer {
562-
return short_circuit();
565+
if !xi_parity && !x_is_integer {
566+
should_short_circuit = false
563567
}
564568
}
565569
}
566570

567-
// Step 3: Find the significand with the smaller divisor
568-
significand *= 10;
569-
570-
let dist = r - (deltai / 2) + (small_divisor / 2);
571-
let approx_y_parity = ((dist ^ (small_divisor / 2)) & 1) != 0;
572-
573-
// Is dist divisible by 10^kappa?
574-
let (dist, is_dist_div_by_kappa) = F::check_div_pow10(dist);
575-
576-
// Add dist / 10^kappa to the significand.
577-
significand += dist as u64;
578-
579-
if is_dist_div_by_kappa {
580-
// Check z^(f) >= epsilon^(f).
581-
// We have either yi == zi - epsiloni or yi == (zi - epsiloni) - 1,
582-
// where yi == zi - epsiloni if and only if z^(f) >= epsilon^(f).
583-
// Since there are only 2 possibilities, we only need to care about the
584-
// parity. Also, zi and r should have the same parity since the divisor is
585-
// an even number.
586-
let (yi_parity, is_y_integer) = F::compute_mul_parity(two_fc, &pow5, beta);
587-
588-
if yi_parity != approx_y_parity {
589-
significand -= 1;
590-
} else if is_y_integer {
591-
// If z^(f) >= epsilon^(f), we might have a tie
592-
// when z^(f) == epsilon^(f), or equivalently, when y is an integer.
593-
// For tie-to-up case, we can just choose the upper one.
594-
significand = RoundMode::Round.break_rounding_tie(significand);
571+
if should_short_circuit {
572+
// Short-circuit case.
573+
let (mant, exp) = F::process_trailing_zeros(significand, minus_k + F::KAPPA as i32 + 1);
574+
extended_float(mant, exp)
575+
} else {
576+
// Step 3: Find the significand with the smaller divisor
577+
significand *= 10;
578+
579+
let dist = r - (deltai / 2) + (small_divisor / 2);
580+
let approx_y_parity = ((dist ^ (small_divisor / 2)) & 1) != 0;
581+
582+
// Is dist divisible by 10^kappa?
583+
let (dist, is_dist_div_by_kappa) = F::check_div_pow10(dist);
584+
585+
// Add dist / 10^kappa to the significand.
586+
significand += dist as u64;
587+
588+
if is_dist_div_by_kappa {
589+
// Check z^(f) >= epsilon^(f).
590+
// We have either yi == zi - epsiloni or yi == (zi - epsiloni) - 1,
591+
// where yi == zi - epsiloni if and only if z^(f) >= epsilon^(f).
592+
// Since there are only 2 possibilities, we only need to care about the
593+
// parity. Also, zi and r should have the same parity since the divisor is
594+
// an even number.
595+
let (yi_parity, is_y_integer) = F::compute_mul_parity(two_fc, &pow5, beta);
596+
let round_down = RoundMode::Round.prefer_round_down(significand);
597+
598+
if yi_parity != approx_y_parity || (is_y_integer && round_down) {
599+
// If z^(f) >= epsilon^(f), we might have a tie
600+
// when z^(f) == epsilon^(f), or equivalently, when y is an integer.
601+
// For tie-to-up case, we can just choose the upper one.
602+
//significand -= 1;
603+
significand -= 1;
604+
}
595605
}
596-
}
597606

598-
// Ensure we haven't re-assigned exponent or minus_k, since this
599-
// is a massive potential security vulnerability.
600-
debug_assert!(float.exponent() == exponent);
601-
debug_assert!(minus_k == floor_log10_pow2(exponent) - F::KAPPA as i32);
607+
// Ensure we haven't re-assigned exponent or minus_k, since this
608+
// is a massive potential security vulnerability.
609+
debug_assert!(float.exponent() == exponent);
610+
debug_assert!(minus_k == floor_log10_pow2(exponent) - F::KAPPA as i32);
602611

603-
extended_float(significand, minus_k + F::KAPPA as i32)
612+
extended_float(significand, minus_k + F::KAPPA as i32)
613+
}
604614
}
605615

606616
/// Compute the interval I = [w,w+).
@@ -628,10 +638,8 @@ pub fn compute_left_closed_directed<F: RawFloat>(float: F) -> ExtendedFloat80 {
628638
// and 29711844 * 2^-81
629639
// = 1.2288530660000000001731007559513386695471126586198806762695... * 10^-17
630640
// for binary32.
631-
if F::BITS == 32 {
632-
if exponent <= -80 {
633-
is_x_integer = false;
634-
}
641+
if F::BITS == 32 && exponent <= -80 {
642+
is_x_integer = false;
635643
}
636644

637645
if !is_x_integer {
@@ -653,15 +661,14 @@ pub fn compute_left_closed_directed<F: RawFloat>(float: F) -> ExtendedFloat80 {
653661
r = big_divisor - r;
654662
}
655663

656-
// Short-circuit case.
657-
let short_circuit = || {
658-
let (mant, exp) = F::process_trailing_zeros(significand, minus_k + F::KAPPA as i32 + 1);
659-
extended_float(mant, exp)
660-
};
661-
662664
// Check for short-circuit.
663-
if r < deltai {
664-
return short_circuit();
665+
// We use this, since the `goto` statements in dragonbox are unidiomatic
666+
// in Rust and lead to unmaintainable code. Using a simple closure is much
667+
// simpler, however, we do store a boolean in some cases to determine
668+
// if we need to short-circuit.
669+
let mut should_short_circuit = true;
670+
if r > deltai {
671+
should_short_circuit = false;
665672
} else if r == deltai {
666673
// Compare the fractional parts.
667674
// This branch is never taken for the exceptional cases
@@ -670,21 +677,26 @@ pub fn compute_left_closed_directed<F: RawFloat>(float: F) -> ExtendedFloat80 {
670677
// and 2f_c = 29711482, e = -80
671678
// (1.2288529832819387448703332688104694625508273020386695861816... * 10^-17).
672679
let (zi_parity, is_z_integer) = F::compute_mul_parity(two_fc + 2, &pow5, beta);
673-
if !(zi_parity && is_z_integer) {
674-
return short_circuit();
680+
if zi_parity || is_z_integer {
681+
should_short_circuit = false;
675682
}
676683
}
677684

678-
// Step 3: Find the significand with the smaller divisor
679-
significand *= 10;
680-
significand -= F::div_pow10(r) as u64;
685+
if should_short_circuit {
686+
let (mant, exp) = F::process_trailing_zeros(significand, minus_k + F::KAPPA as i32 + 1);
687+
extended_float(mant, exp)
688+
} else {
689+
// Step 3: Find the significand with the smaller divisor
690+
significand *= 10;
691+
significand -= F::div_pow10(r) as u64;
681692

682-
// Ensure we haven't re-assigned exponent or minus_k, since this
683-
// is a massive potential security vulnerability.
684-
debug_assert!(float.exponent() == exponent);
685-
debug_assert!(minus_k == floor_log10_pow2(exponent) - F::KAPPA as i32);
693+
// Ensure we haven't re-assigned exponent or minus_k, since this
694+
// is a massive potential security vulnerability.
695+
debug_assert!(float.exponent() == exponent);
696+
debug_assert!(minus_k == floor_log10_pow2(exponent) - F::KAPPA as i32);
686697

687-
extended_float(significand, minus_k + F::KAPPA as i32)
698+
extended_float(significand, minus_k + F::KAPPA as i32)
699+
}
688700
}
689701

690702
/// Compute the interval I = (w−,w]..
@@ -716,15 +728,14 @@ pub fn compute_right_closed_directed<F: RawFloat>(float: F, shorter: bool) -> Ex
716728
let mut significand = F::divide_by_pow10(zi, exp, n_max);
717729
let r = (zi - (big_divisor as u64).wrapping_mul(significand)) as u32;
718730

719-
// Short-circuit case.
720-
let short_circuit = || {
721-
let (mant, exp) = F::process_trailing_zeros(significand, minus_k + F::KAPPA as i32 + 1);
722-
extended_float(mant, exp)
723-
};
724-
725731
// Check for short-circuit.
726-
if r < deltai {
727-
return short_circuit();
732+
// We use this, since the `goto` statements in dragonbox are unidiomatic
733+
// in Rust and lead to unmaintainable code. Using a simple closure is much
734+
// simpler, however, we do store a boolean in some cases to determine
735+
// if we need to short-circuit.
736+
let mut should_short_circuit = true;
737+
if r > deltai {
738+
should_short_circuit = false;
728739
} else if r == deltai {
729740
// Compare the fractional parts.
730741
let two_f = two_fc
@@ -733,21 +744,26 @@ pub fn compute_right_closed_directed<F: RawFloat>(float: F, shorter: bool) -> Ex
733744
} else {
734745
2
735746
};
736-
if F::compute_mul_parity(two_f, &pow5, beta).0 {
737-
return short_circuit();
747+
if !F::compute_mul_parity(two_f, &pow5, beta).0 {
748+
should_short_circuit = false;
738749
}
739750
}
740751

741-
// Step 3: Find the significand with the smaller divisor
742-
significand *= 10;
743-
significand -= F::div_pow10(r) as u64;
752+
if should_short_circuit {
753+
let (mant, exp) = F::process_trailing_zeros(significand, minus_k + F::KAPPA as i32 + 1);
754+
extended_float(mant, exp)
755+
} else {
756+
// Step 3: Find the significand with the smaller divisor
757+
significand *= 10;
758+
significand -= F::div_pow10(r) as u64;
744759

745-
// Ensure we haven't re-assigned exponent or minus_k, since this
746-
// is a massive potential security vulnerability.
747-
debug_assert!(float.exponent() == exponent);
748-
debug_assert!(minus_k == floor_log10_pow2(exponent - shorter as i32) - F::KAPPA as i32);
760+
// Ensure we haven't re-assigned exponent or minus_k, since this
761+
// is a massive potential security vulnerability.
762+
debug_assert!(float.exponent() == exponent);
763+
debug_assert!(minus_k == floor_log10_pow2(exponent - shorter as i32) - F::KAPPA as i32);
749764

750-
extended_float(significand, minus_k + F::KAPPA as i32)
765+
extended_float(significand, minus_k + F::KAPPA as i32)
766+
}
751767
}
752768

753769
// DIGITS
@@ -991,15 +1007,12 @@ pub const fn divide_by_pow10_64(n: u64, exp: u32, n_max: u64) -> u64 {
9911007
// --------
9921008

9931009
impl RoundMode {
994-
/// Zero out the lowest bit.
995-
// NOTE: this is now `prefer_round_down` in dragonbox, but this
996-
// logic is simpler for Rust to handle.
1010+
/// Determine if we should round down.
9971011
#[inline(always)]
998-
pub const fn break_rounding_tie(&self, significand: u64) -> u64 {
1012+
pub const fn prefer_round_down(&self, significand: u64) -> bool {
9991013
match self {
1000-
// r.significand % 2 != 0 ? r.significand : r.significand - 1
1001-
RoundMode::Round => significand & !1u64,
1002-
RoundMode::Truncate => significand - 1u64,
1014+
RoundMode::Round => significand % 2 != 0,
1015+
RoundMode::Truncate => true,
10031016
}
10041017
}
10051018
}
@@ -1062,23 +1075,19 @@ impl IntervalType {
10621075
// ENDPOINTS
10631076
// ---------
10641077

1065-
/// Compute the left or right endpoint from a 64-bit power-of-5.
1066-
#[inline(always)]
1067-
pub fn compute_endpoint_u64<F: DragonboxFloat, const SHIFT: usize>(pow5: u64, beta: i32) -> u64 {
1068-
let zero_carry = pow5 >> (F::MANTISSA_SIZE as usize + SHIFT);
1069-
let mantissa_shift = 64 - F::MANTISSA_SIZE as usize - 1;
1070-
(pow5 - zero_carry) >> (mantissa_shift as i32 - beta)
1071-
}
1072-
10731078
/// Compute the left endpoint from a 64-bit power-of-5.
10741079
#[inline(always)]
10751080
pub fn compute_left_endpoint_u64<F: DragonboxFloat>(pow5: u64, beta: i32) -> u64 {
1076-
compute_endpoint_u64::<F, 2>(pow5, beta)
1081+
let zero_carry = pow5 >> (F::MANTISSA_SIZE as usize + 2);
1082+
let mantissa_shift = 64 - F::MANTISSA_SIZE as usize - 1;
1083+
(pow5 - zero_carry) >> (mantissa_shift as i32 - beta)
10771084
}
10781085

10791086
#[inline(always)]
10801087
pub fn compute_right_endpoint_u64<F: DragonboxFloat>(pow5: u64, beta: i32) -> u64 {
1081-
compute_endpoint_u64::<F, 1>(pow5, beta)
1088+
let zero_carry = pow5 >> (F::MANTISSA_SIZE as usize + 1);
1089+
let mantissa_shift = 64 - F::MANTISSA_SIZE as usize - 1;
1090+
(pow5 + zero_carry) >> (mantissa_shift as i32 - beta)
10821091
}
10831092

10841093
/// Determine if we should round up for the short interval case.
@@ -1140,7 +1149,7 @@ const F64_DIV10_INFO: Div10Info = Div10Info {
11401149
macro_rules! check_div_pow10 {
11411150
($n:ident, $exp:literal, $float:ident, $info:ident) => {{
11421151
// Make sure the computation for max_n does not overflow.
1143-
debug_assert!($exp + 1 <= floor_log10_pow2(31));
1152+
debug_assert!($exp + 2 < floor_log10_pow2(31));
11441153
debug_assert!($n as u64 <= pow64(10, $exp + 1));
11451154

11461155
let n = $n.wrapping_mul($info.magic_number);
@@ -1278,17 +1287,17 @@ impl DragonboxFloat for f32 {
12781287
#[inline(always)]
12791288
fn compute_mul(u: u64, pow5: &Self::Power) -> (u64, bool) {
12801289
let r = umul96_upper64(u, *pow5);
1281-
(r >> 32, r == 0)
1290+
(r >> 32, (r as u32) == 0)
12821291
}
12831292

12841293
#[inline(always)]
12851294
fn compute_mul_parity(two_f: u64, pow5: &Self::Power, beta: i32) -> (bool, bool) {
12861295
debug_assert!((1..64).contains(&beta));
12871296

12881297
let r = umul96_lower64(two_f, *pow5);
1289-
let parity = r >> ((64 - beta) & 1);
1298+
let parity = (r >> (64 - beta)) & 1;
12901299
let is_integer = r >> (32 - beta);
1291-
(parity != 0, is_integer != 0)
1300+
(parity != 0, is_integer == 0)
12921301
}
12931302

12941303
#[inline(always)]
@@ -1396,9 +1405,9 @@ impl DragonboxFloat for f64 {
13961405
debug_assert!((1..64).contains(&beta));
13971406

13981407
let (rhi, rlo) = umul192_lower128(two_f, high(pow5), low(pow5));
1399-
let parity = rhi >> ((64 - beta) & 1);
1408+
let parity = (rhi >> (64 - beta)) & 1;
14001409
let is_integer = (rhi << beta) | (rlo >> (64 - beta));
1401-
(parity != 0, is_integer != 0)
1410+
(parity != 0, is_integer == 0)
14021411
}
14031412

14041413
#[inline(always)]

0 commit comments

Comments
 (0)