@@ -3,18 +3,26 @@ use rand::distributions::Distribution as _;
33use rustc_apfloat:: Float as _;
44use rustc_apfloat:: ieee:: IeeeFloat ;
55
6- /// Disturbes a floating-point result by a relative error on the order of (-2^scale, 2^scale).
6+ /// Disturbes a floating-point result by a relative error in the range (-2^scale, 2^scale).
7+ ///
8+ /// For a 2^N ULP error, you can use an `err_scale` of `-(F::PRECISION - 1 - N)`.
9+ /// In other words, a 1 ULP (absolute) error is the same as a `2^-(F::PRECISION-1)` relative error.
10+ /// (Subtracting 1 compensates for the integer bit.)
711pub ( crate ) fn apply_random_float_error < F : rustc_apfloat:: Float > (
812 ecx : & mut crate :: MiriInterpCx < ' _ > ,
913 val : F ,
1014 err_scale : i32 ,
1115) -> F {
1216 let rng = ecx. machine . rng . get_mut ( ) ;
1317 // Generate a random integer in the range [0, 2^PREC).
18+ // (When read as binary, the position of the first `1` determines the exponent,
19+ // and the remaining bits fill the mantissa. `PREC` is one plus the size of the mantissa,
20+ // so this all works out.)
1421 let dist = rand:: distributions:: Uniform :: new ( 0 , 1 << F :: PRECISION ) ;
15- let err = F :: from_u128 ( dist. sample ( rng) )
16- . value
17- . scalbn ( err_scale. strict_sub ( F :: PRECISION . try_into ( ) . unwrap ( ) ) ) ;
22+ let r = F :: from_u128 ( dist. sample ( rng) ) . value ;
23+ // Multiply this with 2^(scale - PREC). The result is between 0 and
24+ // 2^PREC * 2^(scale - PREC) = 2^scale.
25+ let err = r. scalbn ( err_scale. strict_sub ( F :: PRECISION . try_into ( ) . unwrap ( ) ) ) ;
1826 // give it a random sign
1927 let err = if rng. gen :: < bool > ( ) { -err } else { err } ;
2028 // multiple the value with (1+err)
0 commit comments