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

Commit 1be4dc4

Browse files
committed
Add a generic version of trunc
The algorithm is identical for both types, so this is a straightforward routine to port.
1 parent 430fe28 commit 1be4dc4

File tree

5 files changed

+59
-51
lines changed

5 files changed

+59
-51
lines changed

src/math/generic/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
mod copysign;
22
mod fabs;
3+
mod trunc;
34

45
pub use copysign::copysign;
56
pub use fabs::fabs;
7+
pub use trunc::trunc;

src/math/generic/trunc.rs

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
use super::super::{Float, Int, IntTy, MinInt};
2+
3+
pub fn trunc<F: Float>(x: F) -> F {
4+
let mut xi: F::Int = x.to_bits();
5+
let e: i32 = x.exp_unbiased();
6+
7+
// C1: The represented value has no fractional part, so no truncation is needed
8+
if e >= F::SIG_BITS as i32 {
9+
return x;
10+
}
11+
12+
let mask = if e < 0 {
13+
// C2: If the exponent is negative, the result will be zero so we mask out everything
14+
// except the sign.
15+
F::SIGN_MASK
16+
} else {
17+
// C3: Otherwise, we mask out the last `e` bits of the significand.
18+
!(F::SIG_MASK >> e.unsigned())
19+
};
20+
21+
// C4: If the to-be-masked-out portion is already zero, we have an exact result
22+
if (xi & !mask) == IntTy::<F>::ZERO {
23+
return x;
24+
}
25+
26+
// C5: Otherwise the result is inexact and we will truncate. Raise `FE_INEXACT`, mask the
27+
// result, and return.
28+
force_eval!(x + F::MAX);
29+
xi &= mask;
30+
F::from_bits(xi)
31+
}
32+
33+
#[cfg(test)]
34+
mod tests {
35+
use super::*;
36+
37+
#[test]
38+
fn sanity_check() {
39+
assert_biteq!(trunc(1.1f32), 1.0);
40+
assert_biteq!(trunc(1.1f64), 1.0);
41+
42+
// C1
43+
assert_biteq!(trunc(hf32!("0x1p23")), hf32!("0x1p23"));
44+
assert_biteq!(trunc(hf64!("0x1p52")), hf64!("0x1p52"));
45+
assert_biteq!(trunc(hf32!("-0x1p23")), hf32!("-0x1p23"));
46+
assert_biteq!(trunc(hf64!("-0x1p52")), hf64!("-0x1p52"));
47+
48+
// C2
49+
assert_biteq!(trunc(hf32!("0x1p-1")), 0.0);
50+
assert_biteq!(trunc(hf64!("0x1p-1")), 0.0);
51+
assert_biteq!(trunc(hf32!("-0x1p-1")), -0.0);
52+
assert_biteq!(trunc(hf64!("-0x1p-1")), -0.0);
53+
}
54+
}

src/math/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ use self::rem_pio2::rem_pio2;
121121
use self::rem_pio2_large::rem_pio2_large;
122122
use self::rem_pio2f::rem_pio2f;
123123
#[allow(unused_imports)]
124-
use self::support::{CastFrom, CastInto, DInt, Float, HInt, Int, MinInt};
124+
use self::support::{CastFrom, CastInto, DInt, Float, HInt, Int, IntTy, MinInt};
125125

126126
// Public modules
127127
mod acos;

src/math/trunc.rs

+1-29
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
use core::f64;
2-
31
/// Rounds the number toward 0 to the closest integral value (f64).
42
///
53
/// This effectively removes the decimal part of the number, leaving the integral part.
@@ -11,31 +9,5 @@ pub fn trunc(x: f64) -> f64 {
119
args: x,
1210
}
1311

14-
let x1p120 = f64::from_bits(0x4770000000000000); // 0x1p120f === 2 ^ 120
15-
16-
let mut i: u64 = x.to_bits();
17-
let mut e: i64 = ((i >> 52) & 0x7ff) as i64 - 0x3ff + 12;
18-
let m: u64;
19-
20-
if e >= 52 + 12 {
21-
return x;
22-
}
23-
if e < 12 {
24-
e = 1;
25-
}
26-
m = -1i64 as u64 >> e;
27-
if (i & m) == 0 {
28-
return x;
29-
}
30-
force_eval!(x + x1p120);
31-
i &= !m;
32-
f64::from_bits(i)
33-
}
34-
35-
#[cfg(test)]
36-
mod tests {
37-
#[test]
38-
fn sanity_check() {
39-
assert_eq!(super::trunc(1.1), 1.0);
40-
}
12+
super::generic::trunc(x)
4113
}

src/math/truncf.rs

+1-21
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
use core::f32;
2-
31
/// Rounds the number toward 0 to the closest integral value (f32).
42
///
53
/// This effectively removes the decimal part of the number, leaving the integral part.
@@ -11,25 +9,7 @@ pub fn truncf(x: f32) -> f32 {
119
args: x,
1210
}
1311

14-
let x1p120 = f32::from_bits(0x7b800000); // 0x1p120f === 2 ^ 120
15-
16-
let mut i: u32 = x.to_bits();
17-
let mut e: i32 = ((i >> 23) & 0xff) as i32 - 0x7f + 9;
18-
let m: u32;
19-
20-
if e >= 23 + 9 {
21-
return x;
22-
}
23-
if e < 9 {
24-
e = 1;
25-
}
26-
m = -1i32 as u32 >> e;
27-
if (i & m) == 0 {
28-
return x;
29-
}
30-
force_eval!(x + x1p120);
31-
i &= !m;
32-
f32::from_bits(i)
12+
super::generic::trunc(x)
3313
}
3414

3515
// PowerPC tests are failing on LLVM 13: https://github.com/rust-lang/rust/issues/88520

0 commit comments

Comments
 (0)