@@ -3,18 +3,26 @@ use rand::distributions::Distribution as _;
3
3
use rustc_apfloat:: Float as _;
4
4
use rustc_apfloat:: ieee:: IeeeFloat ;
5
5
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.)
7
11
pub ( crate ) fn apply_random_float_error < F : rustc_apfloat:: Float > (
8
12
ecx : & mut crate :: MiriInterpCx < ' _ > ,
9
13
val : F ,
10
14
err_scale : i32 ,
11
15
) -> F {
12
16
let rng = ecx. machine . rng . get_mut ( ) ;
13
17
// 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.)
14
21
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 ( ) ) ) ;
18
26
// give it a random sign
19
27
let err = if rng. gen :: < bool > ( ) { -err } else { err } ;
20
28
// multiple the value with (1+err)
0 commit comments