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

Commit 2de4ffb

Browse files
authored
Merge pull request #427 from tgross35/generic-trunc
Add `truncf16` and `truncf128`
2 parents 22dd315 + a5d4d3c commit 2de4ffb

File tree

16 files changed

+120
-56
lines changed

16 files changed

+120
-56
lines changed

libm/crates/compiler-builtins-smoke-test/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,8 @@ no_mangle! {
157157
tgammaf(x: f32) -> f32;
158158
trunc(x: f64) -> f64;
159159
truncf(x: f32) -> f32;
160+
truncf128(x: f128) -> f128;
161+
truncf16(x: f16) -> f16;
160162
y0(x: f64) -> f64;
161163
y0f(x: f32) -> f32;
162164
y1(x: f64) -> f64;

libm/crates/libm-macros/src/shared.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
99
FloatTy::F16,
1010
Signature { args: &[Ty::F16], returns: &[Ty::F16] },
1111
None,
12-
&["fabsf16"],
12+
&["fabsf16", "truncf16"],
1313
),
1414
(
1515
// `fn(f32) -> f32`
@@ -40,7 +40,7 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
4040
FloatTy::F128,
4141
Signature { args: &[Ty::F128], returns: &[Ty::F128] },
4242
None,
43-
&["fabsf128"],
43+
&["fabsf128", "truncf128"],
4444
),
4545
(
4646
// `(f16, f16) -> f16`

libm/crates/libm-test/benches/random.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ libm_macros::for_each_function! {
117117
exp10 | exp10f | exp2 | exp2f => (true, Some(musl_math_sys::MACRO_FN_NAME)),
118118

119119
// Musl does not provide `f16` and `f128` functions
120-
copysignf16 | copysignf128 | fabsf16 | fabsf128 => (false, None),
120+
copysignf16 | copysignf128 | fabsf16 | fabsf128 | truncf16 | truncf128 => (false, None),
121121

122122
// By default we never skip (false) and always have a musl function available
123123
_ => (false, Some(musl_math_sys::MACRO_FN_NAME))

libm/crates/libm-test/src/domain.rs

+10
Original file line numberDiff line numberDiff line change
@@ -199,3 +199,13 @@ impl HasDomain<f16> for crate::op::fabsf16::Routine {
199199
impl HasDomain<f128> for crate::op::fabsf128::Routine {
200200
const DOMAIN: Domain<f128> = Domain::<f128>::UNBOUNDED;
201201
}
202+
203+
#[cfg(f16_enabled)]
204+
impl HasDomain<f16> for crate::op::truncf16::Routine {
205+
const DOMAIN: Domain<f16> = Domain::<f16>::UNBOUNDED;
206+
}
207+
208+
#[cfg(f128_enabled)]
209+
impl HasDomain<f128> for crate::op::truncf128::Routine {
210+
const DOMAIN: Domain<f128> = Domain::<f128>::UNBOUNDED;
211+
}

libm/crates/libm-test/src/mpfloat.rs

+3
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ libm_macros::for_each_function! {
141141
lgamma_r, lgammaf_r, modf, modff, nextafter, nextafterf, pow,powf,
142142
remquo, remquof, scalbn, scalbnf, sincos, sincosf, yn, ynf,
143143
copysignf16, copysignf128, fabsf16, fabsf128,
144+
truncf16, truncf128,
144145
],
145146
fn_extra: match MACRO_FN_NAME {
146147
// Remap function names that are different between mpfr and libm
@@ -202,11 +203,13 @@ impl_no_round! {
202203
#[cfg(f16_enabled)]
203204
impl_no_round! {
204205
fabsf16 => abs_mut;
206+
truncf16 => trunc_mut;
205207
}
206208

207209
#[cfg(f128_enabled)]
208210
impl_no_round! {
209211
fabsf128 => abs_mut;
212+
truncf128 => trunc_mut;
210213
}
211214

212215
/// Some functions are difficult to do in a generic way. Implement them here.

libm/crates/libm-test/tests/compare_built_musl.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ where
4848
libm_macros::for_each_function! {
4949
callback: musl_rand_tests,
5050
// Musl does not support `f16` and `f128` on all platforms.
51-
skip: [copysignf16, copysignf128, fabsf16, fabsf128],
51+
skip: [copysignf16, copysignf128, fabsf16, fabsf128, truncf16, truncf128],
5252
attributes: [
5353
#[cfg_attr(x86_no_sse, ignore)] // FIXME(correctness): wrong result on i586
5454
[exp10, exp10f, exp2, exp2f, rint]
@@ -146,5 +146,7 @@ libm_macros::for_each_function! {
146146
// Not provided by musl
147147
fabsf16,
148148
fabsf128,
149+
truncf16,
150+
truncf128,
149151
],
150152
}

libm/crates/util/src/main.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ fn do_eval(basis: &str, op: &str, inputs: &[&str]) {
8484
emit_types: [CFn, RustFn, RustArgs],
8585
extra: (basis, op, inputs),
8686
fn_extra: match MACRO_FN_NAME {
87-
copysignf16 | copysignf128 | fabsf16 | fabsf128 => None,
87+
copysignf16 | copysignf128 | fabsf16 | fabsf128 | truncf16 | truncf128 => None,
8888
_ => Some(musl_math_sys::MACRO_FN_NAME)
8989
}
9090
}

libm/etc/function-definitions.json

+16
Original file line numberDiff line numberDiff line change
@@ -743,17 +743,33 @@
743743
"sources": [
744744
"src/libm_helper.rs",
745745
"src/math/arch/wasm32.rs",
746+
"src/math/generic/trunc.rs",
746747
"src/math/trunc.rs"
747748
],
748749
"type": "f64"
749750
},
750751
"truncf": {
751752
"sources": [
752753
"src/math/arch/wasm32.rs",
754+
"src/math/generic/trunc.rs",
753755
"src/math/truncf.rs"
754756
],
755757
"type": "f32"
756758
},
759+
"truncf128": {
760+
"sources": [
761+
"src/math/generic/trunc.rs",
762+
"src/math/truncf128.rs"
763+
],
764+
"type": "f128"
765+
},
766+
"truncf16": {
767+
"sources": [
768+
"src/math/generic/trunc.rs",
769+
"src/math/truncf16.rs"
770+
],
771+
"type": "f16"
772+
},
757773
"y0": {
758774
"sources": [
759775
"src/libm_helper.rs",

libm/etc/function-list.txt

+2
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ tgamma
111111
tgammaf
112112
trunc
113113
truncf
114+
truncf128
115+
truncf16
114116
y0
115117
y0f
116118
y1

libm/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;

libm/src/math/generic/trunc.rs

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

libm/src/math/mod.rs

+5-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;
@@ -343,19 +343,23 @@ cfg_if! {
343343
if #[cfg(f16_enabled)] {
344344
mod copysignf16;
345345
mod fabsf16;
346+
mod truncf16;
346347

347348
pub use self::copysignf16::copysignf16;
348349
pub use self::fabsf16::fabsf16;
350+
pub use self::truncf16::truncf16;
349351
}
350352
}
351353

352354
cfg_if! {
353355
if #[cfg(f128_enabled)] {
354356
mod copysignf128;
355357
mod fabsf128;
358+
mod truncf128;
356359

357360
pub use self::copysignf128::copysignf128;
358361
pub use self::fabsf128::fabsf128;
362+
pub use self::truncf128::truncf128;
359363
}
360364
}
361365

libm/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
}

libm/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

libm/src/math/truncf128.rs

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/// Rounds the number toward 0 to the closest integral value (f128).
2+
///
3+
/// This effectively removes the decimal part of the number, leaving the integral part.
4+
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
5+
pub fn truncf128(x: f128) -> f128 {
6+
super::generic::trunc(x)
7+
}

libm/src/math/truncf16.rs

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/// Rounds the number toward 0 to the closest integral value (f16).
2+
///
3+
/// This effectively removes the decimal part of the number, leaving the integral part.
4+
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
5+
pub fn truncf16(x: f16) -> f16 {
6+
super::generic::trunc(x)
7+
}

0 commit comments

Comments
 (0)