@@ -460,18 +460,15 @@ impl<S: Semantics> fmt::Display for IeeeFloat<S> {
460
460
// rem <- sig % 10
461
461
// sig <- sig / 10
462
462
let mut rem = 0 ;
463
- for limb in sig. iter_mut ( ) . rev ( ) {
464
- // We don't have an integer doubly wide than Limb,
465
- // so we have to split the divrem on two halves.
466
- const HALF_BITS : usize = LIMB_BITS / 2 ;
467
- let mut halves = [ * limb & ( ( 1 << HALF_BITS ) - 1 ) , * limb >> HALF_BITS ] ;
468
- for half in halves. iter_mut ( ) . rev ( ) {
469
- * half |= rem << HALF_BITS ;
470
- rem = * half % 10 ;
471
- * half /= 10 ;
472
- }
473
- * limb = halves[ 0 ] | ( halves[ 1 ] << HALF_BITS ) ;
474
- }
463
+
464
+ // Use 64-bit division and remainder, with 32-bit chunks from sig.
465
+ sig:: each_chunk ( & mut sig, 32 , |chunk| {
466
+ let chunk = chunk as u32 ;
467
+ let combined = ( ( rem as u64 ) << 32 ) | ( chunk as u64 ) ;
468
+ rem = ( combined % 10 ) as u8 ;
469
+ ( combined / 10 ) as u32 as Limb
470
+ } ) ;
471
+
475
472
// Reduce the sigificand to avoid wasting time dividing 0's.
476
473
while sig. last ( ) == Some ( & 0 ) {
477
474
sig. pop ( ) ;
@@ -491,7 +488,7 @@ impl<S: Semantics> fmt::Display for IeeeFloat<S> {
491
488
exp += 1 ;
492
489
} else {
493
490
in_trail = false ;
494
- buffer. push ( b'0' + digit as u8 ) ;
491
+ buffer. push ( b'0' + digit) ;
495
492
}
496
493
}
497
494
@@ -2065,7 +2062,7 @@ impl<S: Semantics> IeeeFloat<S> {
2065
2062
} ;
2066
2063
2067
2064
// Attempt dec_sig * 10^dec_exp with increasing precision.
2068
- let mut attempt = 1 ;
2065
+ let mut attempt = 0 ;
2069
2066
loop {
2070
2067
let calc_precision = ( LIMB_BITS << attempt) - 1 ;
2071
2068
attempt += 1 ;
@@ -2310,6 +2307,17 @@ mod sig {
2310
2307
limbs. iter ( ) . all ( |& l| l == 0 )
2311
2308
}
2312
2309
2310
+ /// One, not zero, based LSB. That is, returns 0 for a zeroed significand.
2311
+ pub ( super ) fn olsb ( limbs : & [ Limb ] ) -> usize {
2312
+ for i in 0 ..limbs. len ( ) {
2313
+ if limbs[ i] != 0 {
2314
+ return i * LIMB_BITS + limbs[ i] . trailing_zeros ( ) as usize + 1 ;
2315
+ }
2316
+ }
2317
+
2318
+ 0
2319
+ }
2320
+
2313
2321
/// One, not zero, based MSB. That is, returns 0 for a zeroed significand.
2314
2322
pub ( super ) fn omsb ( limbs : & [ Limb ] ) -> usize {
2315
2323
for i in ( 0 ..limbs. len ( ) ) . rev ( ) {
@@ -2468,6 +2476,20 @@ mod sig {
2468
2476
}
2469
2477
}
2470
2478
2479
+ /// For every consecutive chunk of `bits` bits from `limbs`,
2480
+ /// going from most significant to the least significant bits,
2481
+ /// call `f` to transform those bits and store the result back.
2482
+ pub ( super ) fn each_chunk < F : FnMut ( Limb ) -> Limb > ( limbs : & mut [ Limb ] , bits : usize , mut f : F ) {
2483
+ assert_eq ! ( LIMB_BITS % bits, 0 ) ;
2484
+ for limb in limbs. iter_mut ( ) . rev ( ) {
2485
+ let mut r = 0 ;
2486
+ for i in ( 0 ..LIMB_BITS / bits) . rev ( ) {
2487
+ r |= f ( ( * limb >> ( i * bits) ) & ( ( 1 << bits) - 1 ) ) << ( i * bits) ;
2488
+ }
2489
+ * limb = r;
2490
+ }
2491
+ }
2492
+
2471
2493
/// Increment in-place, return the carry flag.
2472
2494
pub ( super ) fn increment ( dst : & mut [ Limb ] ) -> Limb {
2473
2495
for x in dst {
@@ -2686,10 +2708,6 @@ mod sig {
2686
2708
divisor : & mut [ Limb ] ,
2687
2709
precision : usize ,
2688
2710
) -> Loss {
2689
- // Zero the quotient before setting bits in it.
2690
- for x in & mut quotient[ ..limbs_for_bits ( precision) ] {
2691
- * x = 0 ;
2692
- }
2693
2711
2694
2712
// Normalize the divisor.
2695
2713
let bits = precision - omsb ( divisor) ;
@@ -2700,6 +2718,13 @@ mod sig {
2700
2718
let bits = precision - omsb ( dividend) ;
2701
2719
shift_left ( dividend, exp, bits) ;
2702
2720
2721
+ // Division by 1.
2722
+ let olsb_divisor = olsb ( divisor) ;
2723
+ if olsb_divisor == precision {
2724
+ quotient. copy_from_slice ( dividend) ;
2725
+ return Loss :: ExactlyZero ;
2726
+ }
2727
+
2703
2728
// Ensure the dividend >= divisor initially for the loop below.
2704
2729
// Incidentally, this means that the division loop below is
2705
2730
// guaranteed to set the integer bit to one.
@@ -2708,6 +2733,58 @@ mod sig {
2708
2733
assert_ne ! ( cmp( dividend, divisor) , Ordering :: Less )
2709
2734
}
2710
2735
2736
+ // Helper for figuring out the lost fraction.
2737
+ let lost_fraction = |dividend : & [ Limb ] , divisor : & [ Limb ] | {
2738
+ match cmp ( dividend, divisor) {
2739
+ Ordering :: Greater => Loss :: MoreThanHalf ,
2740
+ Ordering :: Equal => Loss :: ExactlyHalf ,
2741
+ Ordering :: Less => {
2742
+ if is_all_zeros ( dividend) {
2743
+ Loss :: ExactlyZero
2744
+ } else {
2745
+ Loss :: LessThanHalf
2746
+ }
2747
+ }
2748
+ }
2749
+ } ;
2750
+
2751
+ // Try to perform a (much faster) short division for small divisors.
2752
+ let divisor_bits = precision - ( olsb_divisor - 1 ) ;
2753
+ macro_rules! try_short_div {
2754
+ ( $W: ty, $H: ty, $half: expr) => {
2755
+ if divisor_bits * 2 <= $half {
2756
+ // Extract the small divisor.
2757
+ let _: Loss = shift_right( divisor, & mut 0 , olsb_divisor - 1 ) ;
2758
+ let divisor = divisor[ 0 ] as $H as $W;
2759
+
2760
+ // Shift the dividend to produce a quotient with the unit bit set.
2761
+ let top_limb = * dividend. last( ) . unwrap( ) ;
2762
+ let mut rem = ( top_limb >> ( LIMB_BITS - ( divisor_bits - 1 ) ) ) as $H;
2763
+ shift_left( dividend, & mut 0 , divisor_bits - 1 ) ;
2764
+
2765
+ // Apply short division in place on $H (of $half bits) chunks.
2766
+ each_chunk( dividend, $half, |chunk| {
2767
+ let chunk = chunk as $H;
2768
+ let combined = ( ( rem as $W) << $half) | ( chunk as $W) ;
2769
+ rem = ( combined % divisor) as $H;
2770
+ ( combined / divisor) as $H as Limb
2771
+ } ) ;
2772
+ quotient. copy_from_slice( dividend) ;
2773
+
2774
+ return lost_fraction( & [ ( rem as Limb ) << 1 ] , & [ divisor as Limb ] ) ;
2775
+ }
2776
+ }
2777
+ }
2778
+
2779
+ try_short_div ! ( u32 , u16 , 16 ) ;
2780
+ try_short_div ! ( u64 , u32 , 32 ) ;
2781
+ try_short_div ! ( u128 , u64 , 64 ) ;
2782
+
2783
+ // Zero the quotient before setting bits in it.
2784
+ for x in & mut quotient[ ..limbs_for_bits ( precision) ] {
2785
+ * x = 0 ;
2786
+ }
2787
+
2711
2788
// Long division.
2712
2789
for bit in ( 0 ..precision) . rev ( ) {
2713
2790
if cmp ( dividend, divisor) != Ordering :: Less {
@@ -2717,17 +2794,6 @@ mod sig {
2717
2794
shift_left ( dividend, & mut 0 , 1 ) ;
2718
2795
}
2719
2796
2720
- // Figure out the lost fraction.
2721
- match cmp ( dividend, divisor) {
2722
- Ordering :: Greater => Loss :: MoreThanHalf ,
2723
- Ordering :: Equal => Loss :: ExactlyHalf ,
2724
- Ordering :: Less => {
2725
- if is_all_zeros ( dividend) {
2726
- Loss :: ExactlyZero
2727
- } else {
2728
- Loss :: LessThanHalf
2729
- }
2730
- }
2731
- }
2797
+ lost_fraction ( dividend, divisor)
2732
2798
}
2733
2799
}
0 commit comments