Skip to content

Commit 50efd8b

Browse files
fix(math): powf underflow sets ERANGE regardless of exponent sign (glibc parity)
powf's range-error branch required `y > 0.0` in its underflow case, so a negative-exponent underflow (e.g. powf(2, -200) -> 0) silently skipped ERANGE while glibc raises it. The f64 pow already uses the sign-agnostic `out == 0.0 && x != 0.0`; aligned powf to match. Verified against the gcc -lm oracle (powf(2,-200), powf(10,-50), powf(0.5,200) all ERANGE). Added two debug-mode unit tests (powf_underflow_{negative,positive}_exponent_sets_range_errno) — the in-crate debug harness is the one that actually exercises fl's powf body (the release integration path const-folds/interposes constant-arg powf calls). 116 math_abi unit tests pass; conformance_math_errno gate green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 40b7193 commit 50efd8b

1 file changed

Lines changed: 32 additions & 2 deletions

File tree

crates/frankenlibc-abi/src/math_abi.rs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,8 +1058,11 @@ pub unsafe extern "C" fn powf(x: f32, y: f32) -> f32 {
10581058
if x.is_finite() && y.is_finite() {
10591059
if x < 0.0 && y.fract() != 0.0 {
10601060
set_domain_errno();
1061-
} else if out.is_infinite() || (x == 0.0 && y < 0.0) || (out == 0.0 && y > 0.0 && x != 0.0)
1062-
{
1061+
} else if out.is_infinite() || (x == 0.0 && y < 0.0) || (out == 0.0 && x != 0.0) {
1062+
// Range error: overflow (inf), pole at x==0 with y<0, or underflow
1063+
// to zero. Mirrors f64 `pow`; the underflow case applies regardless
1064+
// of the exponent's sign (an earlier `y > 0.0` guard wrongly skipped
1065+
// negative-exponent underflow, e.g. powf(2, -200), which glibc flags).
10631066
set_range_errno();
10641067
}
10651068
}
@@ -8091,6 +8094,33 @@ mod tests {
80918094
assert_eq!(abi_errno(), libc::ERANGE);
80928095
}
80938096

8097+
#[test]
8098+
fn powf_underflow_negative_exponent_sets_range_errno() {
8099+
// Regression: powf's underflow branch used to require `y > 0.0`, which
8100+
// wrongly skipped negative-exponent underflow. glibc flags ERANGE for
8101+
// powf(2, -200) -> 0 (matches f64 pow).
8102+
set_errno_for_test(0);
8103+
let x = std::hint::black_box(2.0_f32);
8104+
let y = std::hint::black_box(-200.0_f32);
8105+
// SAFETY: ABI entrypoint accepts plain f32 input.
8106+
let out = unsafe { powf(x, y) };
8107+
std::hint::black_box(out);
8108+
assert_eq!(out, 0.0);
8109+
assert_eq!(abi_errno(), libc::ERANGE);
8110+
}
8111+
8112+
#[test]
8113+
fn powf_underflow_positive_exponent_sets_range_errno() {
8114+
set_errno_for_test(0);
8115+
let x = std::hint::black_box(0.5_f32);
8116+
let y = std::hint::black_box(200.0_f32);
8117+
// SAFETY: ABI entrypoint accepts plain f32 input.
8118+
let out = unsafe { powf(x, y) };
8119+
std::hint::black_box(out);
8120+
assert_eq!(out, 0.0);
8121+
assert_eq!(abi_errno(), libc::ERANGE);
8122+
}
8123+
80948124
#[test]
80958125
fn sqrt_negative_sets_domain_errno() {
80968126
set_errno_for_test(0);

0 commit comments

Comments
 (0)