@@ -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 }
@@ -392,122 +390,180 @@ mod tests {
392390 assert_eq ! ( max. sample:: <f64 , _>( Open01 ) , 1.0 - EPSILON64 / 2.0 ) ;
393391 }
394392
395- #[ cfg( feature = "alloc " ) ]
393+ #[ cfg( feature = "std " ) ]
396394 #[ test]
397395 fn test_highprecision ( ) {
398-
399396 let mut r = :: test:: rng ( 601 ) ;
400397
401398 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)
399+ ( $ty: ty, $uty: ty, $ity: ty, $extra: expr, $test_vals: expr) => {
400+ // Create a closure to make loop labels local
401+ ( || {
402+ let mut vals: Vec <$ty> =
403+ $test_vals. iter( ) . cloned( )
404+ . flat_map( |x| $extra. iter( ) . map( move |y| x + y) )
405+ . map( |x| <$ty>:: from_bits( x as $uty) )
406+ . flat_map( |x| vec![ x, -x] . into_iter( ) )
407+ . filter( |x| x. is_finite( ) )
408+ . collect( ) ;
409+ vals. sort_by( |a, b| a. partial_cmp( b) . unwrap( ) ) ;
410+ vals. dedup( ) ;
411+
412+ for a in vals. iter( ) . cloned( ) {
413+ for b in vals. iter( ) . cloned( ) . filter( |& b| b > a) {
414+ fn to_signed_bits( val: $ty) -> $ity {
415+ if val >= 0.0 {
416+ val. to_bits( ) as $ity
417+ } else {
418+ -( ( -val) . to_bits( ) as $ity)
419+ }
420420 }
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 )
421+ fn from_signed_bits ( val : $ity ) -> $ty {
422+ if val >= 0 {
423+ <$ty> :: from_bits ( val as $uty )
424+ } else {
425+ -<$ty> :: from_bits ( -val as $uty )
426+ }
427427 }
428- }
429428
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
429+ let hp = HighPrecision :: new( a, b) ;
430+ let a_bits = to_signed_bits( a) ;
431+ let b_bits = to_signed_bits( b) ;
432+
433+ const N_RUNS : usize = 10 ;
434+ const N_REPS_PER_RUN : usize = 1000 ;
435+
436+ if ( b_bits. wrapping_sub( a_bits) as $uty) < 100 {
437+ // If a and b are "close enough", we can verify the full distribution
438+ let mut counts = Vec :: <i32 >:: with_capacity( ( b_bits - a_bits) as usize ) ;
439+ counts. resize( ( b_bits - a_bits) as usize , 0 ) ;
440+ ' test_loop_exact: for test_run in 1 ..( N_RUNS +1 ) {
441+ for _ in 0 ..N_REPS_PER_RUN {
442+ let res = hp. sample( & mut r) ;
443+ counts[ ( to_signed_bits( res) - a_bits) as usize ] += 1 ;
444+ }
445+ for ( count, i) in counts. iter( ) . zip( 0 as $ity..) {
446+ let expected = ( test_run * N_REPS_PER_RUN ) as $ty *
447+ ( ( from_signed_bits( a_bits + i + 1 ) -
448+ from_signed_bits( a_bits + i) ) / ( b - a) ) ;
449+ let err = ( * count as $ty - expected) / expected;
450+ if err. abs( ) > 0.2 {
451+ if test_run < N_RUNS {
452+ continue ' test_loop_exact;
453+ }
454+ panic!( format!( "Failed {}-bit exact test: a: 0x{:x}, b: 0x{:x}, err: {:.2}" ,
455+ :: core:: mem:: size_of:: <$ty>( ) * 8 ,
456+ a. to_bits( ) ,
457+ b. to_bits( ) ,
458+ err. abs( ) ) ) ;
459+ }
460+ }
461+ }
453462 } 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
463+ // Otherwise divide range into 10 sections
464+ let step = if ( b - a) . is_finite( ) {
465+ ( b - a) / 10.0
464466 } 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 ) ;
467+ b / 10.0 - a / 10.0
468+ } ;
469+ assert!( step. is_finite( ) ) ;
470+ let mut counts = Vec :: <i32 >:: with_capacity( 10 ) ;
471+ counts. resize( 10 , 0 ) ;
472+
473+ ' test_loop_rough: for test_run in 1 ..( N_RUNS +1 ) {
474+ for _ in 0 ..N_REPS_PER_RUN {
475+ let res = hp. sample( & mut r) ;
476+ assert!( a <= res && res < b) ;
477+ let index = ( res / step - a / step) as usize ;
478+ counts[ :: core:: cmp:: min( index, 9 ) ] += 1 ;
479+ }
480+ for count in & counts {
481+ let expected = ( test_run * N_REPS_PER_RUN ) as $ty / 10.0 ;
482+ let err = ( * count as $ty - expected) / expected;
483+ if err. abs( ) > 0.2 {
484+ if test_run < N_RUNS {
485+ continue ' test_loop_rough;
486+ }
487+ panic!( format!( "Failed {}-bit rough test: a: 0x{:x}, b: 0x{:x}, err: {:.2}" ,
488+ :: core:: mem:: size_of:: <$ty>( ) * 8 ,
489+ a. to_bits( ) ,
490+ b. to_bits( ) ,
491+ err. abs( ) ) ) ;
492+ }
493+ }
494+ }
473495 }
474496 }
475497 }
476- }
477- } }
498+ } ) ( )
499+ }
478500 }
479501
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- ] ) ;
502+ const SLOW_TESTS : bool = false ;
503+ if SLOW_TESTS {
504+ // These test cases are commented out since they
505+ // take too long to run.
506+ float_test ! ( f64 , u64 , i64 ,
507+ [ -5 , -1 , 0 , 1 , 7 ] ,
508+ [ 0i64 ,
509+ 0x0000_0f00_0000_0000 ,
510+ 0x0001_0000_0000_0000 ,
511+ 0x0004_0000_0000_0000 ,
512+ 0x0008_0000_0000_0000 ,
513+ 0x0010_0000_0000_0000 ,
514+ 0x0020_0000_0000_0000 ,
515+ 0x0040_0000_0000_0000 ,
516+ 0x0100_0000_0000_0000 ,
517+ 0x00cd_ef12_3456_789a ,
518+ 0x0100_ffff_ffff_ffff ,
519+ 0x010f_ffff_ffff_ffff ,
520+ 0x0400_1234_5678_abcd ,
521+ 0x7fef_ffff_ffff_ffff ,
522+ ] ) ;
523+ float_test ! ( f32 , u32 , i32 ,
524+ [ -5 , -1 , 0 , 1 , 7 ] ,
525+ [ 0i32 ,
526+ 0x000f_0000 ,
527+ 0x0008_0000 ,
528+ 0x0020_0000 ,
529+ 0x0040_0000 ,
530+ 0x0080_0000 ,
531+ 0x0100_0000 ,
532+ 0x0200_0000 ,
533+ 0x0800_0000 ,
534+ 0x5678_abcd ,
535+ 0x0807_ffff ,
536+ 0x087f_ffff ,
537+ 0x4012_3456 ,
538+ 0x7f7f_ffff ,
539+ ] ) ;
540+ } else {
541+ float_test ! ( f64 , u64 , i64 ,
542+ [ 0 ] ,
543+ [ 0i64 ,
544+ 1 ,
545+ 0x0000_0f00_0000_0000 ,
546+ 0x0000_0f00_0000_0005 ,
547+ 0x000f_ffff_ffff_fffd ,
548+ 0x0010_0000_0000_0000 ,
549+ 0x0040_0000_0000_0000 ,
550+ 0x0100_ffff_ffff_ffff ,
551+ 0x0101_0000_0000_0004 ,
552+ 0x7fef_ffff_ffff_ffff ,
553+ ] ) ;
554+ float_test ! ( f32 , u32 , i32 ,
555+ [ 0 ] ,
556+ [ 0i32 ,
557+ 1 ,
558+ 0x000f_0000 ,
559+ 0x000f_0005 ,
560+ 0x007f_fffd ,
561+ 0x0080_0000 ,
562+ 0x0200_0000 ,
563+ 0x0807_ffff ,
564+ 0x0808_0004 ,
565+ 0x7f7f_ffff ,
566+ ] ) ;
567+ }
512568 }
513569}
0 commit comments