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

Commit b801ddd

Browse files
committed
Add scalbnf16 and scalbnf128
The `scalbn` functions are similar enough that they can easily be made generic. Do so and `f16` and `f128` versions.
1 parent bc6b854 commit b801ddd

File tree

17 files changed

+227
-90
lines changed

17 files changed

+227
-90
lines changed

crates/libm-macros/src/shared.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,13 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
134134
None,
135135
&["jn", "yn"],
136136
),
137+
(
138+
// `(f16, i32) -> f16`
139+
FloatTy::F16,
140+
Signature { args: &[Ty::F16, Ty::I32], returns: &[Ty::F16] },
141+
None,
142+
&["scalbnf16", "ldexpf16"],
143+
),
137144
(
138145
// `(f32, i32) -> f32`
139146
FloatTy::F32,
@@ -148,6 +155,13 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
148155
None,
149156
&["scalbn", "ldexp"],
150157
),
158+
(
159+
// `(f128, i32) -> f128`
160+
FloatTy::F128,
161+
Signature { args: &[Ty::F128, Ty::I32], returns: &[Ty::F128] },
162+
None,
163+
&["scalbnf128", "ldexpf128"],
164+
),
151165
(
152166
// `(f32, &mut f32) -> f32` as `(f32) -> (f32, f32)`
153167
FloatTy::F32,

crates/libm-test/benches/random.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,10 @@ 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 |
121+
fabsf16 | fabsf128 |
122+
ldexpf16 | ldexpf128 |
123+
scalbnf16 | scalbnf128 => (false, None),
121124

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

crates/libm-test/src/mpfloat.rs

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,8 @@ libm_macros::for_each_function! {
137137
fmod, fmodf, frexp, frexpf, ilogb, ilogbf, jn, jnf, ldexp, ldexpf,
138138
lgamma_r, lgammaf_r, modf, modff, nextafter, nextafterf, pow,powf,
139139
remquo, remquof, scalbn, scalbnf, sincos, sincosf, yn, ynf,
140-
copysignf16, copysignf128, fabsf16, fabsf128,
140+
copysignf16, copysignf128, fabsf16, fabsf128, ldexpf16, ldexpf128,
141+
scalbnf16, scalbnf128,
141142
],
142143
fn_extra: match MACRO_FN_NAME {
143144
// Remap function names that are different between mpfr and libm
@@ -315,34 +316,6 @@ macro_rules! impl_op_for_ty {
315316
}
316317
}
317318

318-
// `ldexp` and `scalbn` are the same for binary floating point, so just forward all
319-
// methods.
320-
impl MpOp for crate::op::[<ldexp $suffix>]::Routine {
321-
type MpTy = <crate::op::[<scalbn $suffix>]::Routine as MpOp>::MpTy;
322-
323-
fn new_mp() -> Self::MpTy {
324-
<crate::op::[<scalbn $suffix>]::Routine as MpOp>::new_mp()
325-
}
326-
327-
fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet {
328-
<crate::op::[<scalbn $suffix>]::Routine as MpOp>::run(this, input)
329-
}
330-
}
331-
332-
impl MpOp for crate::op::[<scalbn $suffix>]::Routine {
333-
type MpTy = MpFloat;
334-
335-
fn new_mp() -> Self::MpTy {
336-
new_mpfloat::<Self::FTy>()
337-
}
338-
339-
fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet {
340-
this.assign(input.0);
341-
*this <<= input.1;
342-
prep_retval::<Self::FTy>(this, Ordering::Equal)
343-
}
344-
}
345-
346319
impl MpOp for crate::op::[<sincos $suffix>]::Routine {
347320
type MpTy = (MpFloat, MpFloat);
348321

@@ -397,6 +370,34 @@ macro_rules! impl_op_for_ty_all {
397370
prep_retval::<Self::RustRet>(&mut this.0, Ordering::Equal)
398371
}
399372
}
373+
374+
// `ldexp` and `scalbn` are the same for binary floating point, so just forward all
375+
// methods.
376+
impl MpOp for crate::op::[<ldexp $suffix>]::Routine {
377+
type MpTy = <crate::op::[<scalbn $suffix>]::Routine as MpOp>::MpTy;
378+
379+
fn new_mp() -> Self::MpTy {
380+
<crate::op::[<scalbn $suffix>]::Routine as MpOp>::new_mp()
381+
}
382+
383+
fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet {
384+
<crate::op::[<scalbn $suffix>]::Routine as MpOp>::run(this, input)
385+
}
386+
}
387+
388+
impl MpOp for crate::op::[<scalbn $suffix>]::Routine {
389+
type MpTy = MpFloat;
390+
391+
fn new_mp() -> Self::MpTy {
392+
new_mpfloat::<Self::FTy>()
393+
}
394+
395+
fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet {
396+
this.assign(input.0);
397+
*this <<= input.1;
398+
prep_retval::<Self::FTy>(this, Ordering::Equal)
399+
}
400+
}
400401
}
401402
};
402403
}

crates/libm-test/src/precision.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,5 +461,9 @@ fn bessel_prec_dropoff<F: Float>(
461461

462462
impl MaybeOverride<(f32, f32, f32)> for SpecialCase {}
463463
impl MaybeOverride<(f64, f64, f64)> for SpecialCase {}
464+
#[cfg(f16_enabled)]
465+
impl MaybeOverride<(f16, i32)> for SpecialCase {}
464466
impl MaybeOverride<(f32, i32)> for SpecialCase {}
465467
impl MaybeOverride<(f64, i32)> for SpecialCase {}
468+
#[cfg(f128_enabled)]
469+
impl MaybeOverride<(f128, i32)> for SpecialCase {}

crates/libm-test/tests/compare_built_musl.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,10 @@ where
4747
libm_macros::for_each_function! {
4848
callback: musl_rand_tests,
4949
// Musl does not support `f16` and `f128` on all platforms.
50-
skip: [copysignf16, copysignf128, fabsf16, fabsf128],
50+
skip: [
51+
copysignf16, copysignf128, fabsf16, fabsf128,
52+
ldexpf16, ldexpf128, scalbnf16, scalbnf128,
53+
],
5154
attributes: [
5255
#[cfg_attr(x86_no_sse, ignore)] // FIXME(correctness): wrong result on i586
5356
[exp10, exp10f, exp2, exp2f, rint]

crates/libm-test/tests/multiprecision.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,8 @@ libm_macros::for_each_function! {
138138
jnf,
139139
ldexp,
140140
ldexpf,
141+
ldexpf16,
142+
ldexpf128,
141143
nextafter,
142144
nextafterf,
143145
pow,
@@ -148,6 +150,8 @@ libm_macros::for_each_function! {
148150
remquof,
149151
scalbn,
150152
scalbnf,
153+
scalbnf16,
154+
scalbnf128,
151155
yn,
152156
ynf,
153157
],

etc/function-definitions.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,18 @@
458458
],
459459
"type": "f32"
460460
},
461+
"ldexpf128": {
462+
"sources": [
463+
"src/math/ldexpf128.rs"
464+
],
465+
"type": "f128"
466+
},
467+
"ldexpf16": {
468+
"sources": [
469+
"src/math/ldexpf16.rs"
470+
],
471+
"type": "f16"
472+
},
461473
"lgamma": {
462474
"sources": [
463475
"src/libm_helper.rs",
@@ -630,16 +642,32 @@
630642
"scalbn": {
631643
"sources": [
632644
"src/libm_helper.rs",
645+
"src/math/generic/scalbn.rs",
633646
"src/math/scalbn.rs"
634647
],
635648
"type": "f64"
636649
},
637650
"scalbnf": {
638651
"sources": [
652+
"src/math/generic/scalbn.rs",
639653
"src/math/scalbnf.rs"
640654
],
641655
"type": "f32"
642656
},
657+
"scalbnf128": {
658+
"sources": [
659+
"src/math/generic/scalbn.rs",
660+
"src/math/scalbnf128.rs"
661+
],
662+
"type": "f128"
663+
},
664+
"scalbnf16": {
665+
"sources": [
666+
"src/math/generic/scalbn.rs",
667+
"src/math/scalbnf16.rs"
668+
],
669+
"type": "f16"
670+
},
643671
"sin": {
644672
"sources": [
645673
"src/libm_helper.rs",

etc/function-list.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ jn
6767
jnf
6868
ldexp
6969
ldexpf
70+
ldexpf128
71+
ldexpf16
7072
lgamma
7173
lgamma_r
7274
lgammaf
@@ -95,6 +97,8 @@ round
9597
roundf
9698
scalbn
9799
scalbnf
100+
scalbnf128
101+
scalbnf16
98102
sin
99103
sincos
100104
sincosf

src/math/generic/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
mod copysign;
22
mod fabs;
3+
mod scalbn;
34

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

src/math/generic/scalbn.rs

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
use super::super::{CastFrom, CastInto, Float, IntTy, MinInt};
2+
3+
/// Scale the exponent.
4+
///
5+
/// From N3220:
6+
///
7+
/// > The scalbn and scalbln functions compute `x * b^n`, where `b = FLT_RADIX` if the return type
8+
/// > of the function is a standard floating type, or `b = 10` if the return type of the function
9+
/// > is a decimal floating type. A range error occurs for some finite x, depending on n.
10+
/// >
11+
/// > [...]
12+
/// >
13+
/// > * `scalbn(±0, n)` returns `±0`.
14+
/// > * `scalbn(x, 0)` returns `x`.
15+
/// > * `scalbn(±∞, n)` returns `±∞`.
16+
/// >
17+
/// > If the calculation does not overflow or underflow, the returned value is exact and
18+
/// > independent of the current rounding direction mode.
19+
pub fn scalbn<F: Float>(mut x: F, mut n: i32) -> F
20+
where
21+
u32: CastInto<F::Int>,
22+
F::Int: CastFrom<i32>,
23+
F::Int: CastFrom<u32>,
24+
{
25+
if n == 0 || x == F::ZERO || x.is_nan() || x.is_infinite() {
26+
return x;
27+
}
28+
29+
// Bits including the implicit bit
30+
let sig_total_bits = F::SIG_BITS + 1;
31+
32+
// Maximum and minimum values when biased
33+
let exp_max: i32 = F::EXP_BIAS as i32;
34+
let exp_min = -(exp_max - 1);
35+
let exp_min_with_subnorm = -((F::EXP_BIAS + F::SIG_BITS + 1) as i32);
36+
37+
// let x_exp = x.exp();
38+
// let x_sig = x.frac();
39+
40+
if n > exp_max {
41+
return F::INFINITY * x.signum();
42+
}
43+
44+
if n < exp_min_with_subnorm {
45+
return F::ZERO * x.signum();
46+
}
47+
48+
// 2 ^ Emax, where Emax is the maximum biased exponent value (1023 for f64)
49+
let f_exp_max = F::from_bits(F::Int::cast_from(F::EXP_BIAS << 1) << F::SIG_BITS);
50+
// 2 ^ Emin, where Emin is the minimum biased exponent value (-1022 for f64)
51+
let f_exp_min = F::from_bits(IntTy::<F>::ONE << F::SIG_BITS);
52+
// 2 ^ sig_total_bits, representation of what can be accounted for with subnormals
53+
let f_exp_subnorm = F::from_bits((F::EXP_BIAS + sig_total_bits).cast() << F::SIG_BITS);
54+
55+
// std::println!("{exp_max} {exp_min} {n}");
56+
// std::dbg!(x, exp_max, exp_min, n);
57+
58+
if n > exp_max {
59+
x *= f_exp_max;
60+
n -= exp_max;
61+
// std::dbg!(11, x, n);
62+
if n > exp_max {
63+
x *= f_exp_max;
64+
n -= exp_max;
65+
// std::dbg!(12, x, n);
66+
if n > exp_max {
67+
n = exp_max;
68+
// std::dbg!(13, x, n);
69+
}
70+
}
71+
} else if n < exp_min {
72+
let mul = f_exp_min * f_exp_subnorm;
73+
let add = (exp_max - 1) - sig_total_bits as i32;
74+
75+
x *= mul;
76+
n += add;
77+
// std::dbg!(21, x, n);
78+
if n < exp_min {
79+
x *= mul;
80+
n += add;
81+
// std::dbg!(22, x, n);
82+
if n < exp_min {
83+
n = exp_min;
84+
// std::dbg!(23, x, n);
85+
}
86+
}
87+
}
88+
89+
x * F::from_bits(F::Int::cast_from(F::EXP_BIAS as i32 + n) << F::SIG_BITS)
90+
}
91+
92+
// DELETE
93+
94+
extern crate std;
95+
96+
#[test]
97+
fn testme() {
98+
assert_eq!(scalbn::<f16>(f16::from_bits(0x6ecb), -1336428830), f16::ZERO);
99+
}
100+
101+
#[test]
102+
fn testme2() {
103+
// assert_eq!(scalbn(-f64::INFINITY, -2147033648), f64::ZERO);
104+
}

src/math/ldexpf128.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
2+
pub fn ldexpf128(x: f128, n: i32) -> f128 {
3+
super::scalbnf128(x, n)
4+
}

src/math/ldexpf16.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
2+
pub fn ldexpf16(x: f16, n: i32) -> f16 {
3+
super::scalbnf16(x, n)
4+
}

src/math/mod.rs

Lines changed: 9 additions & 1 deletion
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,27 @@ cfg_if! {
343343
if #[cfg(f16_enabled)] {
344344
mod copysignf16;
345345
mod fabsf16;
346+
mod ldexpf16;
347+
mod scalbnf16;
346348

347349
pub use self::copysignf16::copysignf16;
348350
pub use self::fabsf16::fabsf16;
351+
pub use self::ldexpf16::ldexpf16;
352+
pub use self::scalbnf16::scalbnf16;
349353
}
350354
}
351355

352356
cfg_if! {
353357
if #[cfg(f128_enabled)] {
354358
mod copysignf128;
355359
mod fabsf128;
360+
mod ldexpf128;
361+
mod scalbnf128;
356362

357363
pub use self::copysignf128::copysignf128;
358364
pub use self::fabsf128::fabsf128;
365+
pub use self::ldexpf128::ldexpf128;
366+
pub use self::scalbnf128::scalbnf128;
359367
}
360368
}
361369

0 commit comments

Comments
 (0)