mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
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:
@@ -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,
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user