Skip to content

Commit 51ebf6c

Browse files
implement pown(SNaN, 0) non-det
1 parent 958d5be commit 51ebf6c

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
@@ -362,7 +362,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
362362
let f = this.read_scalar(f)?.to_f32()?;
363363
let i = this.read_scalar(i)?.to_i32()?;
364364

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

@@ -380,7 +380,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
380380
let f = this.read_scalar(f)?.to_f64()?;
381381
let i = this.read_scalar(i)?.to_i32()?;
382382

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

@@ -540,8 +540,8 @@ fn random_nan<S: Semantics>(rng: &mut StdRng) -> IeeeFloat<S> {
540540
/// and the C standard leaves behavior for SNaNs unspecified.
541541
///
542542
/// Miri chooses to adhere to both implementations and returns either one of them non-deterministically.
543-
fn fixed_float_value<'tcx, S: Semantics>(
544-
ecx: &mut MiriInterpCx<'tcx>,
543+
fn fixed_float_value<S: Semantics>(
544+
ecx: &mut MiriInterpCx<'_>,
545545
intrinsic_name: &str,
546546
args: &[IeeeFloat<S>],
547547
) -> Option<IeeeFloat<S>> {
@@ -562,6 +562,7 @@ fn fixed_float_value<'tcx, S: Semantics>(
562562
// Handle both the musl and glibc cases non-deterministically.
563563
if !exp.is_signaling() || rng.random() { one } else { random_nan(rng) }
564564
}
565+
565566
// x^(±0) = 1 for any x, even a NaN, *but* not a SNaN
566567
("powf32" | "powf64", [base, exp]) if exp.is_zero() => {
567568
// Handle both the musl and glibc cases non-deterministically.
@@ -581,13 +582,21 @@ fn fixed_float_value<'tcx, S: Semantics>(
581582

582583
/// Returns `Some(output)` if `powi` (called `pown` in C) results in a fixed value specified in the C standard
583584
/// (specifically, C23 annex F.10.4.6) when doing `base^exp`. Otherwise, returns `None`.
584-
fn fixed_powi_float_value<S: Semantics>(base: IeeeFloat<S>, exp: i32) -> Option<IeeeFloat<S>> {
585-
match (base.category(), exp) {
586-
// x^0 = 1, if x is not a Signaling NaN
587-
// FIXME(#4286): The C ecosystem is inconsistent with handling sNaN's, some return 1 others propogate
588-
// the NaN. We should return either 1 or the NaN non-deterministically here.
589-
// But for now, just handle them all the same.
590-
(_, 0) => Some(IeeeFloat::<S>::one()),
585+
// 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.
586+
fn fixed_powi_float_value<S: Semantics>(
587+
ecx: &mut MiriInterpCx<'_>,
588+
base: IeeeFloat<S>,
589+
exp: i32,
590+
) -> Option<IeeeFloat<S>> {
591+
match exp {
592+
0 => {
593+
let one = IeeeFloat::<S>::one();
594+
let rng = ecx.machine.rng.get_mut();
595+
Some(
596+
// Handle both the musl and glibc powf cases non-deterministically.
597+
if !base.is_signaling() || rng.random() { one } else { random_nan(rng) },
598+
)
599+
}
591600

592601
_ => None,
593602
}

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

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

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

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

0 commit comments

Comments
 (0)