Skip to content

Commit 0b59cdc

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 bb75cc3 commit 0b59cdc

17 files changed

+160
-92
lines changed

crates/libm-macros/src/shared.rs

+14
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

+4-1
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

+32-31
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
@@ -255,36 +256,6 @@ macro_rules! impl_op_for_ty {
255256
}
256257
}
257258

258-
// `ldexp` and `scalbn` are the same for binary floating point, so just forward all
259-
// methods.
260-
impl MpOp for crate::op::[<ldexp $suffix>]::Routine {
261-
type MpTy = <crate::op::[<scalbn $suffix>]::Routine as MpOp>::MpTy;
262-
263-
fn new_mp() -> Self::MpTy {
264-
<crate::op::[<scalbn $suffix>]::Routine as MpOp>::new_mp()
265-
}
266-
267-
fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet {
268-
<crate::op::[<scalbn $suffix>]::Routine as MpOp>::run(this, input)
269-
}
270-
}
271-
272-
impl MpOp for crate::op::[<scalbn $suffix>]::Routine {
273-
type MpTy = (MpFloat, MpFloat);
274-
275-
fn new_mp() -> Self::MpTy {
276-
(new_mpfloat::<Self::FTy>(), new_mpfloat::<Self::FTy>())
277-
}
278-
279-
fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet {
280-
this.0.assign(input.0);
281-
this.1.assign(2.0);
282-
this.1.pow_assign(input.1);
283-
let ord = this.0.mul_assign_round(&this.1, Nearest);
284-
prep_retval::<Self::FTy>(&mut this.0, ord)
285-
}
286-
}
287-
288259
impl MpOp for crate::op::[<sincos $suffix>]::Routine {
289260
type MpTy = (MpFloat, MpFloat);
290261

@@ -339,6 +310,36 @@ macro_rules! impl_op_for_ty_all {
339310
prep_retval::<Self::RustRet>(&mut this.0, Ordering::Equal)
340311
}
341312
}
313+
314+
// `ldexp` and `scalbn` are the same for binary floating point, so just forward all
315+
// methods.
316+
impl MpOp for crate::op::[<ldexp $suffix>]::Routine {
317+
type MpTy = <crate::op::[<scalbn $suffix>]::Routine as MpOp>::MpTy;
318+
319+
fn new_mp() -> Self::MpTy {
320+
<crate::op::[<scalbn $suffix>]::Routine as MpOp>::new_mp()
321+
}
322+
323+
fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet {
324+
<crate::op::[<scalbn $suffix>]::Routine as MpOp>::run(this, input)
325+
}
326+
}
327+
328+
impl MpOp for crate::op::[<scalbn $suffix>]::Routine {
329+
type MpTy = (MpFloat, MpFloat);
330+
331+
fn new_mp() -> Self::MpTy {
332+
(new_mpfloat::<Self::FTy>(), new_mpfloat::<Self::FTy>())
333+
}
334+
335+
fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet {
336+
this.0.assign(input.0);
337+
this.1.assign(2.0);
338+
this.1.pow_assign(input.1);
339+
let ord = this.0.mul_assign_round(&this.1, Nearest);
340+
prep_retval::<Self::FTy>(&mut this.0, ord)
341+
}
342+
}
342343
}
343344
};
344345
}

crates/libm-test/src/precision.rs

+4
Original file line numberDiff line numberDiff line change
@@ -403,5 +403,9 @@ fn bessel_prec_dropoff<F: Float>(
403403

404404
impl MaybeOverride<(f32, f32, f32)> for SpecialCase {}
405405
impl MaybeOverride<(f64, f64, f64)> for SpecialCase {}
406+
#[cfg(f16_enabled)]
407+
impl MaybeOverride<(f16, i32)> for SpecialCase {}
406408
impl MaybeOverride<(f32, i32)> for SpecialCase {}
407409
impl MaybeOverride<(f64, i32)> for SpecialCase {}
410+
#[cfg(f128_enabled)]
411+
impl MaybeOverride<(f128, i32)> for SpecialCase {}

crates/libm-test/tests/compare_built_musl.rs

+4-1
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

+4
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,8 @@ libm_macros::for_each_function! {
144144
jnf,
145145
ldexp,
146146
ldexpf,
147+
ldexpf16,
148+
ldexpf128,
147149
nextafter,
148150
nextafterf,
149151
pow,
@@ -154,6 +156,8 @@ libm_macros::for_each_function! {
154156
remquof,
155157
scalbn,
156158
scalbnf,
159+
scalbnf16,
160+
scalbnf128,
157161
yn,
158162
ynf,
159163

etc/function-definitions.json

+16
Original file line numberDiff line numberDiff line change
@@ -630,16 +630,32 @@
630630
"scalbn": {
631631
"sources": [
632632
"src/libm_helper.rs",
633+
"src/math/generic/scalbn.rs",
633634
"src/math/scalbn.rs"
634635
],
635636
"type": "f64"
636637
},
637638
"scalbnf": {
638639
"sources": [
640+
"src/math/generic/scalbn.rs",
639641
"src/math/scalbnf.rs"
640642
],
641643
"type": "f32"
642644
},
645+
"scalbnf128": {
646+
"sources": [
647+
"src/math/generic/scalbn.rs",
648+
"src/math/scalbnf128.rs"
649+
],
650+
"type": "f128"
651+
},
652+
"scalbnf16": {
653+
"sources": [
654+
"src/math/generic/scalbn.rs",
655+
"src/math/scalbnf16.rs"
656+
],
657+
"type": "f16"
658+
},
643659
"sin": {
644660
"sources": [
645661
"src/libm_helper.rs",

etc/function-list.txt

+2
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ round
9595
roundf
9696
scalbn
9797
scalbnf
98+
scalbnf128
99+
scalbnf16
98100
sin
99101
sincos
100102
sincosf

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 scalbn;
34

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

src/math/generic/scalbn.rs

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
use super::super::{CastFrom, CastInto, Float, IntTy, MinInt};
2+
3+
pub fn scalbn<F: Float>(mut x: F, mut n: i32) -> F
4+
where
5+
u32: CastInto<F::Int>,
6+
F::Int: CastFrom<i32>,
7+
F::Int: CastFrom<u32>,
8+
{
9+
// Bits including the implicit bit
10+
let sig_total_bits = F::SIG_BITS + 1;
11+
12+
// Maximum and minimum values when biased
13+
let exp_max: i32 = F::EXP_BIAS as i32;
14+
let exp_min = -(exp_max - 1);
15+
16+
// 2 ^ Emax, where Emax is the maximum biased exponent value (1023 for f64)
17+
let f_exp_max = F::from_bits(F::Int::cast_from(F::EXP_BIAS << 1) << F::SIG_BITS);
18+
// 2 ^ Emin, where Emin is the minimum biased exponent value (-1022 for f64)
19+
let f_exp_min = F::from_bits(IntTy::<F>::ONE << F::SIG_BITS);
20+
// 2 ^ sig_total_bits, representation of what can be accounted for with subnormals
21+
let f_exp_subnorm = F::from_bits((F::EXP_BIAS + sig_total_bits).cast() << F::SIG_BITS);
22+
23+
if n > exp_max {
24+
x *= f_exp_max;
25+
n -= exp_max;
26+
if n > exp_max {
27+
x *= f_exp_max;
28+
n -= exp_max;
29+
if n > exp_max {
30+
n = exp_max;
31+
}
32+
}
33+
} else if n < exp_min {
34+
let mul = f_exp_min * f_exp_subnorm;
35+
let add = (exp_max - 1) - sig_total_bits as i32;
36+
37+
x *= mul;
38+
n += add;
39+
if n < exp_min {
40+
x *= mul;
41+
n += add;
42+
if n < exp_min {
43+
n = exp_min;
44+
}
45+
}
46+
}
47+
48+
x * F::from_bits(F::Int::cast_from(F::EXP_BIAS as i32 + n) << F::SIG_BITS)
49+
}

src/math/ldexpf128.rs

+4
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

+4
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

+9-1
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ use self::rem_pio2::rem_pio2;
114114
use self::rem_pio2_large::rem_pio2_large;
115115
use self::rem_pio2f::rem_pio2f;
116116
#[allow(unused_imports)]
117-
use self::support::{CastFrom, CastInto, DInt, Float, HInt, Int, MinInt};
117+
use self::support::{CastFrom, CastInto, DInt, Float, HInt, Int, IntTy, MinInt};
118118

119119
// Public modules
120120
mod acos;
@@ -336,19 +336,27 @@ cfg_if! {
336336
if #[cfg(f16_enabled)] {
337337
mod copysignf16;
338338
mod fabsf16;
339+
mod ldexpf16;
340+
mod scalbnf16;
339341

340342
pub use self::copysignf16::copysignf16;
341343
pub use self::fabsf16::fabsf16;
344+
pub use self::ldexpf16::ldexpf16;
345+
pub use self::scalbnf16::scalbnf16;
342346
}
343347
}
344348

345349
cfg_if! {
346350
if #[cfg(f128_enabled)] {
347351
mod copysignf128;
348352
mod fabsf128;
353+
mod ldexpf128;
354+
mod scalbnf128;
349355

350356
pub use self::copysignf128::copysignf128;
351357
pub use self::fabsf128::fabsf128;
358+
pub use self::ldexpf128::ldexpf128;
359+
pub use self::scalbnf128::scalbnf128;
352360
}
353361
}
354362

src/math/scalbn.rs

+2-31
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,4 @@
11
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
2-
pub fn scalbn(x: f64, mut n: i32) -> f64 {
3-
let x1p1023 = f64::from_bits(0x7fe0000000000000); // 0x1p1023 === 2 ^ 1023
4-
let x1p53 = f64::from_bits(0x4340000000000000); // 0x1p53 === 2 ^ 53
5-
let x1p_1022 = f64::from_bits(0x0010000000000000); // 0x1p-1022 === 2 ^ (-1022)
6-
7-
let mut y = x;
8-
9-
if n > 1023 {
10-
y *= x1p1023;
11-
n -= 1023;
12-
if n > 1023 {
13-
y *= x1p1023;
14-
n -= 1023;
15-
if n > 1023 {
16-
n = 1023;
17-
}
18-
}
19-
} else if n < -1022 {
20-
/* make sure final n < -53 to avoid double
21-
rounding in the subnormal range */
22-
y *= x1p_1022 * x1p53;
23-
n += 1022 - 53;
24-
if n < -1022 {
25-
y *= x1p_1022 * x1p53;
26-
n += 1022 - 53;
27-
if n < -1022 {
28-
n = -1022;
29-
}
30-
}
31-
}
32-
y * f64::from_bits(((0x3ff + n) as u64) << 52)
2+
pub fn scalbn(x: f64, n: i32) -> f64 {
3+
super::generic::scalbn(x, n)
334
}

src/math/scalbnf.rs

+2-27
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,4 @@
11
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
2-
pub fn scalbnf(mut x: f32, mut n: i32) -> f32 {
3-
let x1p127 = f32::from_bits(0x7f000000); // 0x1p127f === 2 ^ 127
4-
let x1p_126 = f32::from_bits(0x800000); // 0x1p-126f === 2 ^ -126
5-
let x1p24 = f32::from_bits(0x4b800000); // 0x1p24f === 2 ^ 24
6-
7-
if n > 127 {
8-
x *= x1p127;
9-
n -= 127;
10-
if n > 127 {
11-
x *= x1p127;
12-
n -= 127;
13-
if n > 127 {
14-
n = 127;
15-
}
16-
}
17-
} else if n < -126 {
18-
x *= x1p_126 * x1p24;
19-
n += 126 - 24;
20-
if n < -126 {
21-
x *= x1p_126 * x1p24;
22-
n += 126 - 24;
23-
if n < -126 {
24-
n = -126;
25-
}
26-
}
27-
}
28-
x * f32::from_bits(((0x7f + n) as u32) << 23)
2+
pub fn scalbnf(x: f32, n: i32) -> f32 {
3+
super::generic::scalbn(x, n)
294
}

src/math/scalbnf128.rs

+4
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 scalbnf128(x: f128, n: i32) -> f128 {
3+
super::generic::scalbn(x, n)
4+
}

src/math/scalbnf16.rs

+4
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 scalbnf16(x: f16, n: i32) -> f16 {
3+
super::generic::scalbn(x, n)
4+
}

0 commit comments

Comments
 (0)