Skip to content

Commit

Permalink
change the way we apply a relative error + increase upper bound of th…
Browse files Browse the repository at this point in the history
…e ULP approx check in tests
  • Loading branch information
LorrensP-2158466 committed Feb 5, 2025
1 parent 538adfb commit 58d8e4f
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 48 deletions.
63 changes: 36 additions & 27 deletions src/intrinsics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use rustc_span::{Symbol, sym};
use self::atomic::EvalContextExt as _;
use self::helpers::{ToHost, ToSoft, check_arg_count};
use self::simd::EvalContextExt as _;
use crate::math::apply_random_float_error;
use crate::math::{apply_random_float_error, ulp_err_scale};
use crate::*;

impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
Expand Down Expand Up @@ -248,9 +248,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
"log2f32" => host.log2(),
_ => bug!(),
};
// Apply a relative error with a magnitude on the order of 2^-21 to simulate
// non-deterministic behaviour of floats
let res = apply_random_float_error(this, res.to_soft(), -21);
// Apply a relative error of 16ULP to simulate non-determinism
let res = apply_random_float_error(
this,
res.to_soft(),
ulp_err_scale::<rustc_apfloat::ieee::Single>(4)
);
let res = this.adjust_nan(res, &[f]);
this.write_scalar(res, dest)?;
}
Expand Down Expand Up @@ -278,9 +281,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
"log2f64" => host.log2(),
_ => bug!(),
};
// Apply a relative error with a magnitude on the order of 2^-50 to simulate
// non-deterministic behaviour of floats as per https://github.com/rust-lang/rust/pull/124609
let res = apply_random_float_error(this, res.to_soft(), -50);
// Apply a relative error of 16ULP to simulate non-determinism
let res = apply_random_float_error(
this,
res.to_soft(),
ulp_err_scale::<rustc_apfloat::ieee::Double>(4));
let res = this.adjust_nan(res, &[f]);
this.write_scalar(res, dest)?;
}
Expand Down Expand Up @@ -395,23 +400,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
};
let res = this.binary_op(op, &a, &b)?;
// `binary_op` already called `generate_nan` if needed
// Apply a relative error to simulate non-deterministic
// behaviour of floats
// Apply a relative error of 16ULP to simulate non-determinism
fn apply_error_and_write<'tcx, F: Float + Into<Scalar> >(
ecx: &mut MiriInterpCx<'tcx>,
val: F,
dest: &MPlaceTy<'tcx>,
err_scale: i32
res: F,
dest: &MPlaceTy<'tcx>
) -> InterpResult<'tcx> {
let res = apply_random_float_error(ecx, val, err_scale);
let res = apply_random_float_error(
ecx,
res,
ulp_err_scale::<F>(4)
);
ecx.write_scalar(res, dest)
}
let scalar = res.to_scalar_int()?;
match res.layout.ty.kind(){
ty::Float(FloatTy::F16) => apply_error_and_write(this, scalar.to_f16(), dest, -8),
ty::Float(FloatTy::F32) => apply_error_and_write(this, scalar.to_f32(), dest, -21),
ty::Float(FloatTy::F64) => apply_error_and_write(this, scalar.to_f64(), dest, -50),
ty::Float(FloatTy::F128) => apply_error_and_write(this, scalar.to_f128(), dest, -111),
ty::Float(FloatTy::F16) => apply_error_and_write(this, scalar.to_f16(), dest),
ty::Float(FloatTy::F32) => apply_error_and_write(this, scalar.to_f32(), dest),
ty::Float(FloatTy::F64) => apply_error_and_write(this, scalar.to_f64(), dest),
ty::Float(FloatTy::F128) => apply_error_and_write(this, scalar.to_f128(), dest),
_ => bug!("`{intrinsic_name}` intrinsic called with non-float input type")
}?;
}
Expand Down Expand Up @@ -461,25 +468,27 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
if !float_finite(&res)? {
throw_ub_format!("`{intrinsic_name}` intrinsic produced non-finite value as result");
}
// Apply a relative error to simulate non-deterministic
// behaviour of floats
// Apply a relative error of 16ULP to simulate non-determinism
fn apply_error_and_write<'tcx, F: Float + Into<Scalar> >(
ecx: &mut MiriInterpCx<'tcx>,
val: F,
dest: &MPlaceTy<'tcx>,
err_scale: i32
res: F,
dest: &MPlaceTy<'tcx>
) -> InterpResult<'tcx> {
let res = apply_random_float_error(ecx, val, err_scale);
let res = apply_random_float_error(
ecx,
res,
ulp_err_scale::<F>(4)
);
ecx.write_scalar(res, dest)
}
// This cannot be a NaN so we also don't have to apply any non-determinism.
// (Also, `binary_op` already called `generate_nan` if needed.)
let scalar = res.to_scalar_int()?;
match res.layout.ty.kind(){
ty::Float(FloatTy::F16) => apply_error_and_write(this, scalar.to_f16(), dest, -8),
ty::Float(FloatTy::F32) => apply_error_and_write(this, scalar.to_f32(), dest, -21),
ty::Float(FloatTy::F64) => apply_error_and_write(this, scalar.to_f64(), dest, -50),
ty::Float(FloatTy::F128) => apply_error_and_write(this, scalar.to_f128(), dest, -111),
ty::Float(FloatTy::F16) => apply_error_and_write(this, scalar.to_f16(), dest),
ty::Float(FloatTy::F32) => apply_error_and_write(this, scalar.to_f32(), dest),
ty::Float(FloatTy::F64) => apply_error_and_write(this, scalar.to_f64(), dest),
ty::Float(FloatTy::F128) => apply_error_and_write(this, scalar.to_f128(), dest),
_ => bug!("`{intrinsic_name}` intrinsic called with non-float input type")
}?;
}
Expand Down
14 changes: 14 additions & 0 deletions src/math.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,25 @@ use rand::Rng as _;
use rustc_apfloat::Float as _;
use rustc_apfloat::ieee::IeeeFloat;

<<<<<<< HEAD
/// 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.)
=======
/// Creates an error scale to pass to `apply_random_float_error` when applying a 2^n ULP error.
/// For example, if you want to apply a 16 ULP error, you should pass 4, because 2^4 = 16.
pub(crate) fn ulp_err_scale<F: rustc_apfloat::Float>(n: u32) -> i32 {
let n = i32::try_from(n)
.expect("`err_scale_for_ulp`: exponent is too large to create an error scale");
// we know this fits
let prec = i32::try_from(F::PRECISION).unwrap();
-(prec - n - 1)
}

/// Disturbes a floating-point result by a relative error on the order of (-2^scale, 2^scale).
>>>>>>> e2b42ea95 (change the way we apply a relative error + increase upper bound of the ULP approx check in tests)
pub(crate) fn apply_random_float_error<F: rustc_apfloat::Float>(
ecx: &mut crate::MiriInterpCx<'_>,
val: F,
Expand Down
55 changes: 37 additions & 18 deletions src/shims/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use rustc_target::callconv::{Conv, FnAbi};
use self::helpers::{ToHost, ToSoft};
use super::alloc::EvalContextExt as _;
use super::backtrace::EvalContextExt as _;
use crate::math::ulp_err_scale;
use crate::*;

/// Type of dynamic symbols (for `dlsym` et al)
Expand Down Expand Up @@ -761,9 +762,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
"tgammaf" => f_host.gamma(),
_ => bug!(),
};
// Apply a relative error with a magnitude on the order of 2^-21 to simulate
// non-deterministic behaviour of floats
let res = math::apply_random_float_error(this, res.to_soft(), -21);
// Apply a relative error of 16ULP to simulate non-determinism
let res = math::apply_random_float_error(
this,
res.to_soft(),
ulp_err_scale::<rustc_apfloat::ieee::Single>(4),
);
let res = this.adjust_nan(res, &[f]);
this.write_scalar(res, dest)?;
}
Expand All @@ -786,9 +790,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
"fdimf" => f1.to_host().abs_sub(f2.to_host()).to_soft(),
_ => bug!(),
};
// Apply a relative error with a magnitude on the order of 2^-21 to simulate
// non-deterministic behaviour of floats
let res = math::apply_random_float_error(this, res, -21);
// Apply a relative error of 16ULP to simulate non-determinism
let res = math::apply_random_float_error(
this,
res,
ulp_err_scale::<rustc_apfloat::ieee::Single>(4),
);
let res = this.adjust_nan(res, &[f1, f2]);
this.write_scalar(res, dest)?;
}
Expand Down Expand Up @@ -823,9 +830,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
"tgamma" => f_host.gamma(),
_ => bug!(),
};
// Apply a relative error with a magnitude on the order of 2^-50 to simulate
// non-deterministic behaviour of floats
let res = math::apply_random_float_error(this, res.to_soft(), -50);
// Apply a relative error of 16ULP to simulate non-determinism
let res = math::apply_random_float_error(
this,
res.to_soft(),
ulp_err_scale::<rustc_apfloat::ieee::Double>(4),
);
let res = this.adjust_nan(res, &[f]);
this.write_scalar(res, dest)?;
}
Expand All @@ -848,9 +858,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
"fdim" => f1.to_host().abs_sub(f2.to_host()).to_soft(),
_ => bug!(),
};
// Apply a relative error with a magnitude on the order of 2^-50 to simulate
// non-deterministic behaviour of floats
let res = math::apply_random_float_error(this, res, -50);
// Apply a relative error of 16ULP to simulate non-determinism
let res = math::apply_random_float_error(
this,
res,
ulp_err_scale::<rustc_apfloat::ieee::Double>(4),
);
let res = this.adjust_nan(res, &[f1, f2]);
this.write_scalar(res, dest)?;
}
Expand All @@ -876,9 +889,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Using host floats (but it's fine, these operations do not have guaranteed precision).
let (res, sign) = x.to_host().ln_gamma();
this.write_int(sign, &signp)?;
// Apply a relative error with a magnitude on the order of 2^-21 to simulate
// non-deterministic behaviour of floats
let res = math::apply_random_float_error(this, res.to_soft(), -21);
// Apply a relative error of 16ULP to simulate non-determinism
let res = math::apply_random_float_error(
this,
res.to_soft(),
ulp_err_scale::<rustc_apfloat::ieee::Single>(4),
);
let res = this.adjust_nan(res, &[x]);
this.write_scalar(res, dest)?;
}
Expand All @@ -890,9 +906,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Using host floats (but it's fine, these operations do not have guaranteed precision).
let (res, sign) = x.to_host().ln_gamma();
this.write_int(sign, &signp)?;
// Apply a relative error with a magnitude on the order of 2^-50 to simulate
// non-deterministic behaviour of floats
let res = math::apply_random_float_error(this, res.to_soft(), -50);
// Apply a relative error of 16ULP to simulate non-determinism
let res = math::apply_random_float_error(
this,
res.to_soft(),
ulp_err_scale::<rustc_apfloat::ieee::Double>(4),
);
let res = this.adjust_nan(res, &[x]);
this.write_scalar(res, dest)?;
}
Expand Down
9 changes: 6 additions & 3 deletions tests/pass/float.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ use std::fmt::{Debug, Display, LowerHex};
use std::hint::black_box;
use std::{f32, f64};

// accept up to 54ULP (16ULP for host floats and 16ULP for artificial error and 22 for rounding errors)
const ALLOWED_ULP_DIFF: i128 = 54;

macro_rules! assert_approx_eq {
($a:expr, $b:expr) => {{
let (a, b) = (&$a, &$b);
Expand All @@ -26,10 +29,10 @@ macro_rules! assert_approx_eq {
let b_bits = b.to_bits() as i128;
let ulp_difference = (a_bits - b_bits).abs();
assert!(
ulp_difference <= 16,
ulp_difference <= ALLOWED_ULP_DIFF,
"
{:?} is not approximately equal to {:?}
ulp diff: {ulp_difference} > 16
ulp diff: {ulp_difference} > {ALLOWED_ULP_DIFF}
",
*a,
*b
Expand All @@ -44,8 +47,8 @@ fn main() {
ops();
nan_casts();
rounding();
mul_add();
libm();
mul_add();
test_fast();
test_algebraic();
test_fmuladd();
Expand Down
8 changes: 8 additions & 0 deletions tests/pass/float.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

thread 'main' panicked at tests/pass/float.rs:LL:CC:

$HEX is not approximately equal to $HEX
ulp diff: 40 > 32

note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect

0 comments on commit 58d8e4f

Please sign in to comment.