Skip to content
This repository was archived by the owner on Apr 28, 2025. It is now read-only.

Commit a9cfff9

Browse files
committed
Add a generic version of rint
Use this to implement `rint` and `rintf`.
1 parent 5e9ded4 commit a9cfff9

File tree

5 files changed

+78
-94
lines changed

5 files changed

+78
-94
lines changed

etc/function-definitions.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,7 @@
654654
"src/libm_helper.rs",
655655
"src/math/arch/aarch64.rs",
656656
"src/math/arch/wasm32.rs",
657+
"src/math/generic/rint.rs",
657658
"src/math/rint.rs"
658659
],
659660
"type": "f64"
@@ -662,6 +663,7 @@
662663
"sources": [
663664
"src/math/arch/aarch64.rs",
664665
"src/math/arch/wasm32.rs",
666+
"src/math/generic/rint.rs",
665667
"src/math/rintf.rs"
666668
],
667669
"type": "f32"

src/math/generic/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ mod copysign;
33
mod fabs;
44
mod fdim;
55
mod floor;
6+
mod rint;
67
mod sqrt;
78
mod trunc;
89

@@ -11,5 +12,6 @@ pub use copysign::copysign;
1112
pub use fabs::fabs;
1213
pub use fdim::fdim;
1314
pub use floor::floor;
15+
pub use rint::rint;
1416
pub use sqrt::sqrt;
1517
pub use trunc::trunc;

src/math/generic/rint.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/* SPDX-License-Identifier: MIT */
2+
/* origin: musl src/math/rint.c */
3+
4+
use super::super::Float;
5+
6+
pub fn rint<F: Float>(x: F) -> F {
7+
let toint = F::ONE / F::EPSILON;
8+
let e = x.exp();
9+
let positive = x.is_sign_positive();
10+
11+
// On i386 `force_eval!` must be used to force rounding via storage to memory. Otherwise,
12+
// the excess precission from x87 would cause an incorrect final result.
13+
let use_force = cfg!(x86_no_sse) && F::BITS == 32 || F::BITS == 64;
14+
15+
if e >= F::EXP_BIAS + F::SIG_BITS {
16+
// No fractional part; exact result can be returned.
17+
x
18+
} else {
19+
// Apply a net-zero adjustment that nudges `y` in the direction of the rounding mode.
20+
let y = if positive {
21+
let tmp = if use_force { force_eval!(x) } else { x } + toint;
22+
(if use_force { force_eval!(tmp) } else { tmp } - toint)
23+
} else {
24+
let tmp = if use_force { force_eval!(x) } else { x } - toint;
25+
(if use_force { force_eval!(tmp) } else { tmp } + toint)
26+
};
27+
28+
if y == F::ZERO {
29+
// A zero result takes the sign of the input.
30+
if positive { F::ZERO } else { F::NEG_ZERO }
31+
} else {
32+
y
33+
}
34+
}
35+
}
36+
37+
#[cfg(test)]
38+
mod tests {
39+
use super::*;
40+
41+
#[test]
42+
fn zeroes_f32() {
43+
assert_biteq!(rint(0.0_f32), 0.0_f32);
44+
assert_biteq!(rint(-0.0_f32), -0.0_f32);
45+
}
46+
47+
#[test]
48+
fn sanity_check_f32() {
49+
assert_biteq!(rint(-1.0_f32), -1.0);
50+
assert_biteq!(rint(2.8_f32), 3.0);
51+
assert_biteq!(rint(-0.5_f32), -0.0);
52+
assert_biteq!(rint(0.5_f32), 0.0);
53+
assert_biteq!(rint(-1.5_f32), -2.0);
54+
assert_biteq!(rint(1.5_f32), 2.0);
55+
}
56+
57+
#[test]
58+
fn zeroes_f64() {
59+
assert_biteq!(rint(0.0_f64), 0.0_f64);
60+
assert_biteq!(rint(-0.0_f64), -0.0_f64);
61+
}
62+
63+
#[test]
64+
fn sanity_check_f64() {
65+
assert_biteq!(rint(-1.0_f64), -1.0);
66+
assert_biteq!(rint(2.8_f64), 3.0);
67+
assert_biteq!(rint(-0.5_f64), -0.0);
68+
assert_biteq!(rint(0.5_f64), 0.0);
69+
assert_biteq!(rint(-1.5_f64), -2.0);
70+
assert_biteq!(rint(1.5_f64), 2.0);
71+
}
72+
}

src/math/rint.rs

Lines changed: 1 addition & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -9,51 +9,5 @@ pub fn rint(x: f64) -> f64 {
99
args: x,
1010
}
1111

12-
let one_over_e = 1.0 / f64::EPSILON;
13-
let as_u64: u64 = x.to_bits();
14-
let exponent: u64 = (as_u64 >> 52) & 0x7ff;
15-
let is_positive = (as_u64 >> 63) == 0;
16-
if exponent >= 0x3ff + 52 {
17-
x
18-
} else {
19-
let ans = if is_positive {
20-
#[cfg(all(target_arch = "x86", not(target_feature = "sse2")))]
21-
let x = force_eval!(x);
22-
let xplusoneovere = x + one_over_e;
23-
#[cfg(all(target_arch = "x86", not(target_feature = "sse2")))]
24-
let xplusoneovere = force_eval!(xplusoneovere);
25-
xplusoneovere - one_over_e
26-
} else {
27-
#[cfg(all(target_arch = "x86", not(target_feature = "sse2")))]
28-
let x = force_eval!(x);
29-
let xminusoneovere = x - one_over_e;
30-
#[cfg(all(target_arch = "x86", not(target_feature = "sse2")))]
31-
let xminusoneovere = force_eval!(xminusoneovere);
32-
xminusoneovere + one_over_e
33-
};
34-
35-
if ans == 0.0 { if is_positive { 0.0 } else { -0.0 } } else { ans }
36-
}
37-
}
38-
39-
// PowerPC tests are failing on LLVM 13: https://github.com/rust-lang/rust/issues/88520
40-
#[cfg(not(target_arch = "powerpc64"))]
41-
#[cfg(test)]
42-
mod tests {
43-
use super::rint;
44-
45-
#[test]
46-
fn negative_zero() {
47-
assert_eq!(rint(-0.0_f64).to_bits(), (-0.0_f64).to_bits());
48-
}
49-
50-
#[test]
51-
fn sanity_check() {
52-
assert_eq!(rint(-1.0), -1.0);
53-
assert_eq!(rint(2.8), 3.0);
54-
assert_eq!(rint(-0.5), -0.0);
55-
assert_eq!(rint(0.5), 0.0);
56-
assert_eq!(rint(-1.5), -2.0);
57-
assert_eq!(rint(1.5), 2.0);
58-
}
12+
super::generic::rint(x)
5913
}

src/math/rintf.rs

Lines changed: 1 addition & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -9,51 +9,5 @@ pub fn rintf(x: f32) -> f32 {
99
args: x,
1010
}
1111

12-
let one_over_e = 1.0 / f32::EPSILON;
13-
let as_u32: u32 = x.to_bits();
14-
let exponent: u32 = (as_u32 >> 23) & 0xff;
15-
let is_positive = (as_u32 >> 31) == 0;
16-
if exponent >= 0x7f + 23 {
17-
x
18-
} else {
19-
let ans = if is_positive {
20-
#[cfg(all(target_arch = "x86", not(target_feature = "sse2")))]
21-
let x = force_eval!(x);
22-
let xplusoneovere = x + one_over_e;
23-
#[cfg(all(target_arch = "x86", not(target_feature = "sse2")))]
24-
let xplusoneovere = force_eval!(xplusoneovere);
25-
xplusoneovere - one_over_e
26-
} else {
27-
#[cfg(all(target_arch = "x86", not(target_feature = "sse2")))]
28-
let x = force_eval!(x);
29-
let xminusoneovere = x - one_over_e;
30-
#[cfg(all(target_arch = "x86", not(target_feature = "sse2")))]
31-
let xminusoneovere = force_eval!(xminusoneovere);
32-
xminusoneovere + one_over_e
33-
};
34-
35-
if ans == 0.0 { if is_positive { 0.0 } else { -0.0 } } else { ans }
36-
}
37-
}
38-
39-
// PowerPC tests are failing on LLVM 13: https://github.com/rust-lang/rust/issues/88520
40-
#[cfg(not(target_arch = "powerpc64"))]
41-
#[cfg(test)]
42-
mod tests {
43-
use super::rintf;
44-
45-
#[test]
46-
fn negative_zero() {
47-
assert_eq!(rintf(-0.0_f32).to_bits(), (-0.0_f32).to_bits());
48-
}
49-
50-
#[test]
51-
fn sanity_check() {
52-
assert_eq!(rintf(-1.0), -1.0);
53-
assert_eq!(rintf(2.8), 3.0);
54-
assert_eq!(rintf(-0.5), -0.0);
55-
assert_eq!(rintf(0.5), 0.0);
56-
assert_eq!(rintf(-1.5), -2.0);
57-
assert_eq!(rintf(1.5), 2.0);
58-
}
12+
super::generic::rint(x)
5913
}

0 commit comments

Comments
 (0)