@@ -77,19 +77,19 @@ pub struct HighPrecision<F> where F: HPFloatHelper {
7777 low_as_int : F :: SignedInt ,
7878 high_as_int : F :: SignedInt ,
7979 exponent : u16 ,
80- distribution : F :: SignedIntDistribution ,
80+ mantissa_distribution : F :: SignedIntDistribution ,
8181}
8282
8383impl < F : HPFloatHelper > HighPrecision < F > {
8484 /// Create a new HighPrecision distribution. Sampling from this
85- /// distribution will return values `>= low` and `< high`.
85+ /// distribution will return values `>= low` and `< high`.
8686 pub fn new ( low : F , high : F ) -> Self {
8787 let parsed = F :: parse_new ( low, high) ;
8888 HighPrecision {
8989 low_as_int : parsed. 0 ,
9090 high_as_int : parsed. 1 ,
9191 exponent : parsed. 2 ,
92- distribution : parsed. 3 ,
92+ mantissa_distribution : parsed. 3 ,
9393 }
9494 }
9595}
@@ -239,7 +239,7 @@ macro_rules! float_impls {
239239 if bits >= :: core:: mem:: size_of:: <$uty>( ) as $uty * 8 { ( -1 as $ity) as $uty } else { ( 1 as $uty << bits) - 1 }
240240 }
241241 loop {
242- let signed_mant = self . distribution . sample( rng) ;
242+ let signed_mant = self . mantissa_distribution . sample( rng) ;
243243 // Operate on the absolute value so that we can count bit-sizes
244244 // correctly
245245 let is_neg = signed_mant < 0 ;
@@ -302,6 +302,10 @@ macro_rules! float_impls {
302302 return <$ty>:: from_bits( res) ;
303303 }
304304
305+ // If not start over. We're avoiding reusing any of the previous
306+ // computation in order to avoid introducing bias, and to keep
307+ // things simple since this should be rare.
308+
305309 // Assert that we got here due to rounding
306310 #[ cfg( debug_assertions) ]
307311 {
@@ -318,15 +322,9 @@ macro_rules! float_impls {
318322 assert!( mant_high & bitmask( exp_low - exp_high) != 0 ) ;
319323 }
320324 }
321-
322- // If not start over. We're avoiding reusing any of the previous
323- // computation in order to avoid introducing bias, and to keep
324- // things simple since this should be rare.
325325 }
326326 }
327327 }
328-
329-
330328 }
331329}
332330float_impls ! { f32 , u32 , i32 , 23 , 8 , 127 }
@@ -399,115 +397,174 @@ mod tests {
399397 let mut r = :: test:: rng ( 601 ) ;
400398
401399 macro_rules! float_test {
402- ( $ty: ty, $uty: ty, $ity: ty, $test_vals: expr) => { {
403- let mut vals: Vec <$ty> =
404- $test_vals. iter( ) . cloned( )
405- . flat_map( |x| ( -2 as $ity..3 ) . map( move |y| x + y) )
406- . map( |x| <$ty>:: from_bits( x as $uty) )
407- . flat_map( |x| vec![ x, -x] . into_iter( ) )
408- . filter( |x| x. is_finite( ) )
409- . collect( ) ;
410- vals. sort_by( |a, b| a. partial_cmp( b) . unwrap( ) ) ;
411- vals. dedup( ) ;
412-
413- for a in vals. iter( ) . cloned( ) {
414- for b in vals. iter( ) . cloned( ) . filter( |& b| b > a) {
415- fn to_signed_bits( val: $ty) -> $ity {
416- if val >= 0.0 {
417- val. to_bits( ) as $ity
418- } else {
419- -( ( -val) . to_bits( ) as $ity)
400+ ( $ty: ty, $uty: ty, $ity: ty, $extra: expr, $test_vals: expr) => {
401+ // Create a closure to make loop labels local
402+ ( || {
403+ let mut vals: Vec <$ty> =
404+ $test_vals. iter( ) . cloned( )
405+ . flat_map( |x| $extra. iter( ) . map( move |y| x + y) )
406+ . map( |x| <$ty>:: from_bits( x as $uty) )
407+ . flat_map( |x| vec![ x, -x] . into_iter( ) )
408+ . filter( |x| x. is_finite( ) )
409+ . collect( ) ;
410+ vals. sort_by( |a, b| a. partial_cmp( b) . unwrap( ) ) ;
411+ vals. dedup( ) ;
412+
413+ for a in vals. iter( ) . cloned( ) {
414+ for b in vals. iter( ) . cloned( ) . filter( |& b| b > a) {
415+ fn to_signed_bits( val: $ty) -> $ity {
416+ if val >= 0.0 {
417+ val. to_bits( ) as $ity
418+ } else {
419+ -( ( -val) . to_bits( ) as $ity)
420+ }
420421 }
421- }
422- fn from_signed_bits ( val : $ity ) -> $ty {
423- if val >= 0 {
424- <$ty> :: from_bits ( val as $uty )
425- } else {
426- -<$ty> :: from_bits ( -val as $uty )
422+ fn from_signed_bits ( val : $ity ) -> $ty {
423+ if val >= 0 {
424+ <$ty> :: from_bits ( val as $uty )
425+ } else {
426+ -<$ty> :: from_bits ( -val as $uty )
427+ }
427428 }
428- }
429429
430- let hp = HighPrecision :: new( a, b) ;
431- let a_bits = to_signed_bits( a) ;
432- let b_bits = to_signed_bits( b) ;
433-
434- // If a and b are "close enough", we can verify the full distribution
435- if ( b_bits. wrapping_sub( a_bits) as $uty) < 100 {
436- let mut counts = Vec :: <i32 >:: with_capacity( ( b_bits - a_bits) as usize ) ;
437- counts. resize( ( b_bits - a_bits) as usize , 0 ) ;
438- for _ in 0 ..1000 {
439- let res = hp. sample( & mut r) ;
440- counts[ ( to_signed_bits( res) - a_bits) as usize ] += 1 ;
441- }
442- for ( count, i) in counts. iter( ) . zip( 0 as $ity..) {
443- let expected = 1000.0 as $ty *
444- ( from_signed_bits( a_bits + i + 1 ) -
445- from_signed_bits( a_bits + i) ) / ( b - a) ;
446- let err = ( * count as $ty - expected) / expected;
447- assert!( err. abs( ) <= 0.2 ) ;
448- }
449- } else {
450- // Rough estimate that the distribution is correct
451- let step = if ( b - a) . is_finite( ) {
452- ( b - a) / 10.0
430+ let hp = HighPrecision :: new( a, b) ;
431+ let a_bits = to_signed_bits( a) ;
432+ let b_bits = to_signed_bits( b) ;
433+
434+ const N_RUNS : usize = 10 ;
435+ const N_REPS_PER_RUN : usize = 1000 ;
436+
437+ if ( b_bits. wrapping_sub( a_bits) as $uty) < 100 {
438+ // If a and b are "close enough", we can verify the full distribution
439+ let mut counts = Vec :: <i32 >:: with_capacity( ( b_bits - a_bits) as usize ) ;
440+ counts. resize( ( b_bits - a_bits) as usize , 0 ) ;
441+ ' test_loop_exact: for test_run in 1 ..( N_RUNS +1 ) {
442+ for _ in 0 ..N_REPS_PER_RUN {
443+ let res = hp. sample( & mut r) ;
444+ counts[ ( to_signed_bits( res) - a_bits) as usize ] += 1 ;
445+ }
446+ for ( count, i) in counts. iter( ) . zip( 0 as $ity..) {
447+ let expected = ( test_run * N_REPS_PER_RUN ) as $ty *
448+ ( ( from_signed_bits( a_bits + i + 1 ) -
449+ from_signed_bits( a_bits + i) ) / ( b - a) ) ;
450+ let err = ( * count as $ty - expected) / expected;
451+ if err. abs( ) > 0.2 {
452+ if test_run < N_RUNS {
453+ continue ' test_loop_exact;
454+ }
455+ panic!( format!( "Failed {}-bit exact test: a: 0x{:x}, b: 0x{:x}, err: {:.2}" ,
456+ :: core:: mem:: size_of:: <$ty>( ) * 8 ,
457+ a. to_bits( ) ,
458+ b. to_bits( ) ,
459+ err. abs( ) ) ) ;
460+ }
461+ }
462+ }
453463 } else {
454- b / 10.0 - a / 10.0
455- } ;
456- assert!( step. is_finite( ) ) ;
457- let mut counts = Vec :: <i32 >:: with_capacity( 10 ) ;
458- counts. resize( 10 , 0 ) ;
459- for _ in 0 ..3000 {
460- let res = hp. sample( & mut r) ;
461- assert!( a <= res && res < b) ;
462- let index = if ( res - a) . is_finite( ) {
463- ( res - a) / step
464+ // Otherwise divide range into 10 sections
465+ let step = if ( b - a) . is_finite( ) {
466+ ( b - a) / 10.0
464467 } else {
465- res / step - a / step
466- } as usize ;
467- counts[ :: core:: cmp:: min( index, 9 ) ] += 1 ;
468- }
469- for count in & counts {
470- let expected = 3000.0 as $ty / 10.0 ;
471- let err = ( * count as $ty - expected) / expected;
472- assert!( err. abs( ) <= 0.25 ) ;
468+ b / 10.0 - a / 10.0
469+ } ;
470+ assert!( step. is_finite( ) ) ;
471+ let mut counts = Vec :: <i32 >:: with_capacity( 10 ) ;
472+ counts. resize( 10 , 0 ) ;
473+
474+ ' test_loop_rough: for test_run in 1 ..( N_RUNS +1 ) {
475+ for _ in 0 ..N_REPS_PER_RUN {
476+ let res = hp. sample( & mut r) ;
477+ assert!( a <= res && res < b) ;
478+ let index = ( res / step - a / step) as usize ;
479+ counts[ :: core:: cmp:: min( index, 9 ) ] += 1 ;
480+ }
481+ for count in & counts {
482+ let expected = ( test_run * N_REPS_PER_RUN ) as $ty / 10.0 ;
483+ let err = ( * count as $ty - expected) / expected;
484+ if err. abs( ) > 0.2 {
485+ if test_run < N_RUNS {
486+ continue ' test_loop_rough;
487+ }
488+ panic!( format!( "Failed {}-bit rough test: a: 0x{:x}, b: 0x{:x}, err: {:.2}" ,
489+ :: core:: mem:: size_of:: <$ty>( ) * 8 ,
490+ a. to_bits( ) ,
491+ b. to_bits( ) ,
492+ err. abs( ) ) ) ;
493+ }
494+ }
495+ }
473496 }
474497 }
475498 }
476- }
477- } }
499+ } ) ( )
500+ }
478501 }
479502
480- float_test ! ( f64 , u64 , i64 ,
481- [ 0i64 ,
482- 0x0000_0f00_0000_0000 ,
483- 0x0001_0000_0000_0000 ,
484- 0x0004_0000_0000_0000 ,
485- 0x0008_0000_0000_0000 ,
486- 0x0010_0000_0000_0000 ,
487- 0x0020_0000_0000_0000 ,
488- 0x0040_0000_0000_0000 ,
489- 0x0100_0000_0000_0000 ,
490- 0x00cd_ef12_3456_789a ,
491- 0x0100_ffff_ffff_ffff ,
492- 0x010f_ffff_ffff_ffff ,
493- 0x0400_1234_5678_abcd ,
494- 0x7fef_ffff_ffff_ffff ,
495- ] ) ;
496- float_test ! ( f32 , u32 , i32 ,
497- [ 0i32 ,
498- 0x000f_0000 ,
499- 0x0008_0000 ,
500- 0x0020_0000 ,
501- 0x0040_0000 ,
502- 0x0080_0000 ,
503- 0x0100_0000 ,
504- 0x0200_0000 ,
505- 0x0800_0000 ,
506- 0x5678_abcd ,
507- 0x0807_ffff ,
508- 0x087f_ffff ,
509- 0x4012_3456 ,
510- 0x7f7f_ffff ,
511- ] ) ;
503+ const SLOW_TESTS : bool = false ;
504+ if SLOW_TESTS {
505+ // These test cases are commented out since they
506+ // take too long to run.
507+ float_test ! ( f64 , u64 , i64 ,
508+ [ -5 , -1 , 0 , 1 , 7 ] ,
509+ [ 0i64 ,
510+ 0x0000_0f00_0000_0000 ,
511+ 0x0001_0000_0000_0000 ,
512+ 0x0004_0000_0000_0000 ,
513+ 0x0008_0000_0000_0000 ,
514+ 0x0010_0000_0000_0000 ,
515+ 0x0020_0000_0000_0000 ,
516+ 0x0040_0000_0000_0000 ,
517+ 0x0100_0000_0000_0000 ,
518+ 0x00cd_ef12_3456_789a ,
519+ 0x0100_ffff_ffff_ffff ,
520+ 0x010f_ffff_ffff_ffff ,
521+ 0x0400_1234_5678_abcd ,
522+ 0x7fef_ffff_ffff_ffff ,
523+ ] ) ;
524+ float_test ! ( f32 , u32 , i32 ,
525+ [ -5 , -1 , 0 , 1 , 7 ] ,
526+ [ 0i32 ,
527+ 0x000f_0000 ,
528+ 0x0008_0000 ,
529+ 0x0020_0000 ,
530+ 0x0040_0000 ,
531+ 0x0080_0000 ,
532+ 0x0100_0000 ,
533+ 0x0200_0000 ,
534+ 0x0800_0000 ,
535+ 0x5678_abcd ,
536+ 0x0807_ffff ,
537+ 0x087f_ffff ,
538+ 0x4012_3456 ,
539+ 0x7f7f_ffff ,
540+ ] ) ;
541+ } else {
542+ float_test ! ( f64 , u64 , i64 ,
543+ [ 0 ] ,
544+ [ 0i64 ,
545+ 1 ,
546+ 0x0000_0f00_0000_0000 ,
547+ 0x0000_0f00_0000_0005 ,
548+ 0x000f_ffff_ffff_fffd ,
549+ 0x0010_0000_0000_0000 ,
550+ 0x0040_0000_0000_0000 ,
551+ 0x0100_ffff_ffff_ffff ,
552+ 0x0101_0000_0000_0004 ,
553+ 0x7fef_ffff_ffff_ffff ,
554+ ] ) ;
555+ float_test ! ( f32 , u32 , i32 ,
556+ [ 0 ] ,
557+ [ 0i32 ,
558+ 1 ,
559+ 0x000f_0000 ,
560+ 0x000f_0005 ,
561+ 0x007f_fffd ,
562+ 0x0080_0000 ,
563+ 0x0200_0000 ,
564+ 0x0807_ffff ,
565+ 0x0808_0004 ,
566+ 0x7f7f_ffff ,
567+ ] ) ;
568+ }
512569 }
513570}
0 commit comments