Skip to content

Commit c4ba4f3

Browse files
authored
Rollup merge of #149593 - RalfJung:pow-nondet, r=Amanieu
powf, powi: point out SNaN non-determinism The non-determinism has two sources: - LLVM reserves the right to treat signaling NaNs as-if they were quiet, so it may fold `powf(x, 0)` to `1` even if `x` might be a signaling NaN. - Some libm `pow` implementations (e.g. the one in musl) don't implement the signaling NaN special-case. See https://rust.godbolt.org/z/chsbv5v4d. Cc `@tgross35` `@Amanieu`
2 parents 05faa95 + f5e3e8e commit c4ba4f3

File tree

1 file changed

+7
-6
lines changed

1 file changed

+7
-6
lines changed

src/math.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -272,17 +272,19 @@ where
272272
("pow", [base, exp]) if *base == one => {
273273
let rng = this.machine.rng.get_mut();
274274
// SNaN exponents get special treatment: they might return 1, or a NaN.
275+
// This is non-deterministic because LLVM can treat SNaN as QNaN, and because
276+
// implementation behavior differs between glibc and musl.
275277
let return_nan = exp.is_signaling() && this.machine.float_nondet && rng.random();
276-
// Handle both the musl and glibc cases non-deterministically.
277278
if return_nan { this.generate_nan(args) } else { one }
278279
}
279280

280281
// x^(±0) = 1 for any x, even a NaN
281282
("pow", [base, exp]) if exp.is_zero() => {
282283
let rng = this.machine.rng.get_mut();
283284
// SNaN bases get special treatment: they might return 1, or a NaN.
285+
// This is non-deterministic because LLVM can treat SNaN as QNaN, and because
286+
// implementation behavior differs between glibc and musl.
284287
let return_nan = base.is_signaling() && this.machine.float_nondet && rng.random();
285-
// Handle both the musl and glibc cases non-deterministically.
286288
if return_nan { this.generate_nan(args) } else { one }
287289
}
288290

@@ -306,10 +308,9 @@ where
306308
0 => {
307309
let one = IeeeFloat::<S>::one();
308310
let rng = ecx.machine.rng.get_mut();
309-
let return_nan = ecx.machine.float_nondet && rng.random() && base.is_signaling();
310-
// For SNaN treatment, we are consistent with `powf`above.
311-
// (We wouldn't have two, unlike powf all implementations seem to agree for powi,
312-
// but for now we are maximally conservative.)
311+
// SNaN bases get special treatment: they might return 1, or a NaN.
312+
// This is non-deterministic because LLVM can treat SNaN as QNaN.
313+
let return_nan = base.is_signaling() && ecx.machine.float_nondet && rng.random();
313314
Some(if return_nan { ecx.generate_nan(&[base]) } else { one })
314315
}
315316

0 commit comments

Comments
 (0)