@@ -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 }
@@ -399,115 +397,174 @@ mod tests {
399
397
let mut r = :: test:: rng ( 601 ) ;
400
398
401
399
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
+ }
420
421
}
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
+ }
427
428
}
428
- }
429
429
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
+ }
453
463
} 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
464
467
} 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
+ }
473
496
}
474
497
}
475
498
}
476
- }
477
- } }
499
+ } ) ( )
500
+ }
478
501
}
479
502
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
+ }
512
569
}
513
570
}
0 commit comments