roundeven: Use an assembly implementation on i586

Add an assembly implementation for roundeven which also works for
`rint`, similar to the existing `ceil` and `floor` implementations. This
resolves cases where values close to the *.5 boundary would round the
incorrect direction, such as -519629176421.49976 (tested in
`case_list`).
This commit is contained in:
Trevor Gross
2026-03-29 16:44:27 -05:00
parent 312b9946c7
commit 6bbf64e1dd
6 changed files with 54 additions and 10 deletions
@@ -824,6 +824,7 @@
"rint": {
"sources": [
"libm/src/math/arch/aarch64/rounding.rs",
"libm/src/math/arch/i586/rounding.rs",
"libm/src/math/arch/wasm32/rounding.rs",
"libm/src/math/generic/rint.rs",
"libm/src/math/rint.rs"
@@ -297,15 +297,6 @@ fn check_int<I: Int>(input: (f32,), actual: I, expected: I, ctx: &CheckCtx) -> C
impl MaybeOverride<(f64,)> for SpecialCase {
fn check_float<F: Float>(input: (f64,), actual: F, expected: F, ctx: &CheckCtx) -> CheckAction {
if cfg!(x86_no_sse2)
&& (ctx.base_name == BaseName::Rint || ctx.base_name == BaseName::Roundeven)
&& (expected - actual).abs() <= F::ONE
&& (expected - actual).abs() > F::ZERO
{
// Our rounding mode is incorrect.
return XFAIL("i586 rint rounding mode");
}
if ctx.base_name == BaseName::J0 && input.0 < -1e300 {
// Errors get huge close to -inf
return XFAIL_NOCHECK;
@@ -11,4 +11,4 @@
mod rounding;
pub use exp_all::{x87_exp, x87_exp2, x87_exp2f, x87_exp10, x87_exp10f, x87_expf};
pub use rounding::{ceil, floor};
pub use rounding::{ceil, floor, rint};
@@ -51,3 +51,53 @@ pub fn floor(mut x: f64) -> f64 {
}
x
}
/// Note that this respects rounding mode. Because it is UB to have a non-default rounding
/// mode in Rust, this acts as roundeven.
pub fn rint(mut x: f64) -> f64 {
unsafe {
core::arch::asm!(
"fld qword ptr [{x}]",
"frndint",
"fstp qword ptr [{x}]",
x = in(reg) &mut x,
// All the x87 FPU stack is used, all registers must be clobbered
out("st(0)") _, out("st(1)") _,
out("st(2)") _, out("st(3)") _,
out("st(4)") _, out("st(5)") _,
out("st(6)") _, out("st(7)") _,
options(nostack),
);
}
x
}
/* FIXME(msrv): after 1.82, the below can be used to compute control words using `asm_const`:
#[derive(Clone, Copy, Debug, PartialEq)]
enum Precision {
Single,
Double,
Extended,
}
/// See: Intel® 64 and IA-32 Architectures Software Developer's Manual Volume 1:
/// Basic Architecture, section 8.1.5 x87 FPU Control Word.
const fn make_fpcw(round: Round, prec: Precision) -> u16 {
let exceptions = 0b111111; // Disable all 6 exceptions
let misc = 0b1000000; // reserved field usually set by default
let pc = match prec {
Precision::Single => 0b00,
Precision::Double => 0b10,
Precision::Extended => 0b11,
};
let rc = match round {
Round::Nearest => 0b00,
Round::Negative => 0b01,
Round::Positive => 0b10,
Round::Zero => 0b11,
};
(rc << 10) | (pc << 8) | misc | exceptions
}
*/
@@ -48,6 +48,7 @@
pub use i586::{
ceil,
floor,
rint,
x87_exp,
x87_exp10,
x87_exp10f,
@@ -37,6 +37,7 @@ pub fn rint(x: f64) -> f64 {
all(target_arch = "aarch64", target_feature = "neon"),
all(target_arch = "wasm32", intrinsics_enabled),
),
use_arch_required: x86_no_sse2,
args: x,
}