about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/tools/miri/src/math.rs16
1 files changed, 12 insertions, 4 deletions
diff --git a/src/tools/miri/src/math.rs b/src/tools/miri/src/math.rs
index ed3d2d55678..8422e7ccb64 100644
--- a/src/tools/miri/src/math.rs
+++ b/src/tools/miri/src/math.rs
@@ -3,7 +3,11 @@ use rand::distributions::Distribution as _;
 use rustc_apfloat::Float as _;
 use rustc_apfloat::ieee::IeeeFloat;
 
-/// Disturbes a floating-point result by a relative error on the order of (-2^scale, 2^scale).
+/// Disturbes a floating-point result by a relative error in the range (-2^scale, 2^scale).
+///
+/// For a 2^N ULP error, you can use an `err_scale` of `-(F::PRECISION - 1 - N)`.
+/// In other words, a 1 ULP (absolute) error is the same as a `2^-(F::PRECISION-1)` relative error.
+/// (Subtracting 1 compensates for the integer bit.)
 pub(crate) fn apply_random_float_error<F: rustc_apfloat::Float>(
     ecx: &mut crate::MiriInterpCx<'_>,
     val: F,
@@ -11,10 +15,14 @@ pub(crate) fn apply_random_float_error<F: rustc_apfloat::Float>(
 ) -> F {
     let rng = ecx.machine.rng.get_mut();
     // Generate a random integer in the range [0, 2^PREC).
+    // (When read as binary, the position of the first `1` determines the exponent,
+    // and the remaining bits fill the mantissa. `PREC` is one plus the size of the mantissa,
+    // so this all works out.)
     let dist = rand::distributions::Uniform::new(0, 1 << F::PRECISION);
-    let err = F::from_u128(dist.sample(rng))
-        .value
-        .scalbn(err_scale.strict_sub(F::PRECISION.try_into().unwrap()));
+    let r = F::from_u128(dist.sample(rng)).value;
+    // Multiply this with 2^(scale - PREC). The result is between 0 and
+    // 2^PREC * 2^(scale - PREC) = 2^scale.
+    let err = r.scalbn(err_scale.strict_sub(F::PRECISION.try_into().unwrap()));
     // give it a random sign
     let err = if rng.gen::<bool>() { -err } else { err };
     // multiple the value with (1+err)