Skip to content

Commit 3caf613

Browse files
apply changes from review + minor doc fixes
1 parent 9271fda commit 3caf613

File tree

2 files changed

+24
-50
lines changed

2 files changed

+24
-50
lines changed

src/tools/miri/src/intrinsics/mod.rs

Lines changed: 8 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
550550
}
551551
}
552552

553-
/// Applies a random 16ULP floating point error to `val` and returns the new value.
553+
/// Applies a random ULP floating point error to `val` and returns the new value.
554+
/// So if you want an X ULP error, `ulp_exponent` should be log2(X).
554555
/// Will fail if `val` is not a floating point number.
555556
fn apply_random_float_error_to_imm<'tcx>(
556557
ecx: &mut MiriInterpCx<'tcx>,
@@ -573,24 +574,19 @@ fn apply_random_float_error_to_imm<'tcx>(
573574
interp_ok(ImmTy::from_scalar_int(res, val.layout))
574575
}
575576

576-
// TODO(lorrens): This can be moved to `helpers` when we implement the other intrinsics.
577577
/// For the intrinsics:
578578
/// - sinf32, sinf64
579579
/// - cosf32, cosf64
580580
/// - expf32, expf64, exp2f32, exp2f64
581581
/// - logf32, logf64, log2f32, log2f64, log10f32, log10f64
582582
/// - powf32, powf64
583583
///
584-
/// Returns Some(`output`) if the operation results in a defined fixed `output` specified in the C standard when given `args`
584+
/// Returns Some(`output`) if the `intrinsic` results in a defined fixed `output` specified in the C standard when given `args`
585585
/// as arguments, else None.
586586
fn fixed_float_value<S: Semantics>(
587587
intrinsic_name: &str,
588588
args: &[IeeeFloat<S>],
589589
) -> Option<IeeeFloat<S>> {
590-
// TODO: not sure about this pattern matching stuff. It's definitly cleaner than if-else chains
591-
// Error code 0158 explains this: https://doc.rust-lang.org/stable/error_codes/E0158.html
592-
// The only reason I did this is to use the same function for powf as for sin/cos/exp/log
593-
// TODO: I can't fit powi logic in this because of the exponent being a i32 -> seperate fn "fixed_powi_float_value" for now
594590
let one = IeeeFloat::<S>::one();
595591
match (intrinsic_name, args) {
596592
// sin(+- 0) = +- 0.
@@ -620,21 +616,22 @@ fn fixed_float_value<S: Semantics>(
620616
// x^(±0) = 1 for any x, even a NaN
621617
("powf32" | "powf64", [_, exp]) if exp.is_zero() => Some(one),
622618

623-
// C standard doesn't specify or invalid combination
619+
// C standard doesn't specify any fixed outputs for other combinations of `intrinsic_name` and `args`,
620+
// or an invalid combination was given.
624621
_ => None,
625622
}
626623
}
627624

628-
/// Returns Some(`output`) if powi results in a fixed value specified in the C standard when doing `base^exp` else None.
625+
/// Returns Some(`output`) if `powi` results in a fixed value specified in the C standard when doing `base^exp` else None.
629626
fn fixed_powi_float_value<S: Semantics>(base: IeeeFloat<S>, exp: i32) -> Option<IeeeFloat<S>> {
630627
match (base.category(), exp) {
631628
// ±0^x = ±0 with x an odd integer.
632-
(Category::Zero, x) if x % 2 != 0 => Some(base), // preserve sign of zero.
629+
(Category::Zero, x) if x % 2 != 0 => Some(base), // preserve sign of zero
633630

634631
// ±0^x = +0 with x an even integer.
635632
(Category::Zero, x) if x % 2 == 0 => Some(IeeeFloat::<S>::ZERO),
636633

637-
// x^y = 1, if y is not a Signaling NaN
634+
// x^0 = 1, if x is not a Signaling NaN
638635
(_, 0) if !base.is_signaling() => Some(IeeeFloat::<S>::one()),
639636

640637
_ => None,
@@ -653,40 +650,3 @@ fn clamp_float_value<S: Semantics>(intrinsic_name: &str, val: IeeeFloat<S>) -> I
653650
_ => val,
654651
}
655652
}
656-
657-
// TODO: clean up when I'm sure this is not needed, because powf is now included in fixed_float_value
658-
// fn powf_impl<'tcx, S: Semantics, Op>(
659-
// ecx: &mut MiriInterpCx<'tcx>,
660-
// base: IeeeFloat<S>,
661-
// exp: IeeeFloat<S>,
662-
// op: Op,
663-
// ) -> IeeeFloat<S>
664-
// where
665-
// IeeeFloat<S>: ToHost,
666-
// Op: Fn(IeeeFloat<S>, IeeeFloat<S>) -> IeeeFloat<S>,
667-
// {
668-
// let one = IeeeFloat::<S>::one();
669-
// let fixed_res = match (base.category(), exp.category()) {
670-
// // 1^y = 1 for any y, even a NaN.
671-
// (Category::Normal, _) if base == one => Some(one),
672-
673-
// // (-1)^(±INF) = 1
674-
// (Category::Normal, Category::Infinity) if base == -one => Some(one),
675-
676-
// // x^(±0) = 1 for any x, even a NaN
677-
// (_, Category::Zero) => Some(one),
678-
679-
// // TODO: pow has a lot of "edge" cases which mostly result in ±0 or ±INF
680-
// // do we have to catch them all?
681-
// _ => None,
682-
// };
683-
684-
// fixed_res.unwrap_or_else(|| {
685-
// let res = op(base, exp);
686-
// // Apply a relative error of 4ULP to introduce some non-determinism
687-
// // simulating imprecise implementations and optimizations.
688-
// apply_random_float_error_ulp(
689-
// ecx, res, 2, // log2(4)
690-
// )
691-
// })
692-
// }

src/tools/miri/tests/pass/float.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,12 +1008,14 @@ pub fn libm() {
10081008

10091009
// Some inputs to powf and powi result in fixed outputs
10101010
// and thus must be exactly equal to that value
1011-
// TODO: How to test NaN inputs? f*::NAN is not guaranteed
1012-
// to be any specific bit pattern (in std).
1011+
// C standard says:
1012+
// 1^y = 1 for any y, even a NaN.
10131013
assert_eq!(1f32.powf(10.0), 1f32);
10141014
assert_eq!(1f64.powf(100.0), 1f64);
10151015
assert_eq!(1f32.powf(f32::INFINITY), 1f32);
10161016
assert_eq!(1f64.powf(f64::INFINITY), 1f64);
1017+
assert_eq!(1f32.powf(f32::NAN), 1f32);
1018+
assert_eq!(1f64.powf(f64::NAN), 1f64);
10171019

10181020
assert_eq!((-1f32).powf(f32::INFINITY), 1f32);
10191021
assert_eq!((-1f32).powf(f32::NEG_INFINITY), 1f32);
@@ -1030,6 +1032,18 @@ pub fn libm() {
10301032
assert_eq!(0f32.powi(9), 0f32);
10311033
assert_eq!(0f64.powi(99), 0f64);
10321034

1035+
// C standard says:
1036+
// x^0 = 1, if x is not a Signaling NaN
1037+
assert_eq!(10.0f32.powi(0), 1.0f32);
1038+
assert_eq!(10.0f64.powi(0), 1.0f64);
1039+
assert_eq!(f32::INFINITY.powi(0), 1.0f32);
1040+
assert_eq!(f64::INFINITY.powi(0), 1.0f64);
1041+
// f*::NAN doesn't specify which what kind of bit pattern
1042+
// the NAN will have.
1043+
// We **assume** f*::NAN is not signaling.
1044+
assert_eq!(f32::NAN.powi(0), 1.0f32);
1045+
assert_eq!(f64::NAN.powi(0), 1.0f64);
1046+
10331047
assert_eq!((-0f32).powi(10), 0f32);
10341048
assert_eq!((-0f64).powi(100), 0f64);
10351049
assert_eq!((-0f32).powi(9), -0f32);

0 commit comments

Comments
 (0)