1
Fork 0

Add ldexp and frexp functions

This commit is contained in:
Brendan Zabarauskas 2013-05-14 11:24:55 +10:00
parent 8d4d2b00c5
commit 44cb46f7bf
4 changed files with 184 additions and 1 deletions

View file

@ -10,6 +10,7 @@
//! Operations and constants for `f32`
use libc::c_int;
use num::{Zero, One, strconv};
use num::{FPCategory, FPNaN, FPInfinite , FPZero, FPSubnormal, FPNormal};
use prelude::*;
@ -672,6 +673,25 @@ impl Float for f32 {
#[inline(always)]
fn max_10_exp() -> int { 38 }
/// Constructs a floating point number by multiplying `x` by 2 raised to the power of `exp`
#[inline(always)]
fn ldexp(x: f32, exp: int) -> f32 {
ldexp(x, exp as c_int)
}
///
/// Breaks the number into a normalized fraction and a base-2 exponent, satisfying:
///
/// - `self = x * pow(2, exp)`
/// - `0.5 <= abs(x) < 1.0`
///
#[inline(always)]
fn frexp(&self) -> (f32, int) {
let mut exp = 0;
let x = frexp(*self, &mut exp);
(x, exp as int)
}
///
/// Returns the exponential of the number, minus `1`, in a way that is accurate
/// even if the number is close to zero
@ -1180,4 +1200,44 @@ mod tests {
assert_eq!(1e-37f32.classify(), FPNormal);
assert_eq!(1e-38f32.classify(), FPSubnormal);
}
#[test]
fn test_ldexp() {
// We have to use from_str until base-2 exponents
// are supported in floating-point literals
let f1: f32 = from_str_hex("1p-123").unwrap();
let f2: f32 = from_str_hex("1p-111").unwrap();
assert_eq!(Float::ldexp(1f32, -123), f1);
assert_eq!(Float::ldexp(1f32, -111), f2);
assert_eq!(Float::ldexp(0f32, -123), 0f32);
assert_eq!(Float::ldexp(-0f32, -123), -0f32);
assert_eq!(Float::ldexp(Float::infinity::<f32>(), -123),
Float::infinity::<f32>());
assert_eq!(Float::ldexp(Float::neg_infinity::<f32>(), -123),
Float::neg_infinity::<f32>());
assert!(Float::ldexp(Float::NaN::<f32>(), -123).is_NaN());
}
#[test]
fn test_frexp() {
// We have to use from_str until base-2 exponents
// are supported in floating-point literals
let f1: f32 = from_str_hex("1p-123").unwrap();
let f2: f32 = from_str_hex("1p-111").unwrap();
let (x1, exp1) = f1.frexp();
let (x2, exp2) = f2.frexp();
assert_eq!((x1, exp1), (0.5f32, -122));
assert_eq!((x2, exp2), (0.5f32, -110));
assert_eq!(Float::ldexp(x1, exp1), f1);
assert_eq!(Float::ldexp(x2, exp2), f2);
assert_eq!(0f32.frexp(), (0f32, 0));
assert_eq!((-0f32).frexp(), (-0f32, 0));
assert_eq!(match Float::infinity::<f32>().frexp() { (x, _) => x },
Float::infinity::<f32>())
assert_eq!(match Float::neg_infinity::<f32>().frexp() { (x, _) => x },
Float::neg_infinity::<f32>())
assert!(match Float::NaN::<f32>().frexp() { (x, _) => x.is_NaN() })
}
}

View file

@ -715,6 +715,25 @@ impl Float for f64 {
#[inline(always)]
fn max_10_exp() -> int { 308 }
/// Constructs a floating point number by multiplying `x` by 2 raised to the power of `exp`
#[inline(always)]
fn ldexp(x: f64, exp: int) -> f64 {
ldexp(x, exp as c_int)
}
///
/// Breaks the number into a normalized fraction and a base-2 exponent, satisfying:
///
/// - `self = x * pow(2, exp)`
/// - `0.5 <= abs(x) < 1.0`
///
#[inline(always)]
fn frexp(&self) -> (f64, int) {
let mut exp = 0;
let x = frexp(*self, &mut exp);
(x, exp as int)
}
///
/// Returns the exponential of the number, minus `1`, in a way that is accurate
/// even if the number is close to zero
@ -1226,4 +1245,44 @@ mod tests {
assert_eq!(1e-307f64.classify(), FPNormal);
assert_eq!(1e-308f64.classify(), FPSubnormal);
}
#[test]
fn test_ldexp() {
// We have to use from_str until base-2 exponents
// are supported in floating-point literals
let f1: f64 = from_str_hex("1p-123").unwrap();
let f2: f64 = from_str_hex("1p-111").unwrap();
assert_eq!(Float::ldexp(1f64, -123), f1);
assert_eq!(Float::ldexp(1f64, -111), f2);
assert_eq!(Float::ldexp(0f64, -123), 0f64);
assert_eq!(Float::ldexp(-0f64, -123), -0f64);
assert_eq!(Float::ldexp(Float::infinity::<f64>(), -123),
Float::infinity::<f64>());
assert_eq!(Float::ldexp(Float::neg_infinity::<f64>(), -123),
Float::neg_infinity::<f64>());
assert!(Float::ldexp(Float::NaN::<f64>(), -123).is_NaN());
}
#[test]
fn test_frexp() {
// We have to use from_str until base-2 exponents
// are supported in floating-point literals
let f1: f64 = from_str_hex("1p-123").unwrap();
let f2: f64 = from_str_hex("1p-111").unwrap();
let (x1, exp1) = f1.frexp();
let (x2, exp2) = f2.frexp();
assert_eq!((x1, exp1), (0.5f64, -122));
assert_eq!((x2, exp2), (0.5f64, -110));
assert_eq!(Float::ldexp(x1, exp1), f1);
assert_eq!(Float::ldexp(x2, exp2), f2);
assert_eq!(0f64.frexp(), (0f64, 0));
assert_eq!((-0f64).frexp(), (-0f64, 0));
assert_eq!(match Float::infinity::<f64>().frexp() { (x, _) => x },
Float::infinity::<f64>())
assert_eq!(match Float::neg_infinity::<f64>().frexp() { (x, _) => x },
Float::neg_infinity::<f64>())
assert!(match Float::NaN::<f64>().frexp() { (x, _) => x.is_NaN() })
}
}

View file

@ -881,6 +881,25 @@ impl Float for float {
#[inline(always)]
fn max_10_exp() -> int { Float::max_10_exp::<f64>() }
/// Constructs a floating point number by multiplying `x` by 2 raised to the power of `exp`
#[inline(always)]
fn ldexp(x: float, exp: int) -> float {
Float::ldexp(x as f64, exp) as float
}
///
/// Breaks the number into a normalized fraction and a base-2 exponent, satisfying:
///
/// - `self = x * pow(2, exp)`
/// - `0.5 <= abs(x) < 1.0`
///
#[inline(always)]
fn frexp(&self) -> (float, int) {
match (*self as f64).frexp() {
(x, exp) => (x as float, exp)
}
}
///
/// Returns the exponential of the number, minus `1`, in a way that is accurate
/// even if the number is close to zero
@ -895,7 +914,9 @@ impl Float for float {
/// than if the operations were performed separately
///
#[inline(always)]
fn ln_1p(&self) -> float { (*self as f64).ln_1p() as float }
fn ln_1p(&self) -> float {
(*self as f64).ln_1p() as float
}
///
/// Fused multiply-add. Computes `(self * a) + b` with only one rounding error. This
@ -1174,6 +1195,46 @@ mod tests {
assert_eq!(1e-308f.classify(), FPSubnormal);
}
#[test]
fn test_ldexp() {
// We have to use from_str until base-2 exponents
// are supported in floating-point literals
let f1: float = from_str_hex("1p-123").unwrap();
let f2: float = from_str_hex("1p-111").unwrap();
assert_eq!(Float::ldexp(1f, -123), f1);
assert_eq!(Float::ldexp(1f, -111), f2);
assert_eq!(Float::ldexp(0f, -123), 0f);
assert_eq!(Float::ldexp(-0f, -123), -0f);
assert_eq!(Float::ldexp(Float::infinity::<float>(), -123),
Float::infinity::<float>());
assert_eq!(Float::ldexp(Float::neg_infinity::<float>(), -123),
Float::neg_infinity::<float>());
assert!(Float::ldexp(Float::NaN::<float>(), -123).is_NaN());
}
#[test]
fn test_frexp() {
// We have to use from_str until base-2 exponents
// are supported in floating-point literals
let f1: float = from_str_hex("1p-123").unwrap();
let f2: float = from_str_hex("1p-111").unwrap();
let (x1, exp1) = f1.frexp();
let (x2, exp2) = f2.frexp();
assert_eq!((x1, exp1), (0.5f, -122));
assert_eq!((x2, exp2), (0.5f, -110));
assert_eq!(Float::ldexp(x1, exp1), f1);
assert_eq!(Float::ldexp(x2, exp2), f2);
assert_eq!(0f.frexp(), (0f, 0));
assert_eq!((-0f).frexp(), (-0f, 0));
assert_eq!(match Float::infinity::<float>().frexp() { (x, _) => x },
Float::infinity::<float>())
assert_eq!(match Float::neg_infinity::<float>().frexp() { (x, _) => x },
Float::neg_infinity::<float>())
assert!(match Float::NaN::<float>().frexp() { (x, _) => x.is_NaN() })
}
#[test]
pub fn test_to_str_exact_do_decimal() {
let s = to_str_exact(5.0, 4u);

View file

@ -284,6 +284,9 @@ pub trait Float: Real
fn min_10_exp() -> int;
fn max_10_exp() -> int;
fn ldexp(x: Self, exp: int) -> Self;
fn frexp(&self) -> (Self, int);
fn exp_m1(&self) -> Self;
fn ln_1p(&self) -> Self;
fn mul_add(&self, a: Self, b: Self) -> Self;