Skip to content

Commit e2d7537

Browse files
implement pown(SNaN, 0) non-det
1 parent 2bac80d commit e2d7537

File tree

2 files changed

+24
-17
lines changed

2 files changed

+24
-17
lines changed

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

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
350350
let f = this.read_scalar(f)?.to_f32()?;
351351
let i = this.read_scalar(i)?.to_i32()?;
352352

353-
let res = fixed_powi_float_value(f, i).unwrap_or_else(|| {
353+
let res = fixed_powi_float_value(this, f, i).unwrap_or_else(|| {
354354
// Using host floats (but it's fine, this operation does not have guaranteed precision).
355355
let res = f.to_host().powi(i).to_soft();
356356

@@ -368,7 +368,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
368368
let f = this.read_scalar(f)?.to_f64()?;
369369
let i = this.read_scalar(i)?.to_i32()?;
370370

371-
let res = fixed_powi_float_value(f, i).unwrap_or_else(|| {
371+
let res = fixed_powi_float_value(this, f, i).unwrap_or_else(|| {
372372
// Using host floats (but it's fine, this operation does not have guaranteed precision).
373373
let res = f.to_host().powi(i).to_soft();
374374

@@ -528,8 +528,8 @@ fn random_nan<S: Semantics>(rng: &mut StdRng) -> IeeeFloat<S> {
528528
/// and the C standard leaves behavior for SNaNs unspecified.
529529
///
530530
/// Miri chooses to adhere to both implementations and returns either one of them non-deterministically.
531-
fn fixed_float_value<'tcx, S: Semantics>(
532-
ecx: &mut MiriInterpCx<'tcx>,
531+
fn fixed_float_value<S: Semantics>(
532+
ecx: &mut MiriInterpCx<'_>,
533533
intrinsic_name: &str,
534534
args: &[IeeeFloat<S>],
535535
) -> Option<IeeeFloat<S>> {
@@ -550,6 +550,7 @@ fn fixed_float_value<'tcx, S: Semantics>(
550550
// Handle both the musl and glibc cases non-deterministically.
551551
if !exp.is_signaling() || rng.random() { one } else { random_nan(rng) }
552552
}
553+
553554
// x^(±0) = 1 for any x, even a NaN, *but* not a SNaN
554555
("powf32" | "powf64", [base, exp]) if exp.is_zero() => {
555556
// Handle both the musl and glibc cases non-deterministically.
@@ -569,13 +570,21 @@ fn fixed_float_value<'tcx, S: Semantics>(
569570

570571
/// Returns `Some(output)` if `powi` (called `pown` in C) results in a fixed value specified in the C standard
571572
/// (specifically, C23 annex F.10.4.6) when doing `base^exp`. Otherwise, returns `None`.
572-
fn fixed_powi_float_value<S: Semantics>(base: IeeeFloat<S>, exp: i32) -> Option<IeeeFloat<S>> {
573-
match (base.category(), exp) {
574-
// x^0 = 1, if x is not a Signaling NaN
575-
// FIXME(#4286): The C ecosystem is inconsistent with handling sNaN's, some return 1 others propogate
576-
// the NaN. We should return either 1 or the NaN non-deterministically here.
577-
// But for now, just handle them all the same.
578-
(_, 0) => Some(IeeeFloat::<S>::one()),
573+
// REVIEW: I'm not sure what I should document here about pown(1, SNaN) since musl and glibc do the same and the C standard is explicit here.
574+
fn fixed_powi_float_value<S: Semantics>(
575+
ecx: &mut MiriInterpCx<'_>,
576+
base: IeeeFloat<S>,
577+
exp: i32,
578+
) -> Option<IeeeFloat<S>> {
579+
match exp {
580+
0 => {
581+
let one = IeeeFloat::<S>::one();
582+
let rng = ecx.machine.rng.get_mut();
583+
Some(
584+
// Handle both the musl and glibc powf cases non-deterministically.
585+
if !base.is_signaling() || rng.random() { one } else { random_nan(rng) },
586+
)
587+
}
579588

580589
_ => None,
581590
}

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

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1095,12 +1095,10 @@ pub fn libm() {
10951095
test_snan_nondet!(f32::powf(1.0, SNAN_F32));
10961096
test_snan_nondet!(f64::powf(1.0, SNAN_F64));
10971097

1098-
1099-
// For pown (powi in rust) the C standard says:
1100-
// x^0 = 1 for all x even a sNaN
1101-
// FIXME(#4286): this does not match the behavior of all implementations.
1102-
assert_eq!(SNAN_F32.powi(0), 1.0);
1103-
assert_eq!(SNAN_F64.powi(0), 1.0);
1098+
// same as powf (keep it consistent):
1099+
// x^(SNaN) = (1 | NaN)
1100+
test_snan_nondet!(f32::powi(SNAN_F32, 0));
1101+
test_snan_nondet!(f64::powi(SNAN_F64, 0));
11041102

11051103
assert_eq!(0f32.powi(10), 0.0);
11061104
assert_eq!(0f64.powi(100), 0.0);

0 commit comments

Comments
 (0)