@@ -77,19 +77,19 @@ pub struct HighPrecision<F> where F: HPFloatHelper {
77
77
low_as_int : F :: SignedInt ,
78
78
high_as_int : F :: SignedInt ,
79
79
exponent : u16 ,
80
- distribution : F :: SignedIntDistribution ,
80
+ mantissa_distribution : F :: SignedIntDistribution ,
81
81
}
82
82
83
83
impl < F : HPFloatHelper > HighPrecision < F > {
84
84
/// Create a new HighPrecision distribution. Sampling from this
85
- /// distribution will return values `>= low` and `< high`.
85
+ /// distribution will return values `>= low` and `< high`.
86
86
pub fn new ( low : F , high : F ) -> Self {
87
87
let parsed = F :: parse_new ( low, high) ;
88
88
HighPrecision {
89
89
low_as_int : parsed. 0 ,
90
90
high_as_int : parsed. 1 ,
91
91
exponent : parsed. 2 ,
92
- distribution : parsed. 3 ,
92
+ mantissa_distribution : parsed. 3 ,
93
93
}
94
94
}
95
95
}
@@ -239,7 +239,7 @@ macro_rules! float_impls {
239
239
if bits >= :: core:: mem:: size_of:: <$uty>( ) as $uty * 8 { ( -1 as $ity) as $uty } else { ( 1 as $uty << bits) - 1 }
240
240
}
241
241
loop {
242
- let signed_mant = self . distribution . sample( rng) ;
242
+ let signed_mant = self . mantissa_distribution . sample( rng) ;
243
243
// Operate on the absolute value so that we can count bit-sizes
244
244
// correctly
245
245
let is_neg = signed_mant < 0 ;
@@ -302,6 +302,10 @@ macro_rules! float_impls {
302
302
return <$ty>:: from_bits( res) ;
303
303
}
304
304
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
+
305
309
// Assert that we got here due to rounding
306
310
#[ cfg( debug_assertions) ]
307
311
{
@@ -318,15 +322,9 @@ macro_rules! float_impls {
318
322
assert!( mant_high & bitmask( exp_low - exp_high) != 0 ) ;
319
323
}
320
324
}
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.
325
325
}
326
326
}
327
327
}
328
-
329
-
330
328
}
331
329
}
332
330
float_impls ! { f32 , u32 , i32 , 23 , 8 , 127 }
@@ -392,122 +390,180 @@ mod tests {
392
390
assert_eq ! ( max. sample:: <f64 , _>( Open01 ) , 1.0 - EPSILON64 / 2.0 ) ;
393
391
}
394
392
395
- #[ cfg( feature = "alloc " ) ]
393
+ #[ cfg( feature = "std " ) ]
396
394
#[ test]
397
395
fn test_highprecision ( ) {
398
-
399
396
let mut r = :: test:: rng ( 601 ) ;
400
397
401
398
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
+ }
420
420
}
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
+ }
427
427
}
428
- }
429
428
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
+ }
453
462
} 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
464
466
} 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
+ }
473
495
}
474
496
}
475
497
}
476
- }
477
- } }
498
+ } ) ( )
499
+ }
478
500
}
479
501
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
+ }
512
568
}
513
569
}
0 commit comments