Skip to content

Commit 58d8e4f

Browse files
change the way we apply a relative error + increase upper bound of the ULP approx check in tests
1 parent 538adfb commit 58d8e4f

File tree

5 files changed

+101
-48
lines changed

5 files changed

+101
-48
lines changed

src/intrinsics/mod.rs

+36-27
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use rustc_span::{Symbol, sym};
1313
use self::atomic::EvalContextExt as _;
1414
use self::helpers::{ToHost, ToSoft, check_arg_count};
1515
use self::simd::EvalContextExt as _;
16-
use crate::math::apply_random_float_error;
16+
use crate::math::{apply_random_float_error, ulp_err_scale};
1717
use crate::*;
1818

1919
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
@@ -248,9 +248,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
248248
"log2f32" => host.log2(),
249249
_ => bug!(),
250250
};
251-
// Apply a relative error with a magnitude on the order of 2^-21 to simulate
252-
// non-deterministic behaviour of floats
253-
let res = apply_random_float_error(this, res.to_soft(), -21);
251+
// Apply a relative error of 16ULP to simulate non-determinism
252+
let res = apply_random_float_error(
253+
this,
254+
res.to_soft(),
255+
ulp_err_scale::<rustc_apfloat::ieee::Single>(4)
256+
);
254257
let res = this.adjust_nan(res, &[f]);
255258
this.write_scalar(res, dest)?;
256259
}
@@ -278,9 +281,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
278281
"log2f64" => host.log2(),
279282
_ => bug!(),
280283
};
281-
// Apply a relative error with a magnitude on the order of 2^-50 to simulate
282-
// non-deterministic behaviour of floats as per https://github.com/rust-lang/rust/pull/124609
283-
let res = apply_random_float_error(this, res.to_soft(), -50);
284+
// Apply a relative error of 16ULP to simulate non-determinism
285+
let res = apply_random_float_error(
286+
this,
287+
res.to_soft(),
288+
ulp_err_scale::<rustc_apfloat::ieee::Double>(4));
284289
let res = this.adjust_nan(res, &[f]);
285290
this.write_scalar(res, dest)?;
286291
}
@@ -395,23 +400,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
395400
};
396401
let res = this.binary_op(op, &a, &b)?;
397402
// `binary_op` already called `generate_nan` if needed
398-
// Apply a relative error to simulate non-deterministic
399-
// behaviour of floats
403+
// Apply a relative error of 16ULP to simulate non-determinism
400404
fn apply_error_and_write<'tcx, F: Float + Into<Scalar> >(
401405
ecx: &mut MiriInterpCx<'tcx>,
402-
val: F,
403-
dest: &MPlaceTy<'tcx>,
404-
err_scale: i32
406+
res: F,
407+
dest: &MPlaceTy<'tcx>
405408
) -> InterpResult<'tcx> {
406-
let res = apply_random_float_error(ecx, val, err_scale);
409+
let res = apply_random_float_error(
410+
ecx,
411+
res,
412+
ulp_err_scale::<F>(4)
413+
);
407414
ecx.write_scalar(res, dest)
408415
}
409416
let scalar = res.to_scalar_int()?;
410417
match res.layout.ty.kind(){
411-
ty::Float(FloatTy::F16) => apply_error_and_write(this, scalar.to_f16(), dest, -8),
412-
ty::Float(FloatTy::F32) => apply_error_and_write(this, scalar.to_f32(), dest, -21),
413-
ty::Float(FloatTy::F64) => apply_error_and_write(this, scalar.to_f64(), dest, -50),
414-
ty::Float(FloatTy::F128) => apply_error_and_write(this, scalar.to_f128(), dest, -111),
418+
ty::Float(FloatTy::F16) => apply_error_and_write(this, scalar.to_f16(), dest),
419+
ty::Float(FloatTy::F32) => apply_error_and_write(this, scalar.to_f32(), dest),
420+
ty::Float(FloatTy::F64) => apply_error_and_write(this, scalar.to_f64(), dest),
421+
ty::Float(FloatTy::F128) => apply_error_and_write(this, scalar.to_f128(), dest),
415422
_ => bug!("`{intrinsic_name}` intrinsic called with non-float input type")
416423
}?;
417424
}
@@ -461,25 +468,27 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
461468
if !float_finite(&res)? {
462469
throw_ub_format!("`{intrinsic_name}` intrinsic produced non-finite value as result");
463470
}
464-
// Apply a relative error to simulate non-deterministic
465-
// behaviour of floats
471+
// Apply a relative error of 16ULP to simulate non-determinism
466472
fn apply_error_and_write<'tcx, F: Float + Into<Scalar> >(
467473
ecx: &mut MiriInterpCx<'tcx>,
468-
val: F,
469-
dest: &MPlaceTy<'tcx>,
470-
err_scale: i32
474+
res: F,
475+
dest: &MPlaceTy<'tcx>
471476
) -> InterpResult<'tcx> {
472-
let res = apply_random_float_error(ecx, val, err_scale);
477+
let res = apply_random_float_error(
478+
ecx,
479+
res,
480+
ulp_err_scale::<F>(4)
481+
);
473482
ecx.write_scalar(res, dest)
474483
}
475484
// This cannot be a NaN so we also don't have to apply any non-determinism.
476485
// (Also, `binary_op` already called `generate_nan` if needed.)
477486
let scalar = res.to_scalar_int()?;
478487
match res.layout.ty.kind(){
479-
ty::Float(FloatTy::F16) => apply_error_and_write(this, scalar.to_f16(), dest, -8),
480-
ty::Float(FloatTy::F32) => apply_error_and_write(this, scalar.to_f32(), dest, -21),
481-
ty::Float(FloatTy::F64) => apply_error_and_write(this, scalar.to_f64(), dest, -50),
482-
ty::Float(FloatTy::F128) => apply_error_and_write(this, scalar.to_f128(), dest, -111),
488+
ty::Float(FloatTy::F16) => apply_error_and_write(this, scalar.to_f16(), dest),
489+
ty::Float(FloatTy::F32) => apply_error_and_write(this, scalar.to_f32(), dest),
490+
ty::Float(FloatTy::F64) => apply_error_and_write(this, scalar.to_f64(), dest),
491+
ty::Float(FloatTy::F128) => apply_error_and_write(this, scalar.to_f128(), dest),
483492
_ => bug!("`{intrinsic_name}` intrinsic called with non-float input type")
484493
}?;
485494
}

src/math.rs

+14
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,25 @@ use rand::Rng as _;
22
use rustc_apfloat::Float as _;
33
use rustc_apfloat::ieee::IeeeFloat;
44

5+
<<<<<<< HEAD
56
/// Disturbes a floating-point result by a relative error in the range (-2^scale, 2^scale).
67
///
78
/// For a 2^N ULP error, you can use an `err_scale` of `-(F::PRECISION - 1 - N)`.
89
/// In other words, a 1 ULP (absolute) error is the same as a `2^-(F::PRECISION-1)` relative error.
910
/// (Subtracting 1 compensates for the integer bit.)
11+
=======
12+
/// Creates an error scale to pass to `apply_random_float_error` when applying a 2^n ULP error.
13+
/// For example, if you want to apply a 16 ULP error, you should pass 4, because 2^4 = 16.
14+
pub(crate) fn ulp_err_scale<F: rustc_apfloat::Float>(n: u32) -> i32 {
15+
let n = i32::try_from(n)
16+
.expect("`err_scale_for_ulp`: exponent is too large to create an error scale");
17+
// we know this fits
18+
let prec = i32::try_from(F::PRECISION).unwrap();
19+
-(prec - n - 1)
20+
}
21+
22+
/// Disturbes a floating-point result by a relative error on the order of (-2^scale, 2^scale).
23+
>>>>>>> e2b42ea95 (change the way we apply a relative error + increase upper bound of the ULP approx check in tests)
1024
pub(crate) fn apply_random_float_error<F: rustc_apfloat::Float>(
1125
ecx: &mut crate::MiriInterpCx<'_>,
1226
val: F,

src/shims/foreign_items.rs

+37-18
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use rustc_target::callconv::{Conv, FnAbi};
1717
use self::helpers::{ToHost, ToSoft};
1818
use super::alloc::EvalContextExt as _;
1919
use super::backtrace::EvalContextExt as _;
20+
use crate::math::ulp_err_scale;
2021
use crate::*;
2122

2223
/// Type of dynamic symbols (for `dlsym` et al)
@@ -761,9 +762,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
761762
"tgammaf" => f_host.gamma(),
762763
_ => bug!(),
763764
};
764-
// Apply a relative error with a magnitude on the order of 2^-21 to simulate
765-
// non-deterministic behaviour of floats
766-
let res = math::apply_random_float_error(this, res.to_soft(), -21);
765+
// Apply a relative error of 16ULP to simulate non-determinism
766+
let res = math::apply_random_float_error(
767+
this,
768+
res.to_soft(),
769+
ulp_err_scale::<rustc_apfloat::ieee::Single>(4),
770+
);
767771
let res = this.adjust_nan(res, &[f]);
768772
this.write_scalar(res, dest)?;
769773
}
@@ -786,9 +790,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
786790
"fdimf" => f1.to_host().abs_sub(f2.to_host()).to_soft(),
787791
_ => bug!(),
788792
};
789-
// Apply a relative error with a magnitude on the order of 2^-21 to simulate
790-
// non-deterministic behaviour of floats
791-
let res = math::apply_random_float_error(this, res, -21);
793+
// Apply a relative error of 16ULP to simulate non-determinism
794+
let res = math::apply_random_float_error(
795+
this,
796+
res,
797+
ulp_err_scale::<rustc_apfloat::ieee::Single>(4),
798+
);
792799
let res = this.adjust_nan(res, &[f1, f2]);
793800
this.write_scalar(res, dest)?;
794801
}
@@ -823,9 +830,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
823830
"tgamma" => f_host.gamma(),
824831
_ => bug!(),
825832
};
826-
// Apply a relative error with a magnitude on the order of 2^-50 to simulate
827-
// non-deterministic behaviour of floats
828-
let res = math::apply_random_float_error(this, res.to_soft(), -50);
833+
// Apply a relative error of 16ULP to simulate non-determinism
834+
let res = math::apply_random_float_error(
835+
this,
836+
res.to_soft(),
837+
ulp_err_scale::<rustc_apfloat::ieee::Double>(4),
838+
);
829839
let res = this.adjust_nan(res, &[f]);
830840
this.write_scalar(res, dest)?;
831841
}
@@ -848,9 +858,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
848858
"fdim" => f1.to_host().abs_sub(f2.to_host()).to_soft(),
849859
_ => bug!(),
850860
};
851-
// Apply a relative error with a magnitude on the order of 2^-50 to simulate
852-
// non-deterministic behaviour of floats
853-
let res = math::apply_random_float_error(this, res, -50);
861+
// Apply a relative error of 16ULP to simulate non-determinism
862+
let res = math::apply_random_float_error(
863+
this,
864+
res,
865+
ulp_err_scale::<rustc_apfloat::ieee::Double>(4),
866+
);
854867
let res = this.adjust_nan(res, &[f1, f2]);
855868
this.write_scalar(res, dest)?;
856869
}
@@ -876,9 +889,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
876889
// Using host floats (but it's fine, these operations do not have guaranteed precision).
877890
let (res, sign) = x.to_host().ln_gamma();
878891
this.write_int(sign, &signp)?;
879-
// Apply a relative error with a magnitude on the order of 2^-21 to simulate
880-
// non-deterministic behaviour of floats
881-
let res = math::apply_random_float_error(this, res.to_soft(), -21);
892+
// Apply a relative error of 16ULP to simulate non-determinism
893+
let res = math::apply_random_float_error(
894+
this,
895+
res.to_soft(),
896+
ulp_err_scale::<rustc_apfloat::ieee::Single>(4),
897+
);
882898
let res = this.adjust_nan(res, &[x]);
883899
this.write_scalar(res, dest)?;
884900
}
@@ -890,9 +906,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
890906
// Using host floats (but it's fine, these operations do not have guaranteed precision).
891907
let (res, sign) = x.to_host().ln_gamma();
892908
this.write_int(sign, &signp)?;
893-
// Apply a relative error with a magnitude on the order of 2^-50 to simulate
894-
// non-deterministic behaviour of floats
895-
let res = math::apply_random_float_error(this, res.to_soft(), -50);
909+
// Apply a relative error of 16ULP to simulate non-determinism
910+
let res = math::apply_random_float_error(
911+
this,
912+
res.to_soft(),
913+
ulp_err_scale::<rustc_apfloat::ieee::Double>(4),
914+
);
896915
let res = this.adjust_nan(res, &[x]);
897916
this.write_scalar(res, dest)?;
898917
}

tests/pass/float.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ use std::fmt::{Debug, Display, LowerHex};
1212
use std::hint::black_box;
1313
use std::{f32, f64};
1414

15+
// accept up to 54ULP (16ULP for host floats and 16ULP for artificial error and 22 for rounding errors)
16+
const ALLOWED_ULP_DIFF: i128 = 54;
17+
1518
macro_rules! assert_approx_eq {
1619
($a:expr, $b:expr) => {{
1720
let (a, b) = (&$a, &$b);
@@ -26,10 +29,10 @@ macro_rules! assert_approx_eq {
2629
let b_bits = b.to_bits() as i128;
2730
let ulp_difference = (a_bits - b_bits).abs();
2831
assert!(
29-
ulp_difference <= 16,
32+
ulp_difference <= ALLOWED_ULP_DIFF,
3033
"
3134
{:?} is not approximately equal to {:?}
32-
ulp diff: {ulp_difference} > 16
35+
ulp diff: {ulp_difference} > {ALLOWED_ULP_DIFF}
3336
",
3437
*a,
3538
*b
@@ -44,8 +47,8 @@ fn main() {
4447
ops();
4548
nan_casts();
4649
rounding();
47-
mul_add();
4850
libm();
51+
mul_add();
4952
test_fast();
5053
test_algebraic();
5154
test_fmuladd();

tests/pass/float.stderr

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
thread 'main' panicked at tests/pass/float.rs:LL:CC:
3+
4+
$HEX is not approximately equal to $HEX
5+
ulp diff: 40 > 32
6+
7+
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
8+
note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect

0 commit comments

Comments
 (0)