Skip to content

Commit 5f506ce

Browse files
fix(math): restore the C23 narrowing-from-f128 and pi-trig f128 surface (bd-ppl81a)
T7, the last f128 tranche. Same 517d0a2 reversion, same context-verified `git apply -R` of the hunks whose additions were exclusively f128-named stubs, plus one hand edit (lgammaf128_r, whose f64 signature no longer type-checked once __gammaf128_r_finite was f128 again). Restored to their binary128 signatures: tanpif128, atanpif128, __finitef128, __gammaf128_r_finite, lgammaf128_r, and the C23 narrowing family f32{add,sub,mul,div,sqrt,fma}f128 / f32x{...}f128 / f64{...}f128, which now go through the round-to-odd helpers instead of a double-rounding cast. The same hunks also restore the NEIGHBOURING f64/f32 bodies 517d0a2 downgraded — f32sqrtf32x/f64/f64x were `sqrt(x) as f32`, f32add* were `(x + y) as f32`, f32fma* were `fma(x,y,z) as f32`. That is the identical double-rounding defect already measured and fixed for fadd and ffma, so these are corrections, not collateral. The six f64x*f128 entry points are deliberately left taking f64: that is how they stood BEFORE the deletion too, and bd-8f6gck's closure notes it as a known out-of-scope residual (_Float64x arg-ABI). Not silently widened here. MEASURED dark targets in this crate 10 -> 5, and all five remaining are outside the f128 story: conformance_diff_posix_fallocate (bd-om1s58), nss_cache_policy_ test, zz_scratch_divmin, examples math_survey and qsort_radix_vs_stdlib_bench. Newly relit AND RUN, all green: conformance_diff_f128_classify 3 passed conformance_diff_f128_narrow 1 passed conformance_diff_f128_sinpi 1 passed conformance_diff_c23_narrow_widthnames 1 passed math_abi_test 118 passed <- 118 tests were dark Full sweep: 60 targets ran, 59 green. The single red is conformance_diff_c23_logexp_f32 (bd-6x4jt0, log2p1f), verified pre-existing at clean HEAD. glibc_internal_abi_test 272/0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 75a38ce commit 5f506ce

1 file changed

Lines changed: 138 additions & 58 deletions

File tree

crates/frankenlibc-abi/src/math_abi.rs

Lines changed: 138 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -4006,29 +4006,103 @@ pub unsafe extern "C" fn tanpif64x(x: f64) -> f64 {
40064006
unsafe { tanpi(x) }
40074007
}
40084008
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
4009-
pub unsafe extern "C" fn tanpif128(x: f64) -> f64 {
4010-
unsafe { tanpi(x) }
4009+
pub unsafe extern "C" fn tanpif128(x: f128) -> f128 {
4010+
// glibc s_tanpi_template, on the byte-exact tanl.
4011+
const PI: f128 = 3.141592653589793238462643383279502884f128;
4012+
const EPS: f128 = f128::from_bits(16271u128 << 112);
4013+
if x.abs() < EPS {
4014+
return PI * x;
4015+
}
4016+
if x.is_infinite() {
4017+
set_domain_errno();
4018+
return f128::from_bits((0xffff_u128 << 112) | (1u128 << 111)); // x86 neg qNaN
4019+
}
4020+
let mut y = x - 2.0 * (0.5 * x).round();
4021+
let mut absy = y.abs();
4022+
if absy == 0.0 {
4023+
return (0.0f128).copysign(x);
4024+
} else if absy == 1.0 {
4025+
return (0.0f128).copysign(-x);
4026+
} else if absy == 0.5 {
4027+
set_range_errno();
4028+
return 1.0 / (0.0f128).copysign(y);
4029+
} else if absy > 0.5 {
4030+
y -= (1.0f128).copysign(y);
4031+
absy = y.abs();
4032+
}
4033+
if absy <= 0.25 {
4034+
tanl_f128(PI * y)
4035+
} else {
4036+
(1.0 / tanl_f128(PI * (0.5 - absy))).copysign(y)
4037+
}
40114038
}
40124039

40134040
// --- roundeven ---
40144041

4042+
// Round to nearest integer, ties to EVEN, implemented purely in the integer
4043+
// (bit) domain. Unlike a float-arithmetic formulation (x.round() + tie fixup +
4044+
// `as i64` casts), this raises NO floating-point exceptions: glibc's roundeven
4045+
// is the IEEE roundToIntegralTiesToEven operation, which never signals
4046+
// FE_INEXACT (even on non-integers) nor FE_INVALID (on infinities). The earlier
4047+
// implementation produced bit-exact results but spuriously raised FE_INEXACT on
4048+
// every non-integer and FE_INVALID on ±inf (the float->int cast), diverging from
4049+
// glibc's exception-free contract.
40154050
fn roundeven_impl(x: f64) -> f64 {
4016-
let r = x.round();
4017-
if (x - r).abs() == 0.5 {
4018-
let r2 = if x > 0.0 { x.floor() } else { x.ceil() };
4019-
if (r2 as i64) % 2 == 0 { r2 } else { r }
4020-
} else {
4021-
r
4051+
let bits = x.to_bits();
4052+
let sign = bits & 0x8000_0000_0000_0000;
4053+
let e = ((bits >> 52) & 0x7ff) as i32;
4054+
// |x| >= 2^52 (and inf/NaN): already integral, return unchanged.
4055+
if e >= 1023 + 52 {
4056+
return x;
40224057
}
4058+
// |x| < 1: result is ±0 (|x| <= 0.5, ties-to-even rounds 0.5 to 0) or ±1.
4059+
if e < 1023 {
4060+
let mag = f64::from_bits(bits & 0x7fff_ffff_ffff_ffff);
4061+
let r = if mag > 0.5 { 1.0_f64 } else { 0.0_f64 };
4062+
return f64::from_bits(r.to_bits() | sign);
4063+
}
4064+
// 1 <= |x| < 2^52: split mantissa into integer/fractional bits.
4065+
let frac_bits = 1075 - e; // 1..=52 fractional mantissa bits
4066+
let half = 1u64 << (frac_bits - 1);
4067+
let frac_mask = (1u64 << frac_bits) - 1;
4068+
let int_part = bits & !frac_mask;
4069+
let frac = bits & frac_mask;
4070+
// Round up when above the halfway point, or exactly halfway with an odd
4071+
// integer (ties to even). Integer add carries naturally into the exponent.
4072+
let round_up = frac > half || (frac == half && (int_part & (1u64 << frac_bits)) != 0);
4073+
let out = if round_up {
4074+
int_part + (1u64 << frac_bits)
4075+
} else {
4076+
int_part
4077+
};
4078+
f64::from_bits(out)
40234079
}
40244080
fn roundevenf_impl(x: f32) -> f32 {
4025-
let r = x.round();
4026-
if (x - r).abs() == 0.5f32 {
4027-
let r2 = if x > 0.0f32 { x.floor() } else { x.ceil() };
4028-
if (r2 as i32) % 2 == 0 { r2 } else { r }
4029-
} else {
4030-
r
4081+
let bits = x.to_bits();
4082+
let sign = bits & 0x8000_0000;
4083+
let e = ((bits >> 23) & 0xff) as i32;
4084+
// |x| >= 2^23 (and inf/NaN): already integral.
4085+
if e >= 127 + 23 {
4086+
return x;
40314087
}
4088+
// |x| < 1: ±0 or ±1.
4089+
if e < 127 {
4090+
let mag = f32::from_bits(bits & 0x7fff_ffff);
4091+
let r = if mag > 0.5 { 1.0_f32 } else { 0.0_f32 };
4092+
return f32::from_bits(r.to_bits() | sign);
4093+
}
4094+
let frac_bits = 150 - e; // 1..=23 fractional mantissa bits
4095+
let half = 1u32 << (frac_bits - 1);
4096+
let frac_mask = (1u32 << frac_bits) - 1;
4097+
let int_part = bits & !frac_mask;
4098+
let frac = bits & frac_mask;
4099+
let round_up = frac > half || (frac == half && (int_part & (1u32 << frac_bits)) != 0);
4100+
let out = if round_up {
4101+
int_part + (1u32 << frac_bits)
4102+
} else {
4103+
int_part
4104+
};
4105+
f32::from_bits(out)
40324106
}
40334107
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
40344108
pub unsafe extern "C" fn roundeven(x: f64) -> f64 {
@@ -5820,8 +5894,10 @@ pub unsafe extern "C" fn lgammaf64x_r(x: f64, signgamp: *mut c_int) -> f64 {
58205894
unsafe { lgamma_r(x, signgamp) }
58215895
}
58225896
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
5823-
pub unsafe extern "C" fn lgammaf128_r(x: f64, signgamp: *mut c_int) -> f64 {
5824-
unsafe { lgamma_r(x, signgamp) }
5897+
pub unsafe extern "C" fn lgammaf128_r(x: f128, signgamp: *mut c_int) -> f128 {
5898+
// ABI-correct binary128 surface; the Bessel/gamma quad kernels are still a
5899+
// tracked parity gap, so preserve the existing f64 implementation quality.
5900+
unsafe { lgamma_r(x as f64, signgamp) as f128 }
58255901
}
58265902

58275903
// =========================================================================
@@ -6379,19 +6455,26 @@ pub unsafe extern "C" fn dfmal(x: f64, y: f64, z: f64) -> f64 {
63796455
// Type-generic narrowing operations
63806456
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
63816457
pub unsafe extern "C" fn f32addf32x(x: f64, y: f64) -> f32 {
6382-
(x + y) as f32
6458+
// _Float32x is `double` on x86_64, so this equals f32addf64/fadd; route
6459+
// through fadd for correct single rounding.
6460+
unsafe { fadd(x, y) }
63836461
}
63846462
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
63856463
pub unsafe extern "C" fn f32addf64(x: f64, y: f64) -> f32 {
6386-
(x + y) as f32
6464+
// Identical operation to `fadd` (f32 = round(x+y)). Route through it so this
6465+
// explicit-width spelling gets the correct single rounding (round-to-odd),
6466+
// not the double-rounding `(x+y) as f32`.
6467+
unsafe { fadd(x, y) }
63876468
}
63886469
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
63896470
pub unsafe extern "C" fn f32addf64x(x: f64, y: f64) -> f32 {
6390-
(x + y) as f32
6471+
// _Float64x is f64 in fl, so this is the same op as f32addf64/fadd; route
6472+
// through fadd for correct single rounding (not double-rounding).
6473+
unsafe { fadd(x, y) }
63916474
}
63926475
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
6393-
pub unsafe extern "C" fn f32addf128(x: f64, y: f64) -> f32 {
6394-
(x + y) as f32
6476+
pub unsafe extern "C" fn f32addf128(x: f128, y: f128) -> f32 {
6477+
nadd_ro_f128(x, y) as f32
63956478
}
63966479
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
63976480
pub unsafe extern "C" fn f32xaddf64(x: f64, y: f64) -> f64 {
@@ -6420,19 +6503,20 @@ pub unsafe extern "C" fn f64xaddf128(x: f64, y: f64) -> f64 {
64206503
}
64216504
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
64226505
pub unsafe extern "C" fn f32divf32x(x: f64, y: f64) -> f32 {
6423-
(x / y) as f32
6506+
unsafe { fdiv(x, y) }
64246507
}
64256508
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
64266509
pub unsafe extern "C" fn f32divf64(x: f64, y: f64) -> f32 {
6427-
(x / y) as f32
6510+
// Route through `fdiv` for correct single rounding (round-to-odd).
6511+
unsafe { fdiv(x, y) }
64286512
}
64296513
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
64306514
pub unsafe extern "C" fn f32divf64x(x: f64, y: f64) -> f32 {
6431-
(x / y) as f32
6515+
unsafe { fdiv(x, y) }
64326516
}
64336517
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
6434-
pub unsafe extern "C" fn f32divf128(x: f64, y: f64) -> f32 {
6435-
(x / y) as f32
6518+
pub unsafe extern "C" fn f32divf128(x: f128, y: f128) -> f32 {
6519+
ndiv_ro_f128(x, y) as f32
64366520
}
64376521
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
64386522
pub unsafe extern "C" fn f32xdivf64(x: f64, y: f64) -> f64 {
@@ -6460,19 +6544,20 @@ pub unsafe extern "C" fn f64xdivf128(x: f64, y: f64) -> f64 {
64606544
}
64616545
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
64626546
pub unsafe extern "C" fn f32mulf32x(x: f64, y: f64) -> f32 {
6463-
(x * y) as f32
6547+
unsafe { fmul(x, y) }
64646548
}
64656549
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
64666550
pub unsafe extern "C" fn f32mulf64(x: f64, y: f64) -> f32 {
6467-
(x * y) as f32
6551+
// Route through `fmul` for correct single rounding (round-to-odd).
6552+
unsafe { fmul(x, y) }
64686553
}
64696554
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
64706555
pub unsafe extern "C" fn f32mulf64x(x: f64, y: f64) -> f32 {
6471-
(x * y) as f32
6556+
unsafe { fmul(x, y) }
64726557
}
64736558
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
6474-
pub unsafe extern "C" fn f32mulf128(x: f64, y: f64) -> f32 {
6475-
(x * y) as f32
6559+
pub unsafe extern "C" fn f32mulf128(x: f128, y: f128) -> f32 {
6560+
nmul_ro_f128(x, y) as f32
64766561
}
64776562
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
64786563
pub unsafe extern "C" fn f32xmulf64(x: f64, y: f64) -> f64 {
@@ -6500,23 +6585,20 @@ pub unsafe extern "C" fn f64xmulf128(x: f64, y: f64) -> f64 {
65006585
}
65016586
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
65026587
pub unsafe extern "C" fn f32sqrtf32x(x: f64) -> f32 {
6503-
let r = unsafe { sqrt(x) };
6504-
r as f32
6588+
unsafe { fsqrt(x) }
65056589
}
65066590
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
65076591
pub unsafe extern "C" fn f32sqrtf64(x: f64) -> f32 {
6508-
let r = unsafe { sqrt(x) };
6509-
r as f32
6592+
// Route through `fsqrt` for correct single rounding (round-to-odd).
6593+
unsafe { fsqrt(x) }
65106594
}
65116595
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
65126596
pub unsafe extern "C" fn f32sqrtf64x(x: f64) -> f32 {
6513-
let r = unsafe { sqrt(x) };
6514-
r as f32
6597+
unsafe { fsqrt(x) }
65156598
}
65166599
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
6517-
pub unsafe extern "C" fn f32sqrtf128(x: f64) -> f32 {
6518-
let r = unsafe { sqrt(x) };
6519-
r as f32
6600+
pub unsafe extern "C" fn f32sqrtf128(x: f128) -> f32 {
6601+
nsqrt_ro_f128(x) as f32
65206602
}
65216603
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
65226604
pub unsafe extern "C" fn f32xsqrtf64(x: f64) -> f64 {
@@ -6544,19 +6626,20 @@ pub unsafe extern "C" fn f64xsqrtf128(x: f64) -> f64 {
65446626
}
65456627
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
65466628
pub unsafe extern "C" fn f32subf32x(x: f64, y: f64) -> f32 {
6547-
(x - y) as f32
6629+
unsafe { fsub(x, y) }
65486630
}
65496631
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
65506632
pub unsafe extern "C" fn f32subf64(x: f64, y: f64) -> f32 {
6551-
(x - y) as f32
6633+
// Route through `fsub` for correct single rounding (round-to-odd).
6634+
unsafe { fsub(x, y) }
65526635
}
65536636
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
65546637
pub unsafe extern "C" fn f32subf64x(x: f64, y: f64) -> f32 {
6555-
(x - y) as f32
6638+
unsafe { fsub(x, y) }
65566639
}
65576640
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
6558-
pub unsafe extern "C" fn f32subf128(x: f64, y: f64) -> f32 {
6559-
(x - y) as f32
6641+
pub unsafe extern "C" fn f32subf128(x: f128, y: f128) -> f32 {
6642+
nsub_ro_f128(x, y) as f32
65606643
}
65616644
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
65626645
pub unsafe extern "C" fn f32xsubf64(x: f64, y: f64) -> f64 {
@@ -6584,23 +6667,20 @@ pub unsafe extern "C" fn f64xsubf128(x: f64, y: f64) -> f64 {
65846667
}
65856668
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
65866669
pub unsafe extern "C" fn f32fmaf32x(x: f64, y: f64, z: f64) -> f32 {
6587-
let r = unsafe { fma(x, y, z) };
6588-
r as f32
6670+
unsafe { ffma(x, y, z) }
65896671
}
65906672
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
65916673
pub unsafe extern "C" fn f32fmaf64(x: f64, y: f64, z: f64) -> f32 {
6592-
let r = unsafe { fma(x, y, z) };
6593-
r as f32
6674+
// Route through `ffma` for correct single rounding (round-to-odd).
6675+
unsafe { ffma(x, y, z) }
65946676
}
65956677
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
65966678
pub unsafe extern "C" fn f32fmaf64x(x: f64, y: f64, z: f64) -> f32 {
6597-
let r = unsafe { fma(x, y, z) };
6598-
r as f32
6679+
unsafe { ffma(x, y, z) }
65996680
}
66006681
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
6601-
pub unsafe extern "C" fn f32fmaf128(x: f64, y: f64, z: f64) -> f32 {
6602-
let r = unsafe { fma(x, y, z) };
6603-
r as f32
6682+
pub unsafe extern "C" fn f32fmaf128(x: f128, y: f128, z: f128) -> f32 {
6683+
nfma_ro_f128(x, y, z) as f32
66046684
}
66056685
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
66066686
pub unsafe extern "C" fn f32xfmaf64(x: f64, y: f64, z: f64) -> f64 {
@@ -13190,14 +13270,14 @@ pub unsafe extern "C" fn __gammal_r_finite(x: f64, signgamp: *mut c_int) -> f64
1319013270
unsafe { crate::math_abi::lgamma_r(x, signgamp) }
1319113271
}
1319213272
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
13193-
pub unsafe extern "C" fn __gammaf128_r_finite(x: f64, signgamp: *mut c_int) -> f64 {
13194-
unsafe { crate::math_abi::lgamma_r(x, signgamp) }
13273+
pub unsafe extern "C" fn __gammaf128_r_finite(x: f128, signgamp: *mut c_int) -> f128 {
13274+
unsafe { crate::math_abi::lgammaf128_r(x, signgamp) }
1319513275
}
1319613276

1319713277
// __finite classification variants (f128)
1319813278
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
13199-
pub unsafe extern "C" fn __finitef128(x: f64) -> c_int {
13200-
frankenlibc_core::math::finite(x)
13279+
pub unsafe extern "C" fn __finitef128(x: f128) -> c_int {
13280+
(((x.to_bits() >> 112) & 0x7fff) != 0x7fff) as c_int
1320113281
}
1320213282

1320313283
#[cfg(test)]

0 commit comments

Comments
 (0)