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

Add truncf16 and truncf128 #427

Merged
merged 2 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions crates/compiler-builtins-smoke-test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ no_mangle! {
tgammaf(x: f32) -> f32;
trunc(x: f64) -> f64;
truncf(x: f32) -> f32;
truncf128(x: f128) -> f128;
truncf16(x: f16) -> f16;
y0(x: f64) -> f64;
y0f(x: f32) -> f32;
y1(x: f64) -> f64;
Expand Down
4 changes: 2 additions & 2 deletions crates/libm-macros/src/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
FloatTy::F16,
Signature { args: &[Ty::F16], returns: &[Ty::F16] },
None,
&["fabsf16"],
&["fabsf16", "truncf16"],
),
(
// `fn(f32) -> f32`
Expand Down Expand Up @@ -40,7 +40,7 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
FloatTy::F128,
Signature { args: &[Ty::F128], returns: &[Ty::F128] },
None,
&["fabsf128"],
&["fabsf128", "truncf128"],
),
(
// `(f16, f16) -> f16`
Expand Down
2 changes: 1 addition & 1 deletion crates/libm-test/benches/random.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ libm_macros::for_each_function! {
exp10 | exp10f | exp2 | exp2f => (true, Some(musl_math_sys::MACRO_FN_NAME)),

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

// By default we never skip (false) and always have a musl function available
_ => (false, Some(musl_math_sys::MACRO_FN_NAME))
Expand Down
10 changes: 10 additions & 0 deletions crates/libm-test/src/domain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,3 +199,13 @@ impl HasDomain<f16> for crate::op::fabsf16::Routine {
impl HasDomain<f128> for crate::op::fabsf128::Routine {
const DOMAIN: Domain<f128> = Domain::<f128>::UNBOUNDED;
}

#[cfg(f16_enabled)]
impl HasDomain<f16> for crate::op::truncf16::Routine {
const DOMAIN: Domain<f16> = Domain::<f16>::UNBOUNDED;
}

#[cfg(f128_enabled)]
impl HasDomain<f128> for crate::op::truncf128::Routine {
const DOMAIN: Domain<f128> = Domain::<f128>::UNBOUNDED;
}
3 changes: 3 additions & 0 deletions crates/libm-test/src/mpfloat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ libm_macros::for_each_function! {
lgamma_r, lgammaf_r, modf, modff, nextafter, nextafterf, pow,powf,
remquo, remquof, scalbn, scalbnf, sincos, sincosf, yn, ynf,
copysignf16, copysignf128, fabsf16, fabsf128,
truncf16, truncf128,
],
fn_extra: match MACRO_FN_NAME {
// Remap function names that are different between mpfr and libm
Expand Down Expand Up @@ -202,11 +203,13 @@ impl_no_round! {
#[cfg(f16_enabled)]
impl_no_round! {
fabsf16 => abs_mut;
truncf16 => trunc_mut;
}

#[cfg(f128_enabled)]
impl_no_round! {
fabsf128 => abs_mut;
truncf128 => trunc_mut;
}

/// Some functions are difficult to do in a generic way. Implement them here.
Expand Down
4 changes: 3 additions & 1 deletion crates/libm-test/tests/compare_built_musl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ where
libm_macros::for_each_function! {
callback: musl_rand_tests,
// Musl does not support `f16` and `f128` on all platforms.
skip: [copysignf16, copysignf128, fabsf16, fabsf128],
skip: [copysignf16, copysignf128, fabsf16, fabsf128, truncf16, truncf128],
attributes: [
#[cfg_attr(x86_no_sse, ignore)] // FIXME(correctness): wrong result on i586
[exp10, exp10f, exp2, exp2f, rint]
Expand Down Expand Up @@ -146,5 +146,7 @@ libm_macros::for_each_function! {
// Not provided by musl
fabsf16,
fabsf128,
truncf16,
truncf128,
],
}
2 changes: 1 addition & 1 deletion crates/util/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ fn do_eval(basis: &str, op: &str, inputs: &[&str]) {
emit_types: [CFn, RustFn, RustArgs],
extra: (basis, op, inputs),
fn_extra: match MACRO_FN_NAME {
copysignf16 | copysignf128 | fabsf16 | fabsf128 => None,
copysignf16 | copysignf128 | fabsf16 | fabsf128 | truncf16 | truncf128 => None,
_ => Some(musl_math_sys::MACRO_FN_NAME)
}
}
Expand Down
16 changes: 16 additions & 0 deletions etc/function-definitions.json
Original file line number Diff line number Diff line change
Expand Up @@ -743,17 +743,33 @@
"sources": [
"src/libm_helper.rs",
"src/math/arch/wasm32.rs",
"src/math/generic/trunc.rs",
"src/math/trunc.rs"
],
"type": "f64"
},
"truncf": {
"sources": [
"src/math/arch/wasm32.rs",
"src/math/generic/trunc.rs",
"src/math/truncf.rs"
],
"type": "f32"
},
"truncf128": {
"sources": [
"src/math/generic/trunc.rs",
"src/math/truncf128.rs"
],
"type": "f128"
},
"truncf16": {
"sources": [
"src/math/generic/trunc.rs",
"src/math/truncf16.rs"
],
"type": "f16"
},
"y0": {
"sources": [
"src/libm_helper.rs",
Expand Down
2 changes: 2 additions & 0 deletions etc/function-list.txt
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ tgamma
tgammaf
trunc
truncf
truncf128
truncf16
y0
y0f
y1
Expand Down
2 changes: 2 additions & 0 deletions src/math/generic/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
mod copysign;
mod fabs;
mod trunc;

pub use copysign::copysign;
pub use fabs::fabs;
pub use trunc::trunc;
57 changes: 57 additions & 0 deletions src/math/generic/trunc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/* SPDX-License-Identifier: MIT
* origin: musl src/math/trunc.c */

use super::super::{Float, Int, IntTy, MinInt};

pub fn trunc<F: Float>(x: F) -> F {
let mut xi: F::Int = x.to_bits();
let e: i32 = x.exp_unbiased();

// C1: The represented value has no fractional part, so no truncation is needed
if e >= F::SIG_BITS as i32 {
return x;
}

let mask = if e < 0 {
// C2: If the exponent is negative, the result will be zero so we mask out everything
// except the sign.
F::SIGN_MASK
} else {
// C3: Otherwise, we mask out the last `e` bits of the significand.
!(F::SIG_MASK >> e.unsigned())
};

// C4: If the to-be-masked-out portion is already zero, we have an exact result
if (xi & !mask) == IntTy::<F>::ZERO {
return x;
}

// C5: Otherwise the result is inexact and we will truncate. Raise `FE_INEXACT`, mask the
// result, and return.
force_eval!(x + F::MAX);
xi &= mask;
F::from_bits(xi)
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn sanity_check() {
assert_biteq!(trunc(1.1f32), 1.0);
assert_biteq!(trunc(1.1f64), 1.0);

// C1
assert_biteq!(trunc(hf32!("0x1p23")), hf32!("0x1p23"));
assert_biteq!(trunc(hf64!("0x1p52")), hf64!("0x1p52"));
assert_biteq!(trunc(hf32!("-0x1p23")), hf32!("-0x1p23"));
assert_biteq!(trunc(hf64!("-0x1p52")), hf64!("-0x1p52"));

// C2
assert_biteq!(trunc(hf32!("0x1p-1")), 0.0);
assert_biteq!(trunc(hf64!("0x1p-1")), 0.0);
assert_biteq!(trunc(hf32!("-0x1p-1")), -0.0);
assert_biteq!(trunc(hf64!("-0x1p-1")), -0.0);
}
}
6 changes: 5 additions & 1 deletion src/math/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ use self::rem_pio2::rem_pio2;
use self::rem_pio2_large::rem_pio2_large;
use self::rem_pio2f::rem_pio2f;
#[allow(unused_imports)]
use self::support::{CastFrom, CastInto, DInt, Float, HInt, Int, MinInt};
use self::support::{CastFrom, CastInto, DInt, Float, HInt, Int, IntTy, MinInt};

// Public modules
mod acos;
Expand Down Expand Up @@ -343,19 +343,23 @@ cfg_if! {
if #[cfg(f16_enabled)] {
mod copysignf16;
mod fabsf16;
mod truncf16;

pub use self::copysignf16::copysignf16;
pub use self::fabsf16::fabsf16;
pub use self::truncf16::truncf16;
}
}

cfg_if! {
if #[cfg(f128_enabled)] {
mod copysignf128;
mod fabsf128;
mod truncf128;

pub use self::copysignf128::copysignf128;
pub use self::fabsf128::fabsf128;
pub use self::truncf128::truncf128;
}
}

Expand Down
30 changes: 1 addition & 29 deletions src/math/trunc.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use core::f64;

/// Rounds the number toward 0 to the closest integral value (f64).
///
/// This effectively removes the decimal part of the number, leaving the integral part.
Expand All @@ -11,31 +9,5 @@ pub fn trunc(x: f64) -> f64 {
args: x,
}

let x1p120 = f64::from_bits(0x4770000000000000); // 0x1p120f === 2 ^ 120

let mut i: u64 = x.to_bits();
let mut e: i64 = ((i >> 52) & 0x7ff) as i64 - 0x3ff + 12;
let m: u64;

if e >= 52 + 12 {
return x;
}
if e < 12 {
e = 1;
}
m = -1i64 as u64 >> e;
if (i & m) == 0 {
return x;
}
force_eval!(x + x1p120);
i &= !m;
f64::from_bits(i)
}

#[cfg(test)]
mod tests {
#[test]
fn sanity_check() {
assert_eq!(super::trunc(1.1), 1.0);
}
super::generic::trunc(x)
}
22 changes: 1 addition & 21 deletions src/math/truncf.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use core::f32;

/// Rounds the number toward 0 to the closest integral value (f32).
///
/// This effectively removes the decimal part of the number, leaving the integral part.
Expand All @@ -11,25 +9,7 @@ pub fn truncf(x: f32) -> f32 {
args: x,
}

let x1p120 = f32::from_bits(0x7b800000); // 0x1p120f === 2 ^ 120

let mut i: u32 = x.to_bits();
let mut e: i32 = ((i >> 23) & 0xff) as i32 - 0x7f + 9;
let m: u32;

if e >= 23 + 9 {
return x;
}
if e < 9 {
e = 1;
}
m = -1i32 as u32 >> e;
if (i & m) == 0 {
return x;
}
force_eval!(x + x1p120);
i &= !m;
f32::from_bits(i)
super::generic::trunc(x)
}

// PowerPC tests are failing on LLVM 13: https://github.com/rust-lang/rust/issues/88520
Expand Down
7 changes: 7 additions & 0 deletions src/math/truncf128.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/// Rounds the number toward 0 to the closest integral value (f128).
///
/// This effectively removes the decimal part of the number, leaving the integral part.
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
pub fn truncf128(x: f128) -> f128 {
super::generic::trunc(x)
}
7 changes: 7 additions & 0 deletions src/math/truncf16.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/// Rounds the number toward 0 to the closest integral value (f16).
///
/// This effectively removes the decimal part of the number, leaving the integral part.
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
pub fn truncf16(x: f16) -> f16 {
super::generic::trunc(x)
}