diff --git a/.travis.yml b/.travis.yml index 6088be4..91d9d10 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,11 +40,6 @@ before_cache: # Travis can't cache files that are not readable by "others" - chmod -R a+r $HOME/.cargo -branches: - only: - - auto - - try - notifications: email: on_success: never diff --git a/Cargo.toml b/Cargo.toml index 405ead3..1659efd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ keywords = ["float", "math", "libm", "no_std"] license = "BSD-2-Clause AND ISC AND MIT" name = "m" repository = "https://github.com/japaric/m" -version = "0.1.1" +version = "0.1.2" [dev-dependencies] -quickcheck = "0.3.1" +quickcheck = "0.6.0" diff --git a/src/lib.rs b/src/lib.rs index ffd804f..ab244b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,7 +32,12 @@ //! - `atanf` //! - `fabs` //! - `fabsf` - +//! - `sqrt` +//! - `cosf` +//! - `sinf` +//! - `floor` +//! - `scalbn` +//! - `copysign` #![allow(unknown_lints)] #![cfg_attr(not(test), no_std)] #![deny(warnings)] @@ -40,8 +45,8 @@ #[cfg(test)] extern crate core; + #[cfg(test)] -#[macro_use] extern crate quickcheck; use core::mem; @@ -93,6 +98,17 @@ pub trait Float { /// /// Returns `NaN` if `self` is a negative number fn sqrt(self) -> Self; + + + /// Returns cos of a number + fn cos(self) -> Self; + + /// Returns sin of a number + fn sin(self) -> Self; + + fn floor(self) -> Self; + fn scalbn(self, other: i32) -> Self; + fn copysign(self, other: Self) -> Self; } macro_rules! float { @@ -100,7 +116,12 @@ macro_rules! float { atan = $atan:ident, atan2 = $atan2:ident, fabs = $fabs:ident, - sqrt = $sqrt:ident) => { + sqrt = $sqrt:ident, + cos = $cos:ident, + sin = $sin:ident, + floor = $floor:ident, + scalbn = $scalbn:ident, + copysign = $copysign:ident) => { impl Float for $ty { fn abs(self) -> Self { ll::$fabs(self) @@ -127,17 +148,55 @@ macro_rules! float { fn sqrt(self) -> Self { ll::$sqrt(self) } + + fn cos(self) -> Self { + ll::$cos(self) + } + + fn sin(self) -> Self { + ll::$sin(self) + } + + fn floor(self) -> Self { + ll::$floor(self) + } + + fn scalbn(self, other: i32) -> Self { + ll::$scalbn(self, other) + } + + fn copysign(self, other: Self) -> Self { + ll::$copysign(self, other) + } } } } -float!(f32, - atan = atanf, - atan2 = atan2f, - fabs = fabsf, - sqrt = sqrtf); -float!(f64, atan = atan, atan2 = atan2, fabs = fabs, sqrt = sqrt); +float!( + f32, + atan = atanf, + atan2 = atan2f, + fabs = fabsf, + sqrt = sqrtf, + cos = cosf, + sin = sinf, + floor = floorf, + scalbn = scalbnf, + copysign = copysignf +); +float!( + f64, + atan = atan, + atan2 = atan2, + fabs = fabs, + sqrt = sqrt, + cos = cos, + sin = sin, + floor = floor, + scalbn = scalbn, + copysign = copysign +); trait FloatExt { type Int; @@ -149,10 +208,7 @@ trait FloatExt { fn exponent_bias() -> u32; fn exponent_bits() -> u32; fn exponent_mask() -> Self::Int; - fn from_parts(sign: Sign, - exponent: Self::Int, - significand: Self::Int) - -> Self; + fn from_parts(sign: Sign, exponent: Self::Int, significand: Self::Int) -> Self; fn from_repr(Self::Int) -> Self; fn repr(self) -> Self::Int; fn sign(self) -> Sign; @@ -175,13 +231,15 @@ macro_rules! float_ext { #[cfg(test)] fn eq_repr(self, rhs: Self) -> bool { + const TOLERANCE_ULP: $repr_ty = 100; + if self.is_nan() && rhs.is_nan() { true } else { let (lhs, rhs) = (self.repr(), rhs.repr()); - lhs == rhs || (lhs > rhs && lhs - rhs == 1) || - (rhs > lhs && rhs - lhs == 1) + lhs == rhs || (lhs > rhs && lhs - rhs <= TOLERANCE_ULP) || + (rhs > lhs && rhs - lhs <= TOLERANCE_ULP) } } @@ -243,10 +301,12 @@ macro_rules! float_ext { } float_ext!(f32, repr_ty = u32, exponent_bits = 8, significand_bits = 23); -float_ext!(f64, - repr_ty = u64, - exponent_bits = 11, - significand_bits = 52); +float_ext!( + f64, + repr_ty = u64, + exponent_bits = 11, + significand_bits = 52 +); #[derive(Eq, PartialEq)] enum Sign { @@ -257,7 +317,11 @@ enum Sign { impl Sign { #[cfg(test)] fn from_bool(x: bool) -> Self { - if x { Sign::Negative } else { Sign::Positive } + if x { + Sign::Negative + } else { + Sign::Positive + } } fn u32(self) -> u32 { diff --git a/src/ll.rs b/src/ll.rs index fc333a6..1009d10 100644 --- a/src/ll.rs +++ b/src/ll.rs @@ -21,27 +21,33 @@ pub extern "C" fn atan(_: f64) -> f64 { // [19/16, 39/16] atan(x) = atan(3/2) + atan((t - 1.5) / (1 + 1.5*t)) // [39/16, INF] atan(x) = atan(INF) + atan(-1/t) pub extern "C" fn atanf(mut x: f32) -> f32 { - const A: [f32; 5] = [3.3333328366e-01, - -1.9999158382e-01, - 1.4253635705e-01, - -1.0648017377e-01, - 6.1687607318e-02]; + const A: [f32; 5] = [ + 3.3333328366e-01, + -1.9999158382e-01, + 1.4253635705e-01, + -1.0648017377e-01, + 6.1687607318e-02, + ]; const HUGE: f32 = 1e30; // `[atan(0.5), atan(1.0), atan(1.5), atan(inf)]` let atanhi: [f32; 4] = unsafe { - [mem::transmute(0x3eed6338), - mem::transmute(0x3f490fda), - mem::transmute(0x3f7b985e), - mem::transmute(0x3fc90fda)] + [ + mem::transmute(0x3eed6338), + mem::transmute(0x3f490fda), + mem::transmute(0x3f7b985e), + mem::transmute(0x3fc90fda), + ] }; // `[atan(0.5), atan(1.0), atan(1.5), atan(inf)]` let atanlo: [f32; 4] = unsafe { - [mem::transmute(0x31ac3769), - mem::transmute(0x33222168), - mem::transmute(0x33140fb4), - mem::transmute(0x33a22168)] + [ + mem::transmute(0x31ac3769), + mem::transmute(0x33222168), + mem::transmute(0x33140fb4), + mem::transmute(0x33a22168), + ] }; let sx = x.sign(); @@ -91,7 +97,6 @@ pub extern "C" fn atanf(mut x: f32) -> f32 { id = 3; x = -1. / x; } - } let z = x * x; @@ -106,7 +111,11 @@ pub extern "C" fn atanf(mut x: f32) -> f32 { let id = id as usize; let z = atanhi[id] - ((x * (s1 + s2) - atanlo[id]) - x); - if let Sign::Negative = sx { -z } else { z } + if let Sign::Negative = sx { + -z + } else { + z + } } } } @@ -119,6 +128,7 @@ macro_rules! fabs { } } + // atan2!(atan2, f64, 26, atan = atan, fabs = fabs); // atan2!(atan2f, f32, 60, atan = atanf, fabs = fabsf); fabs!(fabs, f64); @@ -229,7 +239,6 @@ pub extern "C" fn atan2f(y: f32, x: f32) -> f32 { _ => (z - PI_LO) - PI, } } - } } @@ -253,7 +262,6 @@ pub extern "C" fn sqrtf(x: f32) -> f32 { } else if x < 0. { (x - x) / (x - x) } else { - // normalize let mut m = ix >> 23; @@ -325,52 +333,915 @@ pub extern "C" fn sqrtf(x: f32) -> f32 { } } -#[cfg(test)] -check! { - // `atan` has not been implemented yet - // fn atan2(f: extern fn(f64, f64) -> f64, y: F64, x: F64) -> Option { - // Some(F64(f(y.0, x.0))) - // } +pub extern "C" fn sin(_: f64) -> f64 { + unimplemented!() +} + +pub extern "C" fn sinf(x: f32) -> f32 { + use core::f64::consts::FRAC_PI_2; + + const S1PIO2: f64 = 1f64 * FRAC_PI_2; /* 0x3FF921FB, 0x54442D18 */ + const S2PIO2: f64 = 2f64 * FRAC_PI_2; /* 0x400921FB, 0x54442D18 */ + const S3PIO2: f64 = 3f64 * FRAC_PI_2; /* 0x4012D97C, 0x7F3321D2 */ + const S4PIO2: f64 = 4f64 * FRAC_PI_2; /* 0x401921FB, 0x54442D18 */ - fn atan2f(f: extern fn(f32, f32) -> f32, y: F32, x: F32) -> Option { - Some(F32(f(y.0, x.0))) + let hx = x.repr() as i32; + + let ix = hx & 0x7fffffff; + + if ix <= 0x3f490fda { + /* |x| ~<= pi/4 */ + if ix < 0x39800000 { + /* |x| < 2**-12 */ + if (x as i32) == 0 { + return x; /* x with inexact if x != 0 */ + } + } + return sindf(x as f64); + } + if ix <= 0x407b53d1 { + /* |x| ~<= 5*pi/4 */ + if ix <= 0x4016cbe3 { + /* |x| ~<= 3pi/4 */ + if hx > 0 { + return cosdf(x as f64 - S1PIO2); + } else { + return -cosdf(x as f64 + S1PIO2); + } + } else { + return sindf((if hx > 0 { S2PIO2 } else { -S2PIO2 }) - x as f64); + } } + if ix <= 0x40e231d5 { + /* |x| ~<= 9*pi/4 */ + if ix <= 0x40afeddf { + /* |x| ~<= 7*pi/4 */ + if hx > 0 { + return -cosdf(x as f64 - S3PIO2); + } else { + return cosdf(x as f64 + S3PIO2); + } + } else { + return sindf(x as f64 + (if hx > 0 { -S4PIO2 } else { S4PIO2 })); + } + } + /* sin(Inf or NaN) is NaN */ else if ix >= 0x7f800000 { + return x - x; + - fn atanf(f: extern fn(f32) -> f32, x: F32) -> Option { - Some(F32(f(x.0))) + /* general argument reduction needed */ + } else { + let (n, y) = ieee754_rem_pio2f(x); + match n & 3 { + 0 => return sindf(y), + 1 => return cosdf(-y), + 2 => return sindf(-y), + _ => return -cosdf(y), + } } +} + +pub extern "C" fn cos(_: f64) -> f64 { + unimplemented!() +} - // unimplemented! - // fn atan(f: extern fn(f64) -> f64, x: F64) -> Option { - // Some(F64(f(x.0))) - // } +pub extern "C" fn cosf(x: f32) -> f32 { + use core::f64::consts::FRAC_PI_2; - fn fabs(f: extern fn(f64) -> f64, x: F64) -> Option { - Some(F64(f(x.0))) + const C1PIO2: f64 = 1f64 * FRAC_PI_2; /* 0x3FF921FB, 0x54442D18 */ + const C2PIO2: f64 = 2f64 * FRAC_PI_2; /* 0x400921FB, 0x54442D18 */ + const C3PIO2: f64 = 3f64 * FRAC_PI_2; /* 0x4012D97C, 0x7F3321D2 */ + const C4PIO2: f64 = 4f64 * FRAC_PI_2; /* 0x401921FB, 0x54442D18 */ + + let hx = x.repr() as i32; + let ix = hx & 0x7fffffff; + + if ix <= 0x3f490fda { + /* |x| ~<= pi/4 */ + if ix < 0x39800000 { + /* |x| < 2**-12 */ + if x as i32 == 0 { + return 1.0; + } /* 1 with inexact if x != 0 */ + } + return cosdf(x as f64); } + if ix <= 0x407b53d1 { + /* |x| ~<= 5*pi/4 */ + if ix <= 0x4016cbe3 { + /* |x| ~> 3*pi/4 */ + if hx > 0 { + return sindf(C1PIO2 - x as f64); + } else { + return sindf(x as f64 + C1PIO2); + } + } else { + return -cosdf(x as f64 + (if hx > 0 { -C2PIO2 } else { C2PIO2 })); + } + } + if ix <= 0x40e231d5 { + /* |x| ~<= 9*pi/4 */ + if ix <= 0x40afeddf { + /* |x| ~> 7*pi/4 */ + if hx > 0 { + return sindf(x as f64 - C3PIO2); + } else { + return sindf(-C3PIO2 - x as f64); + } + } else { + return cosdf(x as f64 + (if hx > 0 { -C4PIO2 } else { C4PIO2 })); + } + } + /* cos(Inf or NaN) is NaN */ else if ix >= 0x7f800000 { + return x - x; - fn fabsf(f: extern fn(f32) -> f32, x: F32) -> Option { - Some(F32(f(x.0))) + /* general argument reduction needed */ + } else { + let (n, y) = ieee754_rem_pio2f(x); + match n & 3 { + 0 => return cosdf(y), + 1 => return sindf(-y), + 2 => return -cosdf(y), + _ => return sindf(y), + } } +} + +fn cosdf(x: f64) -> f32 { + const ONE: f64 = 1.0; + const C0: f64 = -0.499999997251031003120; + const C1: f64 = 0.0416666233237390631894; + const C2: f64 = -0.00138867637746099294692; + const C3: f64 = 0.0000243904487962774090654; + + let r: f64; + let w: f64; + let z: f64; + + /* Try to optimize for parallel evaluation as in k_tanf.c. */ + z = x * x; + w = z * z; + r = C2 + z * C3; + (((ONE + z * C0) + w * C1) + (w * z) * r) as f32 +} + +fn sindf(x: f64) -> f32 { + const S1: f64 = -0.166666666416265235595; + const S2: f64 = 0.0083333293858894631756; + const S3: f64 = -0.000198393348360966317347; + const S4: f64 = 0.0000027183114939898219064; - // unimplemented! - // fn sqrt(f: extern fn(f64) -> f64, x: F64) -> Option { - // Some(F64(f(x.0))) - // } + let r: f64; + let s: f64; + let w: f64; + let z: f64; - fn sqrtf(f: extern fn(f32) -> f32, x: F32) -> Option { - match () { - #[cfg(all(target_env = "gnu", target_os = "windows"))] - () => { - if x.0.repr() == 0x8000_0000 { - None + z = x * x; + w = z * z; + r = S3 + z * S4; + s = z * x; + ((x + s * (S1 + z * S2)) + s * w * r) as f32 +} + +fn ieee754_rem_pio2f(x: f32) -> (i32, f64) { + const HEX18P52: f64 = 6755399441055744.0; + const INVPIO2: f64 = 6.36619772367581382433e-01; /* 0x3FE45F30, 0x6DC9C883 */ + const PIO2_1: f64 = 1.57079631090164184570e+00; /* 0x3FF921FB, 0x50000000 */ + const PIO2_1T: f64 = 1.58932547735281966916e-08; /* 0x3E5110b4, 0x611A6263 */ + + let y: f64; + let w: f64; + let r: f64; + let mut func: f64; + let z: f32; + let e0: i32; + + let hx = x.repr() as i32; + let ix = hx & 0x7fffffff; + /* 33+53 bit pi is good enough for medium size */ + if ix < 0x4dc90fdb { + /* |x| ~< 2^28*(pi/2), medium size */ + /* Use a specialized rint() to get func. Assume round-to-nearest. */ + func = x as f64 * INVPIO2 + HEX18P52; + func = func - HEX18P52; + let n = func as i32; + r = x as f64 - func * PIO2_1; + w = func * PIO2_1T; + y = r - w; + return (n, y); + } + + /* + * all other (large) arguments + */ + if ix >= 0x7f800000 { + /* x is inf or NaN */ + y = x as f64 - x as f64; + return (0, y); + } + /* set z = scalbn(|x|,ilogb(|x|)-23) */ + e0 = (ix >> 23) - 150; /* e0 = ilogb(|x|)-23; */ + z = f32::from_repr((ix - (e0 << 23)) as u32); + let (n, ty) = kernel_rem_pio2([z as f64], e0, 1, 0); + if hx < 0 { + y = -ty[0]; + return (-n, y); + } + y = ty[0]; + (n, y) +} + +fn kernel_rem_pio2(x: [f64; 1], e0: i32, nx: i32, prec: i32) -> (i32, [f64; 3]) { + const IPIO2: [i32; 66] = [ + 0xA2F983, + 0x6E4E44, + 0x1529FC, + 0x2757D1, + 0xF534DD, + 0xC0DB62, + 0x95993C, + 0x439041, + 0xFE5163, + 0xABDEBB, + 0xC561B7, + 0x246E3A, + 0x424DD2, + 0xE00649, + 0x2EEA09, + 0xD1921C, + 0xFE1DEB, + 0x1CB129, + 0xA73EE8, + 0x8235F5, + 0x2EBB44, + 0x84E99C, + 0x7026B4, + 0x5F7E41, + 0x3991D6, + 0x398353, + 0x39F49C, + 0x845F8B, + 0xBDF928, + 0x3B1FF8, + 0x97FFDE, + 0x05980F, + 0xEF2F11, + 0x8B5A0A, + 0x6D1F6D, + 0x367ECF, + 0x27CB09, + 0xB74F46, + 0x3F669E, + 0x5FEA2D, + 0x7527BA, + 0xC7EBE5, + 0xF17B3D, + 0x0739F7, + 0x8A5292, + 0xEA6BFB, + 0x5FB11F, + 0x8D5D08, + 0x560330, + 0x46FC7B, + 0x6BABF0, + 0xCFBC20, + 0x9AF436, + 0x1DA9E3, + 0x91615E, + 0xE61B08, + 0x659985, + 0x5F14A0, + 0x68408D, + 0xFFD880, + 0x4D7327, + 0x310606, + 0x1556CA, + 0x73A8C9, + 0x60E27B, + 0xC08C6B, + ]; + const PIO2: [f64; 8] = [ + 1.57079625129699707031e+00, /* 0x3FF921FB, 0x40000000 */ + 7.54978941586159635335e-08, /* 0x3E74442D, 0x00000000 */ + 5.39030252995776476554e-15, /* 0x3CF84698, 0x80000000 */ + 3.28200341580791294123e-22, /* 0x3B78CC51, 0x60000000 */ + 1.27065575308067607349e-29, /* 0x39F01B83, 0x80000000 */ + 1.22933308981111328932e-36, /* 0x387A2520, 0x40000000 */ + 2.73370053816464559624e-44, /* 0x36E38222, 0x80000000 */ + 2.16741683877804819444e-51, /* 0x3569F31D, 0x00000000 */ + ]; + const TWO24: f64 = 1.67772160000000000000e+07; + const TWON24: f64 = 5.96046447753906250000e-08; /* 0x3E700000, 0x00000000 */ + const ZERO: f64 = 0.0; + const ONE: f64 = 1.0; + const JK_INIT: [i32; 4] = [3, 4, 4, 6]; + let mut iq: [i32; 20] = [0; 20]; + let mut f: [f64; 20] = [0.0; 20]; + let mut fq: [f64; 20] = [0.0; 20]; + let mut q: [f64; 20] = [0.0; 20]; + let mut y: [f64; 3] = [0.0; 3]; + let mut k: i32; + let mut z: f64 = 0.0; + let mut ih: i32 = 0; + let mut n: i32 = 0; + let mut carry: i32; + + /* initialize jk*/ + let jk = JK_INIT[prec as usize]; + let jp = JK_INIT[prec as usize]; + + /* determine jx,jv,q0, note that 3>q0 */ + let jx: i32 = nx - 1; + let mut jv: i32 = (e0 - 3) / 24; + if jv < 0 { + jv = 0; + } + let mut q0: i32 = e0 - 24 * (jv + 1); + + /* set up f[0] to f[jx+jk] where f[jx+jk] = ipio2[jv+jk] */ + let mut j = jv - jx; + let m = jx + jk; + let mut i: i32 = 0; + while i <= m { + f[i as usize] = if j < 0 { + ZERO + } else { + IPIO2[j as usize] as f64 + }; + i += 1; + j += 1; + } + + i = 0; + let mut fw: f64; + /* compute q[0],q[1],...q[jk] */ + while i <= jk { + j = 0; + fw = 0.0; + while j <= jx { + fw += x[j as usize] * f[(jx + i - j) as usize]; + j += 1; + } + q[i as usize] = fw; + i += 1; + } + + let mut jz = jk; + let mut _done: bool = false; + 'recompute: while !_done { + _done = true; + /* distill q[] into iq[] reversingly */ + //let mut i: usize = 0; + i = 0; + j = jz; + z = q[jz as usize]; + while j > 0 { + fw = ((TWON24 * z) as i32) as f64; + iq[i as usize] = (z - TWO24 * fw) as i32; + z = q[(j - 1) as usize] + fw; + i += 1; + j -= 1; + } + + /* compute n */ + z = scalbn(z, q0); /* actual value of z */ + z -= 8.0 * floor(z * 0.125_f64); /* trim off integer >= 8 */ + n = z as i32; + z -= n as f64; + ih = 0; + if q0 > 0 { + /* need iq[jz-1] to determine n */ + i = (iq[jz as usize - 1] >> (24 - q0)) as i32; + n += i; + iq[jz as usize - 1] -= (i << (24 - q0)) as i32; + ih = iq[jz as usize - 1] >> (23 - q0); + } else if q0 == 0 { + ih = iq[jz as usize - 1] >> 23; + } else if z >= 0.5 { + ih = 2; + } + + if ih > 0 { + /* q > 0.5 */ + n += 1; + carry = 0; + i = 0; + while i < jz { + j = iq[i as usize]; + if carry == 0 { + if j != 0 { + carry = 1; + iq[i as usize] = 0x1000000 - j; + } } else { - Some(F32(f(x.0))) + iq[i as usize] = 0xffffff - j; } - }, - #[cfg(not(all(target_env = "gnu", target_os = "windows")))] - () => Some(F32(f(x.0))), + i += 1; + } + + if q0 > 0 { + /* rare case: chance is 1 in 12 */ + match q0 { + 1 => iq[jz as usize - 1] &= 0x7fffff, + 2 => iq[jz as usize - 1] &= 0x3fffff, + _ => iq[jz as usize - 1] &= 0x3fffff, + } + } + if ih == 2 { + z = ONE - z; + if carry != 0 { + z -= scalbn(ONE, q0); + } + } + } + + /* check if recomputation is needed */ + if z == ZERO { + j = 0; + i = jz - 1; + while i >= jk { + j |= iq[i as usize]; + i -= 1; + } + if j == 0 { + /* need recomputation */ + k = 1; + while iq[(jk - k) as usize] == 0 { + k += 1; + } /* k = no. of terms needed */ + + i = jz + 1; + while i <= jz + k { + /* add q[jz+1] to q[jz+k] */ + f[(jx + i) as usize] = IPIO2[(jv + i) as usize] as f64; + + j = 0; + fw = 0.0; + while j <= jx { + fw += x[j as usize] * f[(jx + i - j) as usize]; + j += 1; + } + q[i as usize] = fw; + i += 1; + } + jz += k; + _done = false; + continue 'recompute; + } + } + } + + /* chop off zero terms */ + if z == 0.0 { + jz -= 1; + q0 -= 24; + while iq[jz as usize] == 0 { + jz -= 1; + q0 -= 24; } - + } else { + /* break z into 24-bit if necessary */ + z = scalbn(z, -(q0)); + if z >= TWO24 { + fw = ((TWON24 * z) as i32) as f64; + iq[jz as usize] = (z - TWO24 * fw) as i32; + jz += 1; + q0 += 24; + iq[jz as usize] = fw as i32; + } else { + iq[jz as usize] = z as i32; + } + } + + /* convert integer "bit" chunk to floating-point value */ + fw = scalbn(ONE, q0); + i = jz; + while i >= 0 { + q[i as usize] = fw * (iq[i as usize] as f64); + fw *= TWON24; + i -= 1; + } + + /* compute PIo2[0,...,jp]*q[jz,...,0] */ + i = jz; + while i >= 0 { + fw = 0.0; + k = 0; + while k <= jp && k <= jz - i { + fw += PIO2[k as usize] * q[(i + k) as usize]; + k += 1; + } + fq[(jz - i) as usize] = fw; + i -= 1; } + + /* compress fq[] into y[] */ + match prec { + 0 => { + fw = 0.0; + i = jz; + while i >= 0 { + fw += fq[i as usize]; + i -= 1; + } + y[0] = if ih == 0 { fw } else { -fw }; + } + + 1 | 2 => { + fw = 0.0; + i = jz; + while i >= 0 { + fw += fq[i as usize]; + i -= 1; + } + // STRICT_ASSIGN(double, fw, fw); + y[0] = if ih == 0 { fw } else { -fw }; + fw = fq[0] - fw; + fq[0] - fw; + i = 1; + while i <= jz { + fw += fq[i as usize]; + i += 1; + } + y[1] = if ih == 0 { fw } else { -fw }; + } + + 3 => { + /* painful */ + i = jz; + while i > 0 { + fw = fq[i as usize - 1] + fq[i as usize]; + fq[i as usize] += fq[i as usize - 1] - fw; + fq[i as usize - 1] = fw; + i -= 1; + } + i = jz; + while i > 1 { + fw = fq[i as usize - 1] + fq[i as usize]; + fq[i as usize] += fq[i as usize - 1] - fw; + fq[i as usize - 1] = fw; + i -= 1; + } + fw = 0.0; + i = jz; + while i >= 2 { + fw += fq[i as usize]; + i -= 1; + } + if ih == 0 { + y[0] = fq[0]; + y[1] = fq[1]; + y[2] = fw; + } else { + y[0] = -fq[0]; + y[1] = -fq[1]; + y[2] = -fw; + } + } + _ => {} + } + (n & 7, y) +} + +pub extern "C" fn floor(x: f64) -> f64 { + #![allow(overflowing_literals)] + const HUGE: f64 = 1.0e300; + let mut i0 = (x.repr() >> 32) as i32; + let mut i1 = x.repr() as u32; + let i: u32; + let j: u32; + + let j0: i32 = ((i0 >> 20) & 0x7ff) - 0x3ff; + if j0 < 20 { + if j0 < 0 { + /* raise inexact if x != 0 */ + if HUGE + x > 0.0 { + /* return 0*sign(x) if |x|<1 */ + if i0 >= 0 { + i0 = 0; + i1 = 0; + } else if (((i0 & 0x7fffffff) as u32) | i1) != 0 { + i0 = 0xbff00000; + i1 = 0; + } + } + } else { + i = 0x000fffff >> j0; + if (((i0 & i as i32) as u32) | i1) == 0 { + return x; + } /* x is integral */ + if HUGE + x > 0.0 { + /* raise inexact flag */ + if i0 < 0 { + i0 += (0x00100000) >> j0; + } + i0 &= !(i as i32); + i1 = 0; + } + } + } else if j0 > 51 { + if j0 == 0x400 { + return x + x; + } + /* inf or NaN */ else { + return x; + } /* x is integral */ + } else { + i = 0xffffffff as u32 >> (j0 - 20); + if (i1 & i) == 0 { + return x; + } /* x is integral */ + if HUGE + x > 0.0 { + /* raise inexact flag */ + if i0 < 0 { + if j0 == 20 { + i0 += 1; + } else { + let i1x = i1 as u32; + j = i1x.wrapping_add(1u32 << (52 - j0 as u32)) as u32; + if j < i1 { + i0 += 1; + } /* got a carry */ + i1 = j; + } + } + i1 &= !i; + } + } + + f64::from_repr(((i0 as u64) << 32) | (i1 as u64)) +} + +pub extern "C" fn floorf(x: f32) -> f32 { + #![allow(overflowing_literals)] + const HUGE: f32 = 1.0e30; + let mut i0 = x.repr() as i32; + //let j0: u32; + let i: i32; + + + let j0: i32 = ((i0 >> 23) & 0xff) as i32 - 0x7f_i32; + + + if j0 < 23 { + if j0 < 0 { + /* raise inexact if x != 0 */ + if HUGE + x > 0.0f32 { + /* return 0*sign(x) if |x|<1 */ + if i0 >= 0 { + i0 = 0; + } else if (i0 & 0x7fffffff) != 0 { + i0 = 0xbf800000; + } + } + } else { + i = (0x007fffff) >> j0; + + + if (i0 & i) == 0 { + return x; /* x is integral */ + } + + if (HUGE + x) > 0.0_f32 { + /* raise inexact flag */ + if i0 < 0 { + i0 += (0x00800000) >> j0; + } + i0 &= !i; + } + } + } else { + if j0 == 0x80 { + return x + x; /* inf or NaN */ + } else { + return x; + } /* x is integral */ + } + + f32::from_repr(i0 as u32) +} + +pub extern "C" fn scalbnf(_: f32, _: i32) -> f32 { + unimplemented!() +} + +pub extern "C" fn scalbn(mut x: f64, n: i32) -> f64 { + const TWO54: f64 = 1.80143985094819840000e+16; /* 0x43500000, 0x00000000 */ + const TWOM54: f64 = 5.55111512312578270212e-17; /* 0x3C900000, 0x00000000 */ + const HUGE: f64 = 1.0e+300; + const TINY: f64 = 1.0e-300; + + let mut hx = (x.repr() >> 32) as i32; + let lx = x.repr() as i32; + let mut k: i32 = ((hx & 0x7ff00000) >> 20) as i32; /* extract exponent */ + if k == 0 { + /* 0 or subnormal x */ + if lx | ((hx) & 0x7fffffff) == 0 { + return x; /* +-0 */ + } + x *= TWO54; + //GET_HIGH_WORD(hx, x); + hx = (x.repr() >> 32) as i32; + k = (((hx & 0x7ff00000) >> 20) - 54) as i32; + if n < -50000 { + return TINY * x; /*underflow*/ + } + } + if k == 0x7ff { + return x + x; /* NaN or Inf */ + } + k = k + n as i32; + if k > 0x7fe { + return HUGE * copysign(HUGE, x); /* overflow */ + } + if k > 0 { + /* normal result */ + let high_word:u64 = (((hx as u32)&0x800fffff)|((k<<20) as u32)) as u64; + return f64::from_repr(high_word<<32 | (x.repr() & 0xffffffff)); + } + if k <= -54 { + if n > 50000 { + /* in case integer overflow in n+k */ + return HUGE * copysign(HUGE, x); /*overflow*/ + } else { + return TINY * copysign(TINY, x); /*underflow*/ + } + } + k += 54; /* subnormal result */ + + let high_word:u64 = (((hx as u32)&0x800fffff)|((k<<20) as u32)) as u64; + f64::from_repr(high_word<<32 | ((x.repr()) & 0xffffffff)) * TWOM54 +} + +pub extern "C" fn copysignf(x: f32, y: f32) -> f32 { + let ix = x.repr(); + let iy = y.repr(); + f32::from_repr((ix & 0x7fffffff) | (iy & 0x80000000)) +} + +pub extern "C" fn copysign(x: f64, y: f64) -> f64 { + f64::from_repr( + (x.repr() & 0x7fffffffffffffff) | (y.repr() & 0x8000000000000000), + ) +} + +#[cfg(test)] +mod more_tests { + #[test] + fn atanf() { + use core::f32::consts::PI; + assert_eq!(super::atanf(0f32), 0f32); + assert_eq!(super::atanf(1f32), 0.7853982f32); + assert_eq!(super::atanf(2f32), 1.1071488f32); + assert_eq!(super::atanf(3f32), 1.2490457f32); + assert_eq!(super::atanf(PI), 1.2626272f32); + } + + #[test] + fn cosf() { + use core::f32::consts::PI; + assert_eq!(super::cosf(0f32), 1f32); + assert_eq!(super::cosf(0.1f32), 0.9950042f32); + assert_eq!(super::cosf(0.2f32), 0.9800666f32); + assert_eq!(super::cosf(0.3f32), 0.9553365f32); + assert_eq!(super::cosf(0.4f32), 0.921061f32); + assert_eq!(super::cosf(0.5f32), 0.87758255f32); + assert_eq!(super::cosf(1f32), 0.5403023f32); + assert_eq!(super::cosf(-1f32), 0.5403023f32); + assert_eq!(super::cosf(2f32), -0.41614684f32); + assert_eq!(super::cosf(3f32), -0.9899925f32); + assert_eq!(super::cosf(4f32), -0.6536436f32); + assert_eq!(super::cosf(PI), -1f32); + assert_eq!(super::cosf(-PI), -1f32); + assert_eq!(super::cosf(1f32), 0.540302305868f32); + } + + #[test] + fn scalbn() { + assert_eq!(super::scalbn(0_f64, 0), 0_f64); + assert_eq!(super::scalbn(-0_f64, 0), -0_f64); + assert_eq!(super::scalbn(0.8_f64, 4), 12.8_f64); + assert_eq!(super::scalbn(-0.854375_f64, 5), -27.34_f64); + assert_eq!(super::scalbn(1_f64, 0), 1_f64); + assert_eq!(super::scalbn(0.8_f64, 4), 12.8_f64); + assert_eq!(super::scalbn(8718927381278827_f64, -18), 33260068440.547283_f64); + } + + #[test] + fn floorf() { + assert_eq!(super::floorf(123.7f32), 123f32); + assert_eq!(super::floorf(12345.6f32), 12345f32); + assert_eq!(super::floorf(1.9f32), 1f32); + assert_eq!(super::floorf(1.009f32), 1f32); + assert_eq!(super::floorf(-1.009f32), -2f32); + assert_eq!(super::floorf(-0.009f32), -1f32); + } + + #[test] + fn floor() { + assert_eq!(super::floor(123.7f64), 123f64); + assert_eq!(super::floor(12345.6f64), 12345f64); + assert_eq!(super::floor(1.9f64), 1f64); + assert_eq!(super::floor(1.009f64), 1f64); + assert_eq!(super::floor(-1.009f64), -2f64); + assert_eq!(super::floor(-0.009f64), -1f64); + assert_eq!(super::floor(-14207526.52111395f64), -14207527f64); + } + + #[test] + fn sinf() { + use core::f32::consts::PI; + assert_eq!(super::sinf(0.00001f32), 0.00001f32); + assert_eq!(super::sinf(0.001f32), 0.0009999999f32); + assert_eq!(super::sinf(0.1f32), 0.09983342f32); + assert_eq!(super::sinf(0.2f32), 0.19866933f32); + assert_eq!(super::sinf(0.3f32), 0.29552022f32); + assert_eq!(super::sinf(0.5f32), 0.47942555f32); + assert_eq!(super::sinf(0.8f32), 0.7173561f32); + assert_eq!(super::sinf(1f32), 0.84147096f32); + assert_eq!(super::sinf(1.5f32), 0.997495f32); + assert_eq!(super::sinf(2f32), 0.9092974f32); + assert_eq!(super::sinf(0f32), 0f32); + assert_eq!(super::sinf(-1f32), -0.84147096f32); + assert_eq!(super::sinf(PI), -0.00000008742278_f32); + assert_eq!(super::sinf(-PI), 0.00000008742278_f32); + assert_eq!(super::sinf(2.5f32), 0.5984721_f32); + assert_eq!(super::sinf(2f32 * PI), 0.00000017484555f32); + } +} + +#[cfg(test)] +check! { +// `atan` has not been implemented yet +// fn atan2(f: extern fn(f64, f64) -> f64, y: F64, x: F64) -> Option { +// Some(F64(f(y.0, x.0))) +// } + + +fn atan2f(f: extern fn (f32, f32) -> f32, y: F32, x: F32) -> Option < F32 > { +Some(F32(f(y.0, x.0))) +} + +fn atanf(f: extern fn (f32) -> f32, x: F32) -> Option { +Some(F32(f(x.0))) +} + +// unimplemented! +// fn atan(f: extern fn(f64) -> f64, x: F64) -> Option { +// Some(F64(f(x.0))) +// } + +fn fabs(f: extern fn (f64) -> f64, x: F64) -> Option { +Some(F64(f(x.0))) +} + +fn fabsf(f: extern fn (f32) -> f32, x: F32) -> Option { +Some(F32(f(x.0))) +} + +fn cosf(f: extern fn (f32) -> f32, x: F32limit) -> Option { +Some(F32limit(f(x.0))) +} + +fn sinf(f: extern fn (f32) -> f32, x: F32limit) -> Option { +Some(F32limit(f(x.0))) +} + +fn copysign(f: extern fn (f64, f64) -> f64, y: F64, x: F64) -> Option < F64 > { +Some(F64(f(y.0, x.0))) +} +fn copysignf(f: extern fn (f32, f32) -> f32, y: F32, x: F32) -> Option < F32 > { +Some(F32(f(y.0, x.0))) +} +fn scalbn(f: extern fn (f64, i32) -> f64, x: F64, n: i32) -> Option < F64 > { +Some(F64(f(x.0, n))) +} + +fn floorf(f: extern fn (f32) -> f32, x: F32) -> Option { +Some(F32(f(x.0))) +} +fn floor(f: extern fn (f64) -> f64, x: F64) -> Option { +Some(F64(f(x.0))) +} + +// unimplemented! +// fn sqrt(f: extern fn(f64) -> f64, x: F64) -> Option { +// Some(F64(f(x.0))) +// } + +fn sqrtf(f: extern fn (f32) -> f32, x: F32) -> Option { +match () { +# [cfg(all(target_env = "gnu", target_os = "windows"))] +() => { +if x.0.repr() == 0x8000_0000 { +None +} else { +Some(F32(f(x.0))) +} +}, +# [cfg(not(all(target_env = "gnu", target_os = "windows")))] +() => Some(F32(f(x.0))), +} + +} } diff --git a/src/m.rs b/src/m.rs index 5ed5ebb..8157429 100644 --- a/src/m.rs +++ b/src/m.rs @@ -9,4 +9,11 @@ extern "C" { pub fn fabs(x: f64) -> f64; pub fn fabsf(x: f32) -> f32; pub fn sqrtf(x: f32) -> f32; + pub fn cosf(x: f32) -> f32; + pub fn sinf(x: f32) -> f32; + pub fn copysign(x: f64, y: f64) -> f64; + pub fn copysignf(x: f32, y: f32) -> f32; + pub fn scalbn(x: f64, n: i32) -> f64; + pub fn floorf(x: f32) -> f32; + pub fn floor(x: f64) -> f64; } diff --git a/src/qc.rs b/src/qc.rs index c98cde3..249d9cc 100644 --- a/src/qc.rs +++ b/src/qc.rs @@ -1,4 +1,4 @@ -use std::{f32, f64, fmt}; +use std::{fmt, f32, f64}; use quickcheck::{Arbitrary, Gen}; @@ -74,6 +74,15 @@ impl fmt::Debug for F32 { } } +#[derive(Clone, Copy, PartialEq)] +pub struct F32limit(pub f32); + +impl fmt::Debug for F32limit { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{} (0x{:08x})", self.0, self.0.repr()) + } +} + #[derive(Clone, Copy, PartialEq)] pub struct F64(pub f64); @@ -84,7 +93,7 @@ impl fmt::Debug for F64 { } macro_rules! arbitrary_float { - ($ty:ident, $fty:ident) => { + ($ty:ident, $fty:ident, $lim:ident) => { impl Arbitrary for $ty { fn arbitrary(g: &mut G) -> $ty where G: Gen @@ -93,7 +102,10 @@ macro_rules! arbitrary_float { [-0.0, 0.0, $fty::NAN, $fty::INFINITY, $fty::NEG_INFINITY]; let (sign, mut exponent, mut significand) = g.gen(); - if g.gen_weighted_bool(10) { + if $lim { + exponent = g.gen_range(0, 1097630827); + //println!(" input: {:?} {:?}", exponent, $fty::from_parts(Sign::from_bool(sign),exponent,significand)); + } else if g.gen_weighted_bool(10) { return $ty(*g.choose(&special).unwrap()); } else if g.gen_weighted_bool(10) { // NaN variants @@ -111,5 +123,7 @@ macro_rules! arbitrary_float { } } -arbitrary_float!(F32, f32); -arbitrary_float!(F64, f64); +arbitrary_float!(F32, f32, false); +arbitrary_float!(F64, f64, false); +arbitrary_float!(F32limit, f32, true); +//arbitrary_float!(F64limit, f64, true);