| /* SPDX-License-Identifier: MIT OR Apache-2.0 */ | ||
| use crate::support::{CastInto, DInt, HInt, Int, MinInt, u256}; | ||
| /// Trait for unsigned division of a double-wide integer | ||
| /// when the quotient doesn't overflow. | ||
| /// | ||
| /// This is the inverse of widening multiplication: | ||
| /// - for any `x` and nonzero `y`: `x.widen_mul(y).checked_narrowing_div_rem(y) == Some((x, 0))`, | ||
| /// - and for any `r in 0..y`: `x.carrying_mul(y, r).checked_narrowing_div_rem(y) == Some((x, r))`, | ||
| pub trait NarrowingDiv: DInt + MinInt<Unsigned = Self> { | ||
| /// Computes `(self / n, self % n))` | ||
| /// | ||
| /// # Safety | ||
| /// The caller must ensure that `self.hi() < n`, or equivalently, | ||
| /// that the quotient does not overflow. | ||
| unsafe fn unchecked_narrowing_div_rem(self, n: Self::H) -> (Self::H, Self::H); | ||
| /// Returns `Some((self / n, self % n))` when `self.hi() < n`. | ||
| fn checked_narrowing_div_rem(self, n: Self::H) -> Option<(Self::H, Self::H)> { | ||
| if self.hi() < n { | ||
| Some(unsafe { self.unchecked_narrowing_div_rem(n) }) | ||
| } else { | ||
| None | ||
| } | ||
| } | ||
| } | ||
| // For primitive types we can just use the standard | ||
| // division operators in the double-wide type. | ||
| macro_rules! impl_narrowing_div_primitive { | ||
| ($D:ident) => { | ||
| impl NarrowingDiv for $D { | ||
| unsafe fn unchecked_narrowing_div_rem(self, n: Self::H) -> (Self::H, Self::H) { | ||
| if self.hi() >= n { | ||
| unsafe { core::hint::unreachable_unchecked() } | ||
| } | ||
| ((self / n.widen()).cast(), (self % n.widen()).cast()) | ||
| } | ||
| } | ||
| }; | ||
| } | ||
| // Extend division from `u2N / uN` to `u4N / u2N` | ||
| // This is not the most efficient algorithm, but it is | ||
| // relatively simple. | ||
| macro_rules! impl_narrowing_div_recurse { | ||
| ($D:ident) => { | ||
| impl NarrowingDiv for $D { | ||
| unsafe fn unchecked_narrowing_div_rem(self, n: Self::H) -> (Self::H, Self::H) { | ||
| if self.hi() >= n { | ||
| unsafe { core::hint::unreachable_unchecked() } | ||
| } | ||
| // Normalize the divisor by shifting the most significant one | ||
| // to the leading position. `n != 0` is implied by `self.hi() < n` | ||
| let lz = n.leading_zeros(); | ||
| let a = self << lz; | ||
| let b = n << lz; | ||
| let ah = a.hi(); | ||
| let (a0, a1) = a.lo().lo_hi(); | ||
| // SAFETY: For both calls, `b.leading_zeros() == 0` by the above shift. | ||
| // SAFETY: `ah < b` follows from `self.hi() < n` | ||
| let (q1, r) = unsafe { div_three_digits_by_two(a1, ah, b) }; | ||
| // SAFETY: `r < b` is given as the postcondition of the previous call | ||
| let (q0, r) = unsafe { div_three_digits_by_two(a0, r, b) }; | ||
| // Undo the earlier normalization for the remainder | ||
| (Self::H::from_lo_hi(q0, q1), r >> lz) | ||
| } | ||
| } | ||
| }; | ||
| } | ||
| impl_narrowing_div_primitive!(u16); | ||
| impl_narrowing_div_primitive!(u32); | ||
| impl_narrowing_div_primitive!(u64); | ||
| impl_narrowing_div_primitive!(u128); | ||
| impl_narrowing_div_recurse!(u256); | ||
| /// Implement `u3N / u2N`-division on top of `u2N / uN`-division. | ||
| /// | ||
| /// Returns the quotient and remainder of `(a * R + a0) / n`, | ||
| /// where `R = (1 << U::BITS)` is the digit size. | ||
| /// | ||
| /// # Safety | ||
| /// Requires that `n.leading_zeros() == 0` and `a < n`. | ||
| unsafe fn div_three_digits_by_two<U>(a0: U, a: U::D, n: U::D) -> (U, U::D) | ||
| where | ||
| U: HInt, | ||
| U::D: Int + NarrowingDiv, | ||
| { | ||
| if n.leading_zeros() > 0 || a >= n { | ||
| unsafe { core::hint::unreachable_unchecked() } | ||
| } | ||
| // n = n1R + n0 | ||
| let (n0, n1) = n.lo_hi(); | ||
| // a = a2R + a1 | ||
| let (a1, a2) = a.lo_hi(); | ||
| let mut q; | ||
| let mut r; | ||
| let mut wrap; | ||
| // `a < n` is guaranteed by the caller, but `a2 == n1 && a1 < n0` is possible | ||
| if let Some((q0, r1)) = a.checked_narrowing_div_rem(n1) { | ||
| q = q0; | ||
| // a = qn1 + r1, where 0 <= r1 < n1 | ||
| // Include the remainder with the low bits: | ||
| // r = a0 + r1R | ||
| r = U::D::from_lo_hi(a0, r1); | ||
| // Subtract the contribution of the divisor low bits with the estimated quotient | ||
| let d = q.widen_mul(n0); | ||
| (r, wrap) = r.overflowing_sub(d); | ||
| // Since `q` is the quotient of dividing with a slightly smaller divisor, | ||
| // it may be an overapproximation, but is never too small, and similarly, | ||
| // `r` is now either the correct remainder ... | ||
| if !wrap { | ||
| return (q, r); | ||
| } | ||
| // ... or the remainder went "negative" (by as much as `d = qn0 < RR`) | ||
| // and we have to adjust. | ||
| q -= U::ONE; | ||
| } else { | ||
| debug_assert!(a2 == n1 && a1 < n0); | ||
| // Otherwise, `a2 == n1`, and the estimated quotient would be | ||
| // `R + (a1 % n1)`, but the correct quotient can't overflow. | ||
| // We'll start from `q = R = (1 << U::BITS)`, | ||
| // so `r = aR + a0 - qn = (a - n)R + a0` | ||
| r = U::D::from_lo_hi(a0, a1.wrapping_sub(n0)); | ||
| // Since `a < n`, the first decrement is always needed: | ||
| q = U::MAX; /* R - 1 */ | ||
| } | ||
| (r, wrap) = r.overflowing_add(n); | ||
| if wrap { | ||
| return (q, r); | ||
| } | ||
| // If the remainder still didn't wrap, we need another step. | ||
| q -= U::ONE; | ||
| (r, wrap) = r.overflowing_add(n); | ||
| // Since `n >= RR/2`, at least one of the two `r += n` must have wrapped. | ||
| debug_assert!(wrap, "estimated quotient should be off by at most two"); | ||
| (q, r) | ||
| } | ||
| #[cfg(test)] | ||
| mod test { | ||
| use super::{HInt, NarrowingDiv}; | ||
| #[test] | ||
| fn inverse_mul() { | ||
| for x in 0..=u8::MAX { | ||
| for y in 1..=u8::MAX { | ||
| let xy = x.widen_mul(y); | ||
| assert_eq!(xy.checked_narrowing_div_rem(y), Some((x, 0))); | ||
| assert_eq!( | ||
| (xy + (y - 1) as u16).checked_narrowing_div_rem(y), | ||
| Some((x, y - 1)) | ||
| ); | ||
| if y > 1 { | ||
| assert_eq!((xy + 1).checked_narrowing_div_rem(y), Some((x, 1))); | ||
| assert_eq!( | ||
| (xy + (y - 2) as u16).checked_narrowing_div_rem(y), | ||
| Some((x, y - 2)) | ||
| ); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } |
| /* SPDX-License-Identifier: MIT OR Apache-2.0 */ | ||
| //! This module provides accelerated modular multiplication by large powers | ||
| //! of two, which is needed for computing floating point remainders in `fmod` | ||
| //! and similar functions. | ||
| //! | ||
| //! To keep the equations somewhat concise, the following conventions are used: | ||
| //! - all integer operations are in the mathematical sense, without overflow | ||
| //! - concatenation means multiplication: `2xq = 2 * x * q` | ||
| //! - `R = (1 << U::BITS)` is the modulus of wrapping arithmetic in `U` | ||
| use crate::support::int_traits::NarrowingDiv; | ||
| use crate::support::{DInt, HInt, Int}; | ||
| /// Compute the remainder `(x << e) % y` with unbounded integers. | ||
| /// Requires `x < 2y` and `y.leading_zeros() >= 2` | ||
| pub fn linear_mul_reduction<U>(x: U, mut e: u32, mut y: U) -> U | ||
| where | ||
| U: HInt + Int<Unsigned = U>, | ||
| U::D: NarrowingDiv, | ||
| { | ||
| assert!(y <= U::MAX >> 2); | ||
| assert!(x < (y << 1)); | ||
| let _0 = U::ZERO; | ||
| let _1 = U::ONE; | ||
| // power of two divisors | ||
| if (y & (y - _1)).is_zero() { | ||
| if e < U::BITS { | ||
| // shift and only keep low bits | ||
| return (x << e) & (y - _1); | ||
| } else { | ||
| // would shift out all the bits | ||
| return _0; | ||
| } | ||
| } | ||
| // Use the identity `(x << e) % y == ((x << (e + s)) % (y << s)) >> s` | ||
| // to shift the divisor so it has exactly two leading zeros to satisfy | ||
| // the precondition of `Reducer::new` | ||
| let s = y.leading_zeros() - 2; | ||
| e += s; | ||
| y <<= s; | ||
| // `m: Reducer` keeps track of the remainder `x` in a form that makes it | ||
| // very efficient to do `x <<= k` modulo `y` for integers `k < U::BITS` | ||
| let mut m = Reducer::new(x, y); | ||
| // Use the faster special case with constant `k == U::BITS - 1` while we can | ||
| while e >= U::BITS - 1 { | ||
| m.word_reduce(); | ||
| e -= U::BITS - 1; | ||
| } | ||
| // Finish with the variable shift operation | ||
| m.shift_reduce(e); | ||
| // The partial remainder is in `[0, 2y)` ... | ||
| let r = m.partial_remainder(); | ||
| // ... so check and correct, and compensate for the earlier shift. | ||
| r.checked_sub(y).unwrap_or(r) >> s | ||
| } | ||
| /// Helper type for computing the reductions. The implementation has a number | ||
| /// of seemingly weird choices, but everything is aimed at streamlining | ||
| /// `Reducer::word_reduce` into its current form. | ||
| /// | ||
| /// Implicitly contains: | ||
| /// n in (R/8, R/4) | ||
| /// x in [0, 2n) | ||
| /// The value of `n` is fixed for a given `Reducer`, | ||
| /// but the value of `x` is modified by the methods. | ||
| #[derive(Debug, Clone, PartialEq, Eq)] | ||
| struct Reducer<U: HInt> { | ||
| // m = 2n | ||
| m: U, | ||
| // q = (RR/2) / m | ||
| // r = (RR/2) % m | ||
| // Then RR/2 = qm + r, where `0 <= r < m` | ||
| // The value `q` is only needed during construction, so isn't saved. | ||
| r: U, | ||
| // The value `x` is implicitly stored as `2 * q * x`: | ||
| _2xq: U::D, | ||
| } | ||
| impl<U> Reducer<U> | ||
| where | ||
| U: HInt, | ||
| U: Int<Unsigned = U>, | ||
| { | ||
| /// Construct a reducer for `(x << _) mod n`. | ||
| /// | ||
| /// Requires `R/8 < n < R/4` and `x < 2n`. | ||
| fn new(x: U, n: U) -> Self | ||
| where | ||
| U::D: NarrowingDiv, | ||
| { | ||
| let _1 = U::ONE; | ||
| assert!(n > (_1 << (U::BITS - 3))); | ||
| assert!(n < (_1 << (U::BITS - 2))); | ||
| let m = n << 1; | ||
| assert!(x < m); | ||
| // We need to compute the parameters | ||
| // `q = (RR/2) / m` | ||
| // `r = (RR/2) % m` | ||
| // Since `m` is in `(R/4, R/2)`, the quotient `q` is in `[R, 2R)`, and | ||
| // it would overflow in `U` if computed directly. Instead, we compute | ||
| // `f = q - R`, which is in `[0, R)`. To do so, we simply subtract `Rm` | ||
| // from the dividend, which doesn't change the remainder: | ||
| // `f = R(R/2 - m) / m` | ||
| // `r = R(R/2 - m) % m` | ||
| let dividend = ((_1 << (U::BITS - 1)) - m).widen_hi(); | ||
| let (f, r) = dividend.checked_narrowing_div_rem(m).unwrap(); | ||
| // As `x < m`, `xq < qm <= RR/2` | ||
| // Thus `2xq = 2xR + 2xf` does not overflow in `U::D`. | ||
| let _2x = x + x; | ||
| let _2xq = _2x.widen_hi() + _2x.widen_mul(f); | ||
| Self { m, r, _2xq } | ||
| } | ||
| /// Extract the current remainder `x` in the range `[0, 2n)` | ||
| fn partial_remainder(&self) -> U { | ||
| // `RR/2 = qm + r`, where `0 <= r < m` | ||
| // `2xq = uR + v`, where `0 <= v < R` | ||
| // The goal is to extract the current value of `x` from the value `2xq` | ||
| // that we actually have. A bit simplified, we could multiply it by `m` | ||
| // to obtain `2xqm == 2x(RR/2 - r) == xRR - 2xr`, where `2xr < RR`. | ||
| // We could just round that up to the next multiple of `RR` to get `x`, | ||
| // but we can avoid having to multiply the full double-wide `2xq` by | ||
| // making a couple of adjustments: | ||
| // First, let's only use the high half `u` for the product, and | ||
| // include an additional error term due to the truncation: | ||
| // `mu = xR - (2xr + mv)/R` | ||
| // Next, show bounds for the error term | ||
| // `0 <= mv < mR` follows from `0 <= v < R` | ||
| // `0 <= 2xr < mR` follows from `0 <= x < m < R/2` and `0 <= r < m` | ||
| // Adding those together, we have: | ||
| // `0 <= (mv + 2xr)/R < 2m` | ||
| // Which also implies: | ||
| // `0 < 2m - (mv + 2xr)/R <= 2m < R` | ||
| // For that reason, we can use `u + 2` as the factor to obtain | ||
| // `m(u + 2) = xR + (2m - (mv + 2xr)/R)` | ||
| // By the previous inequality, the second term fits neatly in the lower | ||
| // half, so we get exactly `x` as the high half. | ||
| let u = self._2xq.hi(); | ||
| let _2 = U::ONE + U::ONE; | ||
| self.m.widen_mul(u + _2).hi() | ||
| // Additionally, we should ensure that `u + 2` cannot overflow: | ||
| // Since `x < m` and `2qm <= RR`, | ||
| // `2xq <= 2q(m-1) <= RR - 2q` | ||
| // As we also have `q > R`, | ||
| // `2xq < RR - 2R` | ||
| // which is sufficient. | ||
| } | ||
| /// Replace the remainder `x` with `(x << k) - un`, | ||
| /// for a suitable quotient `u`, which is returned. | ||
| /// | ||
| /// Requires that `k < U::BITS`. | ||
| fn shift_reduce(&mut self, k: u32) -> U { | ||
| assert!(k < U::BITS); | ||
| // First, split the shifted value: | ||
| // `2xq << k = aRR/2 + b`, where `0 <= b < RR/2` | ||
| let a = self._2xq.hi() >> (U::BITS - 1 - k); | ||
| let (low, high) = (self._2xq << k).lo_hi(); | ||
| let b = U::D::from_lo_hi(low, high & (U::MAX >> 1)); | ||
| // Then, subtract `2anq = aqm`: | ||
| // ``` | ||
| // (2xq << k) - aqm | ||
| // = aRR/2 + b - aqm | ||
| // = a(RR/2 - qm) + b | ||
| // = ar + b | ||
| // ``` | ||
| self._2xq = a.widen_mul(self.r) + b; | ||
| a | ||
| // Since `a` is at most the high half of `2xq`, we have | ||
| // `a + 2 < R` (shown above, in `partial_remainder`) | ||
| // Using that together with `b < RR/2` and `r < m < R/2`, | ||
| // we get `(a + 2)r + b < RR`, so | ||
| // `ar + b < RR - 2r = 2mq` | ||
| // which shows that the new remainder still satisfies `x < m`. | ||
| } | ||
| // NB: `word_reduce()` is just the special case `shift_reduce(U::BITS - 1)` | ||
| // that optimizes especially well. The correspondence is that `a == u` and | ||
| // `b == (v >> 1).widen_hi()` | ||
| // | ||
| /// Replace the remainder `x` with `x(R/2) - un`, | ||
| /// for a suitable quotient `u`, which is returned. | ||
| fn word_reduce(&mut self) -> U { | ||
| // To do so, we replace `2xq = uR + v` with | ||
| // ``` | ||
| // 2 * (x(R/2) - un) * q | ||
| // = xqR - 2unq | ||
| // = xqR - uqm | ||
| // = uRR/2 + vR/2 - uRR/2 + ur | ||
| // = ur + (v/2)R | ||
| // ``` | ||
| let (v, u) = self._2xq.lo_hi(); | ||
| self._2xq = u.widen_mul(self.r) + U::widen_hi(v >> 1); | ||
| u | ||
| // Additional notes: | ||
| // 1. As `v` is the low bits of `2xq`, it is even and can be halved. | ||
| // 2. The new remainder is `(xr + mv/2) / R` (see below) | ||
| // and since `v < R`, `r < m`, `x < m < R/2`, | ||
| // that is also strictly less than `m`. | ||
| // ``` | ||
| // (x(R/2) - un)R | ||
| // = xRR/2 - (m/2)uR | ||
| // = x(qm + r) - (m/2)(2xq - v) | ||
| // = xqm + xr - xqm + mv/2 | ||
| // = xr + mv/2 | ||
| // ``` | ||
| } | ||
| } | ||
| #[cfg(test)] | ||
| mod test { | ||
| use crate::support::linear_mul_reduction; | ||
| use crate::support::modular::Reducer; | ||
| #[test] | ||
| fn reducer_ops() { | ||
| for n in 33..=63_u8 { | ||
| for x in 0..2 * n { | ||
| let temp = Reducer::new(x, n); | ||
| let n = n as u32; | ||
| let x0 = temp.partial_remainder() as u32; | ||
| assert_eq!(x as u32, x0); | ||
| for k in 0..=7 { | ||
| let mut red = temp.clone(); | ||
| let u = red.shift_reduce(k) as u32; | ||
| let x1 = red.partial_remainder() as u32; | ||
| assert_eq!(x1, (x0 << k) - u * n); | ||
| assert!(x1 < 2 * n); | ||
| assert!((red._2xq as u32).is_multiple_of(2 * x1)); | ||
| // `word_reduce` is equivalent to | ||
| // `shift_reduce(U::BITS - 1)` | ||
| if k == 7 { | ||
| let mut alt = temp.clone(); | ||
| let w = alt.word_reduce(); | ||
| assert_eq!(u, w as u32); | ||
| assert_eq!(alt, red); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| #[test] | ||
| fn reduction_u8() { | ||
| for y in 1..64u8 { | ||
| for x in 0..2 * y { | ||
| let mut r = x % y; | ||
| for e in 0..100 { | ||
| assert_eq!(r, linear_mul_reduction(x, e, y)); | ||
| // maintain the correct expected remainder | ||
| r <<= 1; | ||
| if r >= y { | ||
| r -= y; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| #[test] | ||
| fn reduction_u128() { | ||
| assert_eq!( | ||
| linear_mul_reduction::<u128>(17, 100, 123456789), | ||
| (17 << 100) % 123456789 | ||
| ); | ||
| // power-of-two divisor | ||
| assert_eq!( | ||
| linear_mul_reduction(0xdead_beef, 100, 1_u128 << 116), | ||
| 0xbeef << 100 | ||
| ); | ||
| let x = 10_u128.pow(37); | ||
| let y = 11_u128.pow(36); | ||
| assert!(x < y); | ||
| let mut r = x; | ||
| for e in 0..1000 { | ||
| assert_eq!(r, linear_mul_reduction(x, e, y)); | ||
| // maintain the correct expected remainder | ||
| r <<= 1; | ||
| if r >= y { | ||
| r -= y; | ||
| } | ||
| assert!(r != 0); | ||
| } | ||
| } | ||
| } |
| { | ||
| "git": { | ||
| "sha1": "a4c748f72a1dce652cc3e41c3a8425731bd1519a" | ||
| "sha1": "dfd2203a4d6110820ad7bb65cafe1bf331a03a3d" | ||
| }, | ||
| "path_in_vcs": "libm" | ||
| } |
+11
-11
@@ -7,3 +7,3 @@ # This file is automatically @generated by Cargo. | ||
| name = "libm" | ||
| version = "0.2.15" | ||
| version = "0.2.16" | ||
| dependencies = [ | ||
@@ -15,5 +15,5 @@ "no-panic", | ||
| name = "no-panic" | ||
| version = "0.1.35" | ||
| version = "0.1.36" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "113d1abd5bb3dc25a75d9b3a973f40e31eb03e0bae23c172b32cca4bcb9cfad2" | ||
| checksum = "f967505aabc8af5752d098c34146544a43684817cdba8f9725b292530cabbf53" | ||
| dependencies = [ | ||
@@ -27,5 +27,5 @@ "proc-macro2", | ||
| name = "proc-macro2" | ||
| version = "1.0.95" | ||
| version = "1.0.106" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" | ||
| checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" | ||
| dependencies = [ | ||
@@ -37,5 +37,5 @@ "unicode-ident", | ||
| name = "quote" | ||
| version = "1.0.40" | ||
| version = "1.0.44" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" | ||
| checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" | ||
| dependencies = [ | ||
@@ -47,5 +47,5 @@ "proc-macro2", | ||
| name = "syn" | ||
| version = "2.0.101" | ||
| version = "2.0.114" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" | ||
| checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" | ||
| dependencies = [ | ||
@@ -59,4 +59,4 @@ "proc-macro2", | ||
| name = "unicode-ident" | ||
| version = "1.0.18" | ||
| version = "1.0.22" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" | ||
| checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" |
+7
-3
@@ -16,4 +16,9 @@ # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO | ||
| name = "libm" | ||
| version = "0.2.15" | ||
| authors = ["Jorge Aparicio <jorge@japaric.io>"] | ||
| version = "0.2.16" | ||
| authors = [ | ||
| "Alex Crichton <alex@alexcrichton.com>", | ||
| "Amanieu d'Antras <amanieu@gmail.com>", | ||
| "Jorge Aparicio <japaricious@gmail.com>", | ||
| "Trevor Gross <tg@trevorgross.com>", | ||
| ] | ||
| build = "build.rs" | ||
@@ -26,3 +31,2 @@ autolib = false | ||
| description = "libm in pure Rust" | ||
| documentation = "https://docs.rs/libm" | ||
| readme = "README.md" | ||
@@ -29,0 +33,0 @@ keywords = [ |
+16
-0
@@ -11,2 +11,18 @@ # Changelog | ||
| ## [0.2.16](https://github.com/rust-lang/compiler-builtins/compare/libm-v0.2.15...libm-v0.2.16) - 2025-12-07 | ||
| ### Fixed | ||
| - Fix an incorrect result for `fminimum` and `fmaximum` with the input (-0, NaN) | ||
| - Fix a typo in `libm::Libm::roundeven` | ||
| - Fix the `expm1f` overflow threshold | ||
| - Change `CmpResult` to use a pointer-sized return type | ||
| - Compare against `CARGO_CFG_TARGET_FAMILY` in a multi-valued fashion | ||
| - Implement `exp` and its variants for i586 with inline assembly | ||
| - Implement `floor` and `ceil` in assembly on `i586` | ||
| ### Other | ||
| - Significantly optimize `fmod` worst case performance ([#1002](https://github.com/rust-lang/compiler-builtins/pull/1002)) | ||
| ## [0.2.15](https://github.com/rust-lang/compiler-builtins/compare/libm-v0.2.14...libm-v0.2.15) - 2025-05-06 | ||
@@ -13,0 +29,0 @@ |
+21
-52
@@ -6,2 +6,3 @@ // Configuration shared with both libm and libm-test | ||
| #[derive(Debug)] | ||
| #[allow(dead_code)] | ||
@@ -13,5 +14,6 @@ pub struct Config { | ||
| pub cargo_features: Vec<String>, | ||
| pub target_triple: String, | ||
| pub target_arch: String, | ||
| pub target_env: String, | ||
| pub target_family: Option<String>, | ||
| pub target_families: Vec<String>, | ||
| pub target_os: String, | ||
@@ -21,2 +23,4 @@ pub target_string: String, | ||
| pub target_features: Vec<String>, | ||
| pub reliable_f128: bool, | ||
| pub reliable_f16: bool, | ||
| } | ||
@@ -26,2 +30,6 @@ | ||
| pub fn from_env() -> Self { | ||
| let target_triple = env::var("TARGET").unwrap(); | ||
| let target_families = env::var("CARGO_CFG_TARGET_FAMILY") | ||
| .map(|feats| feats.split(',').map(ToOwned::to_owned).collect()) | ||
| .unwrap_or_default(); | ||
| let target_features = env::var("CARGO_CFG_TARGET_FEATURE") | ||
@@ -36,2 +44,3 @@ .map(|feats| feats.split(',').map(ToOwned::to_owned).collect()) | ||
| Self { | ||
| target_triple, | ||
| manifest_dir: PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()), | ||
@@ -43,3 +52,3 @@ out_dir: PathBuf::from(env::var("OUT_DIR").unwrap()), | ||
| target_env: env::var("CARGO_CFG_TARGET_ENV").unwrap(), | ||
| target_family: env::var("CARGO_CFG_TARGET_FAMILY").ok(), | ||
| target_families, | ||
| target_os: env::var("CARGO_CFG_TARGET_OS").unwrap(), | ||
@@ -49,2 +58,6 @@ target_string: env::var("TARGET").unwrap(), | ||
| target_features, | ||
| // Note that these are unstable options, so only show up with the nightly compiler or | ||
| // with `RUSTC_BOOTSTRAP=1` (which is required to use the types anyway). | ||
| reliable_f128: env::var_os("CARGO_CFG_TARGET_HAS_RELIABLE_F128").is_some(), | ||
| reliable_f16: env::var_os("CARGO_CFG_TARGET_HAS_RELIABLE_F16").is_some(), | ||
| } | ||
@@ -138,60 +151,16 @@ } | ||
| // Set whether or not `f16` and `f128` are supported at a basic level by LLVM. This only means | ||
| // that the backend will not crash when using these types and generates code that can be called | ||
| // without crashing (no infinite recursion). This does not mean that the platform doesn't have | ||
| // ABI or other bugs. | ||
| // | ||
| // We do this here rather than in `rust-lang/rust` because configuring via cargo features is | ||
| // not straightforward. | ||
| // | ||
| // Original source of this list: | ||
| // <https://github.com/rust-lang/compiler-builtins/pull/652#issuecomment-2266151350> | ||
| let f16_enabled = match cfg.target_arch.as_str() { | ||
| // Unsupported <https://github.com/llvm/llvm-project/issues/94434> | ||
| "arm64ec" => false, | ||
| // Selection failure <https://github.com/llvm/llvm-project/issues/50374> | ||
| "s390x" => false, | ||
| // Infinite recursion <https://github.com/llvm/llvm-project/issues/97981> | ||
| // FIXME(llvm): loongarch fixed by <https://github.com/llvm/llvm-project/pull/107791> | ||
| "csky" => false, | ||
| "hexagon" => false, | ||
| "loongarch64" => false, | ||
| "mips" | "mips64" | "mips32r6" | "mips64r6" => false, | ||
| "powerpc" | "powerpc64" => false, | ||
| "sparc" | "sparc64" => false, | ||
| "wasm32" | "wasm64" => false, | ||
| // Most everything else works as of LLVM 19 | ||
| _ => true, | ||
| }; | ||
| /* See the compiler-builtins configure file for info about the meaning of these options */ | ||
| let f128_enabled = match cfg.target_arch.as_str() { | ||
| // Unsupported (libcall is not supported) <https://github.com/llvm/llvm-project/issues/121122> | ||
| "amdgpu" => false, | ||
| // Unsupported <https://github.com/llvm/llvm-project/issues/94434> | ||
| "arm64ec" => false, | ||
| // Selection failure <https://github.com/llvm/llvm-project/issues/96432> | ||
| "mips64" | "mips64r6" => false, | ||
| // Selection failure <https://github.com/llvm/llvm-project/issues/95471> | ||
| "nvptx64" => false, | ||
| // Selection failure <https://github.com/llvm/llvm-project/issues/101545> | ||
| "powerpc64" if &cfg.target_os == "aix" => false, | ||
| // Selection failure <https://github.com/llvm/llvm-project/issues/41838> | ||
| "sparc" => false, | ||
| // Most everything else works as of LLVM 19 | ||
| _ => true, | ||
| }; | ||
| // If the feature is set, disable both of these types. | ||
| let no_f16_f128 = cfg.cargo_features.iter().any(|s| s == "no-f16-f128"); | ||
| // If the feature is set, disable these types. | ||
| let disable_both = env::var_os("CARGO_FEATURE_NO_F16_F128").is_some(); | ||
| println!("cargo:rustc-check-cfg=cfg(f16_enabled)"); | ||
| println!("cargo:rustc-check-cfg=cfg(f128_enabled)"); | ||
| if f16_enabled && !disable_both { | ||
| if cfg.reliable_f16 && !no_f16_f128 { | ||
| println!("cargo:rustc-cfg=f16_enabled"); | ||
| } | ||
| if f128_enabled && !disable_both { | ||
| println!("cargo:rustc-check-cfg=cfg(f128_enabled)"); | ||
| if cfg.reliable_f128 && !no_f16_f128 { | ||
| println!("cargo:rustc-cfg=f128_enabled"); | ||
| } | ||
| } |
+1
-1
@@ -37,3 +37,3 @@ # `libm` | ||
| Contributions are licensed under both the MIT license and the Apache License, | ||
| Version 2.0, available at <htps://www.apache.org/licenses/LICENSE-2.0>. Unless | ||
| Version 2.0, available at <https://www.apache.org/licenses/LICENSE-2.0>. Unless | ||
| you explicitly state otherwise, any contribution intentionally submitted for | ||
@@ -40,0 +40,0 @@ inclusion in the work by you, as defined in the Apache-2.0 license, shall be |
@@ -171,3 +171,3 @@ use core::marker::PhantomData; | ||
| (fn round(x: f64) -> (f64); => round); | ||
| (fn roundevem(x: f64) -> (f64); => roundeven); | ||
| (fn roundeven(x: f64) -> (f64); => roundeven); | ||
| (fn scalbn(x: f64, n: i32) -> (f64); => scalbn); | ||
@@ -174,0 +174,0 @@ (fn sin(x: f64) -> (f64); => sin); |
+1
-1
@@ -62,3 +62,3 @@ /* origin: FreeBSD /usr/src/lib/msun/src/e_acos.c */ | ||
| /// Returns values in radians, in the range of 0 to pi. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn acos(x: f64) -> f64 { | ||
@@ -65,0 +65,0 @@ let x1p_120f = f64::from_bits(0x3870000000000000); // 0x1p-120 === 2 ^ -120 |
@@ -36,3 +36,3 @@ /* origin: FreeBSD /usr/src/lib/msun/src/e_acosf.c */ | ||
| /// Returns values in radians, in the range of 0 to pi. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn acosf(x: f32) -> f32 { | ||
@@ -39,0 +39,0 @@ let x1p_120 = f32::from_bits(0x03800000); // 0x1p-120 === 2 ^ (-120) |
@@ -10,3 +10,3 @@ use super::{log, log1p, sqrt}; | ||
| /// `x` must be a number greater than or equal to 1. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn acosh(x: f64) -> f64 { | ||
@@ -13,0 +13,0 @@ let u = x.to_bits(); |
@@ -10,3 +10,3 @@ use super::{log1pf, logf, sqrtf}; | ||
| /// `x` must be a number greater than or equal to 1. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn acoshf(x: f32) -> f32 { | ||
@@ -13,0 +13,0 @@ let u = x.to_bits(); |
@@ -33,2 +33,8 @@ //! Architecture-specific support for aarch64 with neon. | ||
| // NB: `frintx` is technically the correct instruction for C's `rint`. However, in Rust (and LLVM | ||
| // by default), `rint` is identical to `roundeven` (no fpenv interaction) so we use the | ||
| // side-effect-free `frintn`. | ||
| // | ||
| // In general, C code that calls Rust's libm should assume that fpenv is ignored. | ||
| pub fn rint(mut x: f64) -> f64 { | ||
@@ -35,0 +41,0 @@ // SAFETY: `frintn` is available with neon and has no side effects. |
+114
-30
| //! Architecture-specific support for x86-32 without SSE2 | ||
| //! | ||
| //! We use an alternative implementation on x86, because the | ||
| //! main implementation fails with the x87 FPU used by | ||
| //! debian i386, probably due to excess precision issues. | ||
| //! | ||
| //! See https://github.com/rust-lang/compiler-builtins/pull/976 for discussion on why these | ||
| //! functions are implemented in this way. | ||
| use super::super::fabs; | ||
| /// Use an alternative implementation on x86, because the | ||
| /// main implementation fails with the x87 FPU used by | ||
| /// debian i386, probably due to excess precision issues. | ||
| /// Basic implementation taken from https://github.com/rust-lang/libm/issues/219. | ||
| pub fn ceil(x: f64) -> f64 { | ||
| if fabs(x).to_bits() < 4503599627370496.0_f64.to_bits() { | ||
| let truncated = x as i64 as f64; | ||
| if truncated < x { | ||
| return truncated + 1.0; | ||
| } else { | ||
| return truncated; | ||
| } | ||
| } else { | ||
| return x; | ||
| pub fn ceil(mut x: f64) -> f64 { | ||
| unsafe { | ||
| core::arch::asm!( | ||
| "fld qword ptr [{x}]", | ||
| // Save the FPU control word, using `x` as scratch space. | ||
| "fstcw [{x}]", | ||
| // Set rounding control to 0b10 (+∞). | ||
| "mov word ptr [{x} + 2], 0x0b7f", | ||
| "fldcw [{x} + 2]", | ||
| // Round. | ||
| "frndint", | ||
| // Restore FPU control word. | ||
| "fldcw [{x}]", | ||
| // Save rounded value to memory. | ||
| "fstp qword ptr [{x}]", | ||
| x = in(reg) &mut x, | ||
| // All the x87 FPU stack is used, all registers must be clobbered | ||
| out("st(0)") _, out("st(1)") _, | ||
| out("st(2)") _, out("st(3)") _, | ||
| out("st(4)") _, out("st(5)") _, | ||
| out("st(6)") _, out("st(7)") _, | ||
| options(nostack), | ||
| ); | ||
| } | ||
| x | ||
| } | ||
| /// Use an alternative implementation on x86, because the | ||
| /// main implementation fails with the x87 FPU used by | ||
| /// debian i386, probably due to excess precision issues. | ||
| /// Basic implementation taken from https://github.com/rust-lang/libm/issues/219. | ||
| pub fn floor(x: f64) -> f64 { | ||
| if fabs(x).to_bits() < 4503599627370496.0_f64.to_bits() { | ||
| let truncated = x as i64 as f64; | ||
| if truncated > x { | ||
| return truncated - 1.0; | ||
| } else { | ||
| return truncated; | ||
| } | ||
| } else { | ||
| return x; | ||
| pub fn floor(mut x: f64) -> f64 { | ||
| unsafe { | ||
| core::arch::asm!( | ||
| "fld qword ptr [{x}]", | ||
| // Save the FPU control word, using `x` as scratch space. | ||
| "fstcw [{x}]", | ||
| // Set rounding control to 0b01 (-∞). | ||
| "mov word ptr [{x} + 2], 0x077f", | ||
| "fldcw [{x} + 2]", | ||
| // Round. | ||
| "frndint", | ||
| // Restore FPU control word. | ||
| "fldcw [{x}]", | ||
| // Save rounded value to memory. | ||
| "fstp qword ptr [{x}]", | ||
| x = in(reg) &mut x, | ||
| // All the x87 FPU stack is used, all registers must be clobbered | ||
| out("st(0)") _, out("st(1)") _, | ||
| out("st(2)") _, out("st(3)") _, | ||
| out("st(4)") _, out("st(5)") _, | ||
| out("st(6)") _, out("st(7)") _, | ||
| options(nostack), | ||
| ); | ||
| } | ||
| x | ||
| } | ||
| /// Implements the exponential functions with `x87` assembly. | ||
| /// | ||
| /// This relies on the instruction `f2xm1`, which computes `2^x - 1` (for | ||
| /// |x| < 1). This transcendental instruction is documented to produce results | ||
| /// with error below 1ulp (in the native double-extended precision format). This | ||
| /// translates to correctly rounded results for f32, but results in f64 may have | ||
| /// 1ulp error, which may depend on the hardware. | ||
| macro_rules! x87exp { | ||
| ($float_ty:ident, $word_size:literal, $fn_name:ident, $load_op:literal) => { | ||
| pub fn $fn_name(mut x: $float_ty) -> $float_ty { unsafe { | ||
| core::arch::asm!( | ||
| // Prepare the register stack as | ||
| // ``` | ||
| // st(0) = y = x*log2(base) | ||
| // st(1) = 1.0 | ||
| // st(2) = round(y) | ||
| // ``` | ||
| concat!($load_op, " ", $word_size, " ptr [{x}]"), | ||
| "fld1", | ||
| "fld st(1)", | ||
| "frndint", | ||
| "fxch st(2)", | ||
| // Compare y with round(y) to determine if y is finite and | ||
| // not an integer. If so, compute `exp2(y - round(y))` into | ||
| // st(1). Otherwise skip ahead with `st(1) = 1.0` | ||
| "fucom st(2)", | ||
| "fstsw ax", | ||
| "test ax, 0x4000", | ||
| "jnz 2f", | ||
| "fsub st(0), st(2)", // st(0) = y - round(y) | ||
| "f2xm1", // st(0) = 2^st(0) - 1.0 | ||
| "fadd st(1), st(0)", // st(1) = 1 + st(0) = exp2(y - round(y)) | ||
| "2:", | ||
| // Finally, scale by `exp2(round(y))` and clear the stack. | ||
| "fstp st(0)", | ||
| "fscale", | ||
| concat!("fstp ", $word_size, " ptr [{x}]"), | ||
| "fstp st(0)", | ||
| x = in(reg) &mut x, | ||
| out("ax") _, | ||
| out("st(0)") _, out("st(1)") _, | ||
| out("st(2)") _, out("st(3)") _, | ||
| out("st(4)") _, out("st(5)") _, | ||
| out("st(6)") _, out("st(7)") _, | ||
| options(nostack), | ||
| ); | ||
| x | ||
| }} | ||
| }; | ||
| } | ||
| x87exp!(f32, "dword", x87_exp2f, "fld"); | ||
| x87exp!(f64, "qword", x87_exp2, "fld"); | ||
| x87exp!(f32, "dword", x87_exp10f, "fldl2t\nfmul"); | ||
| x87exp!(f64, "qword", x87_exp10, "fldl2t\nfmul"); | ||
| x87exp!(f32, "dword", x87_expf, "fldl2e\nfmul"); | ||
| x87exp!(f64, "qword", x87_exp, "fldl2e\nfmul"); |
@@ -51,1 +51,6 @@ //! Architecture-specific routines and operations. | ||
| } | ||
| cfg_if! { | ||
| if #[cfg(x86_no_sse)] { | ||
| pub use i586::{x87_exp10f, x87_exp10, x87_expf, x87_exp, x87_exp2f, x87_exp2}; | ||
| } | ||
| } |
@@ -42,2 +42,4 @@ // Using runtime feature detection requires atomics. Currently there are no x86 targets | ||
| /// [std-detect]: https://github.com/rust-lang/stdarch/blob/690b3a6334d482874163bd6fcef408e0518febe9/crates/std_detect/src/detect/os/x86.rs#L142 | ||
| // FIXME(msrv): Remove unsafe block around __cpuid once https://github.com/rust-lang/stdarch/pull/1935 is available in MSRV. | ||
| #[allow(unused_unsafe)] | ||
| fn load_x86_features() -> Flags { | ||
@@ -44,0 +46,0 @@ let mut value = Flags::empty(); |
+1
-1
@@ -69,3 +69,3 @@ /* origin: FreeBSD /usr/src/lib/msun/src/e_asin.c */ | ||
| /// Returns values in radians, in the range of -pi/2 to pi/2. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn asin(mut x: f64) -> f64 { | ||
@@ -72,0 +72,0 @@ let z: f64; |
@@ -38,3 +38,3 @@ /* origin: FreeBSD /usr/src/lib/msun/src/e_asinf.c */ | ||
| /// Returns values in radians, in the range of -pi/2 to pi/2. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn asinf(mut x: f32) -> f32 { | ||
@@ -41,0 +41,0 @@ let x1p_120 = f64::from_bits(0x3870000000000000); // 0x1p-120 === 2 ^ (-120) |
@@ -10,3 +10,3 @@ use super::{log, log1p, sqrt}; | ||
| /// Is defined as `sgn(x)*log(|x|+sqrt(x*x+1))`. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn asinh(mut x: f64) -> f64 { | ||
@@ -13,0 +13,0 @@ let mut u = x.to_bits(); |
@@ -10,3 +10,3 @@ use super::{log1pf, logf, sqrtf}; | ||
| /// Is defined as `sgn(x)*log(|x|+sqrt(x*x+1))`. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn asinhf(mut x: f32) -> f32 { | ||
@@ -13,0 +13,0 @@ let u = x.to_bits(); |
+10
-12
@@ -32,4 +32,2 @@ /* origin: FreeBSD /usr/src/lib/msun/src/s_atan.c */ | ||
| use core::f64; | ||
| use super::fabs; | ||
@@ -69,3 +67,3 @@ | ||
| /// Returns a value in radians, in the range of -pi/2 to pi/2. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn atan(x: f64) -> f64 { | ||
@@ -139,3 +137,3 @@ let mut x = x; | ||
| mod tests { | ||
| use core::f64; | ||
| use core::f64::consts; | ||
@@ -147,8 +145,8 @@ use super::atan; | ||
| for (input, answer) in [ | ||
| (3.0_f64.sqrt() / 3.0, f64::consts::FRAC_PI_6), | ||
| (1.0, f64::consts::FRAC_PI_4), | ||
| (3.0_f64.sqrt(), f64::consts::FRAC_PI_3), | ||
| (-3.0_f64.sqrt() / 3.0, -f64::consts::FRAC_PI_6), | ||
| (-1.0, -f64::consts::FRAC_PI_4), | ||
| (-3.0_f64.sqrt(), -f64::consts::FRAC_PI_3), | ||
| (3.0_f64.sqrt() / 3.0, consts::FRAC_PI_6), | ||
| (1.0, consts::FRAC_PI_4), | ||
| (3.0_f64.sqrt(), consts::FRAC_PI_3), | ||
| (-3.0_f64.sqrt() / 3.0, -consts::FRAC_PI_6), | ||
| (-1.0, -consts::FRAC_PI_4), | ||
| (-3.0_f64.sqrt(), -consts::FRAC_PI_3), | ||
| ] | ||
@@ -174,3 +172,3 @@ .iter() | ||
| fn infinity() { | ||
| assert_eq!(atan(f64::INFINITY), f64::consts::FRAC_PI_2); | ||
| assert_eq!(atan(f64::INFINITY), consts::FRAC_PI_2); | ||
| } | ||
@@ -180,3 +178,3 @@ | ||
| fn minus_infinity() { | ||
| assert_eq!(atan(f64::NEG_INFINITY), -f64::consts::FRAC_PI_2); | ||
| assert_eq!(atan(f64::NEG_INFINITY), -consts::FRAC_PI_2); | ||
| } | ||
@@ -183,0 +181,0 @@ |
@@ -50,3 +50,3 @@ /* origin: FreeBSD /usr/src/lib/msun/src/e_atan2.c */ | ||
| /// Returns a value in radians, in the range of -pi to pi. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn atan2(y: f64, x: f64) -> f64 { | ||
@@ -53,0 +53,0 @@ if x.is_nan() || y.is_nan() { |
@@ -26,3 +26,3 @@ /* origin: FreeBSD /usr/src/lib/msun/src/e_atan2f.c */ | ||
| /// Returns a value in radians, in the range of -pi to pi. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn atan2f(y: f32, x: f32) -> f32 { | ||
@@ -29,0 +29,0 @@ if x.is_nan() || y.is_nan() { |
@@ -44,3 +44,3 @@ /* origin: FreeBSD /usr/src/lib/msun/src/s_atanf.c */ | ||
| /// Returns a value in radians, in the range of -pi/2 to pi/2. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn atanf(mut x: f32) -> f32 { | ||
@@ -47,0 +47,0 @@ let x1p_120 = f32::from_bits(0x03800000); // 0x1p-120 === 2 ^ (-120) |
@@ -8,3 +8,3 @@ use super::log1p; | ||
| /// Is defined as `log((1+x)/(1-x))/2 = log1p(2x/(1-x))/2`. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn atanh(x: f64) -> f64 { | ||
@@ -11,0 +11,0 @@ let u = x.to_bits(); |
@@ -8,3 +8,3 @@ use super::log1pf; | ||
| /// Is defined as `log((1+x)/(1-x))/2 = log1p(2x/(1-x))/2`. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn atanhf(mut x: f32) -> f32 { | ||
@@ -11,0 +11,0 @@ let mut u = x.to_bits(); |
+1
-1
@@ -11,3 +11,3 @@ /* SPDX-License-Identifier: MIT */ | ||
| /// Compute the cube root of the argument. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn cbrt(x: f64) -> f64 { | ||
@@ -14,0 +14,0 @@ cbrt_round(x, Round::Nearest).val |
@@ -20,4 +20,2 @@ /* origin: FreeBSD /usr/src/lib/msun/src/s_cbrtf.c */ | ||
| use core::f32; | ||
| const B1: u32 = 709958130; /* B1 = (127-127.0/3-0.03306235651)*2**23 */ | ||
@@ -29,3 +27,3 @@ const B2: u32 = 642849266; /* B2 = (127-127.0/3-24/3-0.03306235651)*2**23 */ | ||
| /// Computes the cube root of the argument. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn cbrtf(x: f32) -> f32 { | ||
@@ -32,0 +30,0 @@ let x1p24 = f32::from_bits(0x4b800000); // 0x1p24f === 2 ^ 24 |
+4
-4
@@ -5,3 +5,3 @@ /// Ceil (f16) | ||
| #[cfg(f16_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn ceilf16(x: f16) -> f16 { | ||
@@ -14,3 +14,3 @@ super::generic::ceil(x) | ||
| /// Finds the nearest integer greater than or equal to `x`. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn ceilf(x: f32) -> f32 { | ||
@@ -29,3 +29,3 @@ select_implementation! { | ||
| /// Finds the nearest integer greater than or equal to `x`. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn ceil(x: f64) -> f64 { | ||
@@ -46,5 +46,5 @@ select_implementation! { | ||
| #[cfg(f128_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn ceilf128(x: f128) -> f128 { | ||
| super::generic::ceil(x) | ||
| } |
+13
-5
@@ -6,3 +6,3 @@ /// Sign of Y, magnitude of X (f16) | ||
| #[cfg(f16_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn copysignf16(x: f16, y: f16) -> f16 { | ||
@@ -16,3 +16,3 @@ super::generic::copysign(x, y) | ||
| /// first argument, `x`, and the sign of its second argument, `y`. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn copysignf(x: f32, y: f32) -> f32 { | ||
@@ -26,3 +26,3 @@ super::generic::copysign(x, y) | ||
| /// first argument, `x`, and the sign of its second argument, `y`. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn copysign(x: f64, y: f64) -> f64 { | ||
@@ -37,3 +37,3 @@ super::generic::copysign(x, y) | ||
| #[cfg(f128_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn copysignf128(x: f128, y: f128) -> f128 { | ||
@@ -66,5 +66,13 @@ super::generic::copysign(x, y) | ||
| assert_biteq!(f(F::NAN, F::NAN), F::NAN); | ||
| assert_biteq!(f(F::NAN, F::ONE), F::NAN); | ||
| assert_biteq!(f(F::NAN, F::NEG_ONE), F::NEG_NAN); | ||
| assert_biteq!(f(F::NAN, F::NEG_NAN), F::NEG_NAN); | ||
| assert_biteq!(f(F::NEG_NAN, F::NAN), F::NAN); | ||
| assert_biteq!(f(F::NAN, F::NEG_NAN), F::NEG_NAN); | ||
| assert_biteq!(f(F::NEG_NAN, F::ONE), F::NAN); | ||
| assert_biteq!(f(F::NEG_NAN, F::NEG_ONE), F::NEG_NAN); | ||
| assert_biteq!(f(F::NEG_NAN, F::NEG_NAN), F::NEG_NAN); | ||
| assert_biteq!(f(F::ONE, F::NAN), F::ONE); | ||
| assert_biteq!(f(F::ONE, F::NEG_NAN), F::NEG_ONE); | ||
| assert_biteq!(f(F::NEG_ONE, F::NAN), F::ONE); | ||
| assert_biteq!(f(F::NEG_ONE, F::NEG_NAN), F::NEG_ONE); | ||
| } | ||
@@ -71,0 +79,0 @@ |
+1
-1
@@ -48,3 +48,3 @@ // origin: FreeBSD /usr/src/lib/msun/src/s_cos.c */ | ||
| /// `x` is specified in radians. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn cos(x: f64) -> f64 { | ||
@@ -51,0 +51,0 @@ let ix = (f64::to_bits(x) >> 32) as u32 & 0x7fffffff; |
+1
-1
@@ -30,3 +30,3 @@ /* origin: FreeBSD /usr/src/lib/msun/src/s_cosf.c */ | ||
| /// `x` is specified in radians. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn cosf(x: f32) -> f32 { | ||
@@ -33,0 +33,0 @@ let x64 = x as f64; |
+1
-1
@@ -8,3 +8,3 @@ use super::{exp, expm1, k_expo2}; | ||
| /// Angles are specified in radians. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn cosh(mut x: f64) -> f64 { | ||
@@ -11,0 +11,0 @@ /* |x| */ |
@@ -8,3 +8,3 @@ use super::{expf, expm1f, k_expo2f}; | ||
| /// Angles are specified in radians. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn coshf(mut x: f32) -> f32 { | ||
@@ -11,0 +11,0 @@ let x1p120 = f32::from_bits(0x7b800000); // 0x1p120f === 2 ^ 120 |
+1
-1
@@ -222,3 +222,3 @@ use super::{exp, fabs, get_high_word, with_set_low_word}; | ||
| /// deviations of the mean (assuming a normal distribution). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn erf(x: f64) -> f64 { | ||
@@ -225,0 +225,0 @@ let r: f64; |
+1
-1
@@ -133,3 +133,3 @@ /* origin: FreeBSD /usr/src/lib/msun/src/s_erff.c */ | ||
| /// deviations of the mean (assuming a normal distribution). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn erff(x: f32) -> f32 { | ||
@@ -136,0 +136,0 @@ let r: f32; |
+7
-1
@@ -84,4 +84,10 @@ /* origin: FreeBSD /usr/src/lib/msun/src/e_exp.c */ | ||
| /// (where *e* is the base of the natural system of logarithms, approximately 2.71828). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn exp(mut x: f64) -> f64 { | ||
| select_implementation! { | ||
| name: x87_exp, | ||
| use_arch_required: x86_no_sse, | ||
| args: x, | ||
| } | ||
| let x1p1023 = f64::from_bits(0x7fe0000000000000); // 0x1p1023 === 2 ^ 1023 | ||
@@ -88,0 +94,0 @@ let x1p_149 = f64::from_bits(0x36a0000000000000); // 0x1p-149 === 2 ^ -149 |
@@ -10,4 +10,10 @@ use super::{exp2, modf, pow}; | ||
| /// Calculates 10 raised to the power of `x` (f64). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn exp10(x: f64) -> f64 { | ||
| select_implementation! { | ||
| name: x87_exp10, | ||
| use_arch_required: x86_no_sse, | ||
| args: x, | ||
| } | ||
| let (mut y, n) = modf(x); | ||
@@ -14,0 +20,0 @@ let u: u64 = n.to_bits(); |
@@ -10,4 +10,10 @@ use super::{exp2, exp2f, modff}; | ||
| /// Calculates 10 raised to the power of `x` (f32). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn exp10f(x: f32) -> f32 { | ||
| select_implementation! { | ||
| name: x87_exp10f, | ||
| use_arch_required: x86_no_sse, | ||
| args: x, | ||
| } | ||
| let (mut y, n) = modff(x); | ||
@@ -14,0 +20,0 @@ let u = n.to_bits(); |
+7
-1
@@ -325,4 +325,10 @@ // origin: FreeBSD /usr/src/lib/msun/src/s_exp2.c */ | ||
| /// Calculate `2^x`, that is, 2 raised to the power `x`. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn exp2(mut x: f64) -> f64 { | ||
| select_implementation! { | ||
| name: x87_exp2, | ||
| use_arch_required: x86_no_sse, | ||
| args: x, | ||
| } | ||
| let redux = f64::from_bits(0x4338000000000000) / TBLSIZE as f64; | ||
@@ -329,0 +335,0 @@ let p1 = f64::from_bits(0x3fe62e42fefa39ef); |
@@ -76,4 +76,10 @@ // origin: FreeBSD /usr/src/lib/msun/src/s_exp2f.c | ||
| /// Calculate `2^x`, that is, 2 raised to the power `x`. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn exp2f(mut x: f32) -> f32 { | ||
| select_implementation! { | ||
| name: x87_exp2f, | ||
| use_arch_required: x86_no_sse, | ||
| args: x, | ||
| } | ||
| let redux = f32::from_bits(0x4b400000) / TBLSIZE as f32; | ||
@@ -80,0 +86,0 @@ let p1 = f32::from_bits(0x3f317218); |
+7
-1
@@ -33,4 +33,10 @@ /* origin: FreeBSD /usr/src/lib/msun/src/e_expf.c */ | ||
| /// (where *e* is the base of the natural system of logarithms, approximately 2.71828). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn expf(mut x: f32) -> f32 { | ||
| select_implementation! { | ||
| name: x87_expf, | ||
| use_arch_required: x86_no_sse, | ||
| args: x, | ||
| } | ||
| let x1p127 = f32::from_bits(0x7f000000); // 0x1p127f === 2 ^ 127 | ||
@@ -37,0 +43,0 @@ let x1p_126 = f32::from_bits(0x800000); // 0x1p-126f === 2 ^ -126 /*original 0x1p-149f ??????????? */ |
@@ -13,4 +13,2 @@ /* origin: FreeBSD /usr/src/lib/msun/src/s_expm1.c */ | ||
| use core::f64; | ||
| const O_THRESHOLD: f64 = 7.09782712893383973096e+02; /* 0x40862E42, 0xFEFA39EF */ | ||
@@ -34,3 +32,3 @@ const LN2_HI: f64 = 6.93147180369123816490e-01; /* 0x3fe62e42, 0xfee00000 */ | ||
| /// where using `exp(x)-1` would lose many significant digits. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn expm1(mut x: f64) -> f64 { | ||
@@ -37,0 +35,0 @@ let hi: f64; |
@@ -16,3 +16,2 @@ /* origin: FreeBSD /usr/src/lib/msun/src/s_expm1f.c */ | ||
| const O_THRESHOLD: f32 = 8.8721679688e+01; /* 0x42b17180 */ | ||
| const LN2_HI: f32 = 6.9313812256e-01; /* 0x3f317180 */ | ||
@@ -36,3 +35,3 @@ const LN2_LO: f32 = 9.0580006145e-06; /* 0x3717f7d1 */ | ||
| /// where using `exp(x)-1` would lose many significant digits. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn expm1f(mut x: f32) -> f32 { | ||
@@ -55,3 +54,4 @@ let x1p127 = f32::from_bits(0x7f000000); // 0x1p127f === 2 ^ 127 | ||
| } | ||
| if x > O_THRESHOLD { | ||
| if hx > 0x42b17217 { | ||
| /* x > log(FLT_MAX) */ | ||
| x *= x1p127; | ||
@@ -58,0 +58,0 @@ return x; |
| use super::{combine_words, exp}; | ||
| /* exp(x)/2 for x >= log(DBL_MAX), slightly better than 0.5*exp(x/2)*exp(x/2) */ | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub(crate) fn expo2(x: f64) -> f64 { | ||
@@ -6,0 +6,0 @@ /* k is such that k*ln2 has minimal relative error and x - kln2 > log(DBL_MIN) */ |
+4
-4
@@ -6,3 +6,3 @@ /// Absolute value (magnitude) (f16) | ||
| #[cfg(f16_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fabsf16(x: f16) -> f16 { | ||
@@ -16,3 +16,3 @@ super::generic::fabs(x) | ||
| /// by direct manipulation of the bit representation of `x`. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fabsf(x: f32) -> f32 { | ||
@@ -32,3 +32,3 @@ select_implementation! { | ||
| /// by direct manipulation of the bit representation of `x`. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fabs(x: f64) -> f64 { | ||
@@ -49,3 +49,3 @@ select_implementation! { | ||
| #[cfg(f128_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fabsf128(x: f128) -> f128 { | ||
@@ -52,0 +52,0 @@ super::generic::fabs(x) |
+4
-4
@@ -10,3 +10,3 @@ /// Positive difference (f16) | ||
| #[cfg(f16_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fdimf16(x: f16, y: f16) -> f16 { | ||
@@ -24,3 +24,3 @@ super::generic::fdim(x, y) | ||
| /// A range error may occur. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fdimf(x: f32, y: f32) -> f32 { | ||
@@ -38,3 +38,3 @@ super::generic::fdim(x, y) | ||
| /// A range error may occur. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fdim(x: f64, y: f64) -> f64 { | ||
@@ -53,5 +53,5 @@ super::generic::fdim(x, y) | ||
| #[cfg(f128_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fdimf128(x: f128, y: f128) -> f128 { | ||
| super::generic::fdim(x, y) | ||
| } |
@@ -5,3 +5,3 @@ /// Floor (f16) | ||
| #[cfg(f16_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn floorf16(x: f16) -> f16 { | ||
@@ -14,3 +14,3 @@ return super::generic::floor(x); | ||
| /// Finds the nearest integer less than or equal to `x`. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn floor(x: f64) -> f64 { | ||
@@ -30,3 +30,3 @@ select_implementation! { | ||
| /// Finds the nearest integer less than or equal to `x`. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn floorf(x: f32) -> f32 { | ||
@@ -46,5 +46,5 @@ select_implementation! { | ||
| #[cfg(f128_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn floorf128(x: f128) -> f128 { | ||
| return super::generic::floor(x); | ||
| } |
+4
-4
@@ -10,3 +10,3 @@ /* SPDX-License-Identifier: MIT */ | ||
| #[cfg(f16_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub(crate) fn fmaf16(_x: f16, _y: f16, _z: f16) -> f16 { | ||
@@ -19,3 +19,3 @@ unimplemented!() | ||
| /// Computes `(x*y)+z`, rounded as one ternary operation (i.e. calculated with infinite precision). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fmaf(x: f32, y: f32, z: f32) -> f32 { | ||
@@ -37,3 +37,3 @@ select_implementation! { | ||
| /// Computes `(x*y)+z`, rounded as one ternary operation (i.e. calculated with infinite precision). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fma(x: f64, y: f64, z: f64) -> f64 { | ||
@@ -56,3 +56,3 @@ select_implementation! { | ||
| #[cfg(f128_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fmaf128(x: f128, y: f128, z: f128) -> f128 { | ||
@@ -59,0 +59,0 @@ generic::fma_round(x, y, z, Round::Nearest).val |
+124
-14
@@ -6,3 +6,3 @@ /// Return the lesser of two arguments or, if either argument is NaN, the other argument. | ||
| #[cfg(f16_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fminf16(x: f16, y: f16) -> f16 { | ||
@@ -16,3 +16,3 @@ super::generic::fmin(x, y) | ||
| /// the inputs are -0.0 and +0.0, either may be returned). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fminf(x: f32, y: f32) -> f32 { | ||
@@ -26,3 +26,3 @@ super::generic::fmin(x, y) | ||
| /// the inputs are -0.0 and +0.0, either may be returned). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fmin(x: f64, y: f64) -> f64 { | ||
@@ -37,3 +37,3 @@ super::generic::fmin(x, y) | ||
| #[cfg(f128_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fminf128(x: f128, y: f128) -> f128 { | ||
@@ -48,3 +48,3 @@ super::generic::fmin(x, y) | ||
| #[cfg(f16_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fmaxf16(x: f16, y: f16) -> f16 { | ||
@@ -58,3 +58,3 @@ super::generic::fmax(x, y) | ||
| /// the inputs are -0.0 and +0.0, either may be returned). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fmaxf(x: f32, y: f32) -> f32 { | ||
@@ -68,3 +68,3 @@ super::generic::fmax(x, y) | ||
| /// the inputs are -0.0 and +0.0, either may be returned). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fmax(x: f64, y: f64) -> f64 { | ||
@@ -79,3 +79,3 @@ super::generic::fmax(x, y) | ||
| #[cfg(f128_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fmaxf128(x: f128, y: f128) -> f128 { | ||
@@ -93,12 +93,60 @@ super::generic::fmax(x, y) | ||
| (F::ZERO, F::ZERO, F::ZERO), | ||
| (F::ONE, F::ONE, F::ONE), | ||
| (F::ZERO, F::ONE, F::ZERO), | ||
| (F::ZERO, F::NEG_ONE, F::NEG_ONE), | ||
| (F::ZERO, F::INFINITY, F::ZERO), | ||
| (F::ZERO, F::NEG_INFINITY, F::NEG_INFINITY), | ||
| (F::ZERO, F::NAN, F::ZERO), | ||
| (F::ZERO, F::NEG_NAN, F::ZERO), | ||
| (F::NEG_ZERO, F::NEG_ZERO, F::NEG_ZERO), | ||
| (F::NEG_ZERO, F::ONE, F::NEG_ZERO), | ||
| (F::NEG_ZERO, F::NEG_ONE, F::NEG_ONE), | ||
| (F::NEG_ZERO, F::INFINITY, F::NEG_ZERO), | ||
| (F::NEG_ZERO, F::NEG_INFINITY, F::NEG_INFINITY), | ||
| (F::NEG_ZERO, F::NAN, F::NEG_ZERO), | ||
| (F::NEG_ZERO, F::NEG_NAN, F::NEG_ZERO), | ||
| (F::ONE, F::ZERO, F::ZERO), | ||
| (F::ZERO, F::NEG_ONE, F::NEG_ONE), | ||
| (F::ONE, F::NEG_ZERO, F::NEG_ZERO), | ||
| (F::ONE, F::ONE, F::ONE), | ||
| (F::ONE, F::NEG_ONE, F::NEG_ONE), | ||
| (F::ONE, F::INFINITY, F::ONE), | ||
| (F::ONE, F::NEG_INFINITY, F::NEG_INFINITY), | ||
| (F::ONE, F::NAN, F::ONE), | ||
| (F::ONE, F::NEG_NAN, F::ONE), | ||
| (F::NEG_ONE, F::ZERO, F::NEG_ONE), | ||
| (F::NEG_ONE, F::NEG_ZERO, F::NEG_ONE), | ||
| (F::NEG_ONE, F::ONE, F::NEG_ONE), | ||
| (F::NEG_ONE, F::NEG_ONE, F::NEG_ONE), | ||
| (F::NEG_ONE, F::INFINITY, F::NEG_ONE), | ||
| (F::NEG_ONE, F::NEG_INFINITY, F::NEG_INFINITY), | ||
| (F::NEG_ONE, F::NAN, F::NEG_ONE), | ||
| (F::NEG_ONE, F::NEG_NAN, F::NEG_ONE), | ||
| (F::INFINITY, F::ZERO, F::ZERO), | ||
| (F::INFINITY, F::NEG_ZERO, F::NEG_ZERO), | ||
| (F::INFINITY, F::ONE, F::ONE), | ||
| (F::INFINITY, F::NEG_ONE, F::NEG_ONE), | ||
| (F::INFINITY, F::INFINITY, F::INFINITY), | ||
| (F::INFINITY, F::NEG_INFINITY, F::NEG_INFINITY), | ||
| (F::INFINITY, F::NAN, F::INFINITY), | ||
| (F::INFINITY, F::NEG_NAN, F::INFINITY), | ||
| (F::NEG_INFINITY, F::ZERO, F::NEG_INFINITY), | ||
| (F::NEG_INFINITY, F::NEG_ZERO, F::NEG_INFINITY), | ||
| (F::NEG_INFINITY, F::ONE, F::NEG_INFINITY), | ||
| (F::NEG_INFINITY, F::NEG_ONE, F::NEG_INFINITY), | ||
| (F::NEG_INFINITY, F::INFINITY, F::NEG_INFINITY), | ||
| (F::NEG_INFINITY, F::NEG_INFINITY, F::NEG_INFINITY), | ||
| (F::NEG_INFINITY, F::NAN, F::NEG_INFINITY), | ||
| (F::NEG_INFINITY, F::NEG_NAN, F::NEG_INFINITY), | ||
| (F::NAN, F::ZERO, F::ZERO), | ||
| (F::ZERO, F::NAN, F::ZERO), | ||
| (F::NAN, F::NEG_ZERO, F::NEG_ZERO), | ||
| (F::NAN, F::ONE, F::ONE), | ||
| (F::NAN, F::NEG_ONE, F::NEG_ONE), | ||
| (F::NAN, F::INFINITY, F::INFINITY), | ||
| (F::NAN, F::NEG_INFINITY, F::NEG_INFINITY), | ||
| (F::NAN, F::NAN, F::NAN), | ||
| (F::NEG_NAN, F::ZERO, F::ZERO), | ||
| (F::NEG_NAN, F::NEG_ZERO, F::NEG_ZERO), | ||
| (F::NEG_NAN, F::ONE, F::ONE), | ||
| (F::NEG_NAN, F::NEG_ONE, F::NEG_ONE), | ||
| (F::NEG_NAN, F::INFINITY, F::INFINITY), | ||
| (F::NEG_NAN, F::NEG_INFINITY, F::NEG_INFINITY), | ||
| ]; | ||
@@ -110,2 +158,9 @@ | ||
| } | ||
| // Ordering between zeros and NaNs does not matter | ||
| assert_eq!(f(F::ZERO, F::NEG_ZERO), F::ZERO); | ||
| assert_eq!(f(F::NEG_ZERO, F::ZERO), F::ZERO); | ||
| assert!(f(F::NAN, F::NEG_NAN).is_nan()); | ||
| assert!(f(F::NEG_NAN, F::NAN).is_nan()); | ||
| assert!(f(F::NEG_NAN, F::NEG_NAN).is_nan()); | ||
| } | ||
@@ -138,12 +193,60 @@ | ||
| (F::ZERO, F::ZERO, F::ZERO), | ||
| (F::ONE, F::ONE, F::ONE), | ||
| (F::ZERO, F::ONE, F::ONE), | ||
| (F::ZERO, F::NEG_ONE, F::ZERO), | ||
| (F::ZERO, F::INFINITY, F::INFINITY), | ||
| (F::ZERO, F::NEG_INFINITY, F::ZERO), | ||
| (F::ZERO, F::NAN, F::ZERO), | ||
| (F::ZERO, F::NEG_NAN, F::ZERO), | ||
| (F::NEG_ZERO, F::NEG_ZERO, F::NEG_ZERO), | ||
| (F::NEG_ZERO, F::ONE, F::ONE), | ||
| (F::NEG_ZERO, F::NEG_ONE, F::NEG_ZERO), | ||
| (F::NEG_ZERO, F::INFINITY, F::INFINITY), | ||
| (F::NEG_ZERO, F::NEG_INFINITY, F::NEG_ZERO), | ||
| (F::NEG_ZERO, F::NAN, F::NEG_ZERO), | ||
| (F::NEG_ZERO, F::NEG_NAN, F::NEG_ZERO), | ||
| (F::ONE, F::ZERO, F::ONE), | ||
| (F::ZERO, F::NEG_ONE, F::ZERO), | ||
| (F::ONE, F::NEG_ZERO, F::ONE), | ||
| (F::ONE, F::ONE, F::ONE), | ||
| (F::ONE, F::NEG_ONE, F::ONE), | ||
| (F::ONE, F::INFINITY, F::INFINITY), | ||
| (F::ONE, F::NEG_INFINITY, F::ONE), | ||
| (F::ONE, F::NAN, F::ONE), | ||
| (F::ONE, F::NEG_NAN, F::ONE), | ||
| (F::NEG_ONE, F::ZERO, F::ZERO), | ||
| (F::NEG_ONE, F::NEG_ZERO, F::NEG_ZERO), | ||
| (F::NEG_ONE, F::ONE, F::ONE), | ||
| (F::NEG_ONE, F::NEG_ONE, F::NEG_ONE), | ||
| (F::NEG_ONE, F::INFINITY, F::INFINITY), | ||
| (F::NEG_ONE, F::NEG_INFINITY, F::NEG_ONE), | ||
| (F::NEG_ONE, F::NAN, F::NEG_ONE), | ||
| (F::NEG_ONE, F::NEG_NAN, F::NEG_ONE), | ||
| (F::INFINITY, F::ZERO, F::INFINITY), | ||
| (F::INFINITY, F::NEG_ZERO, F::INFINITY), | ||
| (F::INFINITY, F::ONE, F::INFINITY), | ||
| (F::INFINITY, F::NEG_ONE, F::INFINITY), | ||
| (F::INFINITY, F::INFINITY, F::INFINITY), | ||
| (F::INFINITY, F::NEG_INFINITY, F::INFINITY), | ||
| (F::INFINITY, F::NAN, F::INFINITY), | ||
| (F::INFINITY, F::NEG_NAN, F::INFINITY), | ||
| (F::NEG_INFINITY, F::ZERO, F::ZERO), | ||
| (F::NEG_INFINITY, F::NEG_ZERO, F::NEG_ZERO), | ||
| (F::NEG_INFINITY, F::ONE, F::ONE), | ||
| (F::NEG_INFINITY, F::NEG_ONE, F::NEG_ONE), | ||
| (F::NEG_INFINITY, F::INFINITY, F::INFINITY), | ||
| (F::NEG_INFINITY, F::NEG_INFINITY, F::NEG_INFINITY), | ||
| (F::NEG_INFINITY, F::NAN, F::NEG_INFINITY), | ||
| (F::NEG_INFINITY, F::NEG_NAN, F::NEG_INFINITY), | ||
| (F::NAN, F::ZERO, F::ZERO), | ||
| (F::ZERO, F::NAN, F::ZERO), | ||
| (F::NAN, F::NEG_ZERO, F::NEG_ZERO), | ||
| (F::NAN, F::ONE, F::ONE), | ||
| (F::NAN, F::NEG_ONE, F::NEG_ONE), | ||
| (F::NAN, F::INFINITY, F::INFINITY), | ||
| (F::NAN, F::NEG_INFINITY, F::NEG_INFINITY), | ||
| (F::NAN, F::NAN, F::NAN), | ||
| (F::NEG_NAN, F::ZERO, F::ZERO), | ||
| (F::NEG_NAN, F::NEG_ZERO, F::NEG_ZERO), | ||
| (F::NEG_NAN, F::ONE, F::ONE), | ||
| (F::NEG_NAN, F::NEG_ONE, F::NEG_ONE), | ||
| (F::NEG_NAN, F::INFINITY, F::INFINITY), | ||
| (F::NEG_NAN, F::NEG_INFINITY, F::NEG_INFINITY), | ||
| ]; | ||
@@ -155,2 +258,9 @@ | ||
| } | ||
| // Ordering between zeros and NaNs does not matter | ||
| assert_eq!(f(F::ZERO, F::NEG_ZERO), F::ZERO); | ||
| assert_eq!(f(F::NEG_ZERO, F::ZERO), F::ZERO); | ||
| assert!(f(F::NAN, F::NEG_NAN).is_nan()); | ||
| assert!(f(F::NEG_NAN, F::NAN).is_nan()); | ||
| assert!(f(F::NEG_NAN, F::NEG_NAN).is_nan()); | ||
| } | ||
@@ -157,0 +267,0 @@ |
@@ -5,3 +5,3 @@ /// Return the lesser of two arguments or, if either argument is NaN, NaN. | ||
| #[cfg(f16_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fminimum_numf16(x: f16, y: f16) -> f16 { | ||
@@ -14,3 +14,3 @@ super::generic::fminimum_num(x, y) | ||
| /// This coincides with IEEE 754-2019 `minimumNumber`. The result orders -0.0 < 0.0. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fminimum_numf(x: f32, y: f32) -> f32 { | ||
@@ -23,3 +23,3 @@ super::generic::fminimum_num(x, y) | ||
| /// This coincides with IEEE 754-2019 `minimumNumber`. The result orders -0.0 < 0.0. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fminimum_num(x: f64, y: f64) -> f64 { | ||
@@ -33,3 +33,3 @@ super::generic::fminimum_num(x, y) | ||
| #[cfg(f128_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fminimum_numf128(x: f128, y: f128) -> f128 { | ||
@@ -43,3 +43,3 @@ super::generic::fminimum_num(x, y) | ||
| #[cfg(f16_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fmaximum_numf16(x: f16, y: f16) -> f16 { | ||
@@ -52,3 +52,3 @@ super::generic::fmaximum_num(x, y) | ||
| /// This coincides with IEEE 754-2019 `maximumNumber`. The result orders -0.0 < 0.0. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fmaximum_numf(x: f32, y: f32) -> f32 { | ||
@@ -61,3 +61,3 @@ super::generic::fmaximum_num(x, y) | ||
| /// This coincides with IEEE 754-2019 `maximumNumber`. The result orders -0.0 < 0.0. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fmaximum_num(x: f64, y: f64) -> f64 { | ||
@@ -71,3 +71,3 @@ super::generic::fmaximum_num(x, y) | ||
| #[cfg(f128_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fmaximum_numf128(x: f128, y: f128) -> f128 { | ||
@@ -85,20 +85,73 @@ super::generic::fmaximum_num(x, y) | ||
| (F::ZERO, F::ZERO, F::ZERO), | ||
| (F::ONE, F::ONE, F::ONE), | ||
| (F::ZERO, F::NEG_ZERO, F::NEG_ZERO), | ||
| (F::ZERO, F::ONE, F::ZERO), | ||
| (F::ZERO, F::NEG_ONE, F::NEG_ONE), | ||
| (F::ZERO, F::INFINITY, F::ZERO), | ||
| (F::ZERO, F::NEG_INFINITY, F::NEG_INFINITY), | ||
| (F::ZERO, F::NAN, F::ZERO), | ||
| (F::ZERO, F::NEG_NAN, F::ZERO), | ||
| (F::NEG_ZERO, F::ZERO, F::NEG_ZERO), | ||
| (F::NEG_ZERO, F::NEG_ZERO, F::NEG_ZERO), | ||
| (F::NEG_ZERO, F::ONE, F::NEG_ZERO), | ||
| (F::NEG_ZERO, F::NEG_ONE, F::NEG_ONE), | ||
| (F::NEG_ZERO, F::INFINITY, F::NEG_ZERO), | ||
| (F::NEG_ZERO, F::NEG_INFINITY, F::NEG_INFINITY), | ||
| (F::NEG_ZERO, F::NAN, F::NEG_ZERO), | ||
| (F::NEG_ZERO, F::NEG_NAN, F::NEG_ZERO), | ||
| (F::ONE, F::ZERO, F::ZERO), | ||
| (F::ZERO, F::NEG_ONE, F::NEG_ONE), | ||
| (F::ONE, F::NEG_ZERO, F::NEG_ZERO), | ||
| (F::ONE, F::ONE, F::ONE), | ||
| (F::ONE, F::NEG_ONE, F::NEG_ONE), | ||
| (F::ONE, F::INFINITY, F::ONE), | ||
| (F::ONE, F::NEG_INFINITY, F::NEG_INFINITY), | ||
| (F::ONE, F::NAN, F::ONE), | ||
| (F::ONE, F::NEG_NAN, F::ONE), | ||
| (F::NEG_ONE, F::ZERO, F::NEG_ONE), | ||
| (F::NEG_ONE, F::NEG_ZERO, F::NEG_ONE), | ||
| (F::NEG_ONE, F::ONE, F::NEG_ONE), | ||
| (F::NEG_ONE, F::NEG_ONE, F::NEG_ONE), | ||
| (F::NEG_ONE, F::INFINITY, F::NEG_ONE), | ||
| (F::NEG_ONE, F::NEG_INFINITY, F::NEG_INFINITY), | ||
| (F::NEG_ONE, F::NAN, F::NEG_ONE), | ||
| (F::NEG_ONE, F::NEG_NAN, F::NEG_ONE), | ||
| (F::INFINITY, F::ZERO, F::ZERO), | ||
| (F::INFINITY, F::NEG_ZERO, F::NEG_ZERO), | ||
| (F::INFINITY, F::ONE, F::ONE), | ||
| (F::INFINITY, F::NEG_ONE, F::NEG_ONE), | ||
| (F::INFINITY, F::INFINITY, F::INFINITY), | ||
| (F::INFINITY, F::NEG_INFINITY, F::NEG_INFINITY), | ||
| (F::INFINITY, F::NAN, F::INFINITY), | ||
| (F::INFINITY, F::NEG_NAN, F::INFINITY), | ||
| (F::NEG_INFINITY, F::ZERO, F::NEG_INFINITY), | ||
| (F::NEG_INFINITY, F::NEG_ZERO, F::NEG_INFINITY), | ||
| (F::NEG_INFINITY, F::ONE, F::NEG_INFINITY), | ||
| (F::NEG_INFINITY, F::NEG_ONE, F::NEG_INFINITY), | ||
| (F::NEG_INFINITY, F::INFINITY, F::NEG_INFINITY), | ||
| (F::NEG_INFINITY, F::NEG_INFINITY, F::NEG_INFINITY), | ||
| (F::NEG_INFINITY, F::NAN, F::NEG_INFINITY), | ||
| (F::NEG_INFINITY, F::NEG_NAN, F::NEG_INFINITY), | ||
| (F::NAN, F::ZERO, F::ZERO), | ||
| (F::ZERO, F::NAN, F::ZERO), | ||
| (F::NAN, F::NEG_ZERO, F::NEG_ZERO), | ||
| (F::NAN, F::ONE, F::ONE), | ||
| (F::NAN, F::NEG_ONE, F::NEG_ONE), | ||
| (F::NAN, F::INFINITY, F::INFINITY), | ||
| (F::NAN, F::NEG_INFINITY, F::NEG_INFINITY), | ||
| (F::NAN, F::NAN, F::NAN), | ||
| (F::ZERO, F::NEG_ZERO, F::NEG_ZERO), | ||
| (F::NEG_ZERO, F::ZERO, F::NEG_ZERO), | ||
| (F::NEG_NAN, F::ZERO, F::ZERO), | ||
| (F::NEG_NAN, F::NEG_ZERO, F::NEG_ZERO), | ||
| (F::NEG_NAN, F::ONE, F::ONE), | ||
| (F::NEG_NAN, F::NEG_ONE, F::NEG_ONE), | ||
| (F::NEG_NAN, F::INFINITY, F::INFINITY), | ||
| (F::NEG_NAN, F::NEG_INFINITY, F::NEG_INFINITY), | ||
| ]; | ||
| for (x, y, res) in cases { | ||
| let val = f(x, y); | ||
| assert_biteq!(val, res, "fminimum_num({}, {})", Hexf(x), Hexf(y)); | ||
| for (x, y, expected) in cases { | ||
| let actual = f(x, y); | ||
| assert_biteq!(actual, expected, "fminimum_num({}, {})", Hexf(x), Hexf(y)); | ||
| } | ||
| // Ordering between NaNs does not matter | ||
| assert!(f(F::NAN, F::NEG_NAN).is_nan()); | ||
| assert!(f(F::NEG_NAN, F::NAN).is_nan()); | ||
| assert!(f(F::NEG_NAN, F::NEG_NAN).is_nan()); | ||
| } | ||
@@ -131,20 +184,73 @@ | ||
| (F::ZERO, F::ZERO, F::ZERO), | ||
| (F::ONE, F::ONE, F::ONE), | ||
| (F::ZERO, F::NEG_ZERO, F::ZERO), | ||
| (F::ZERO, F::ONE, F::ONE), | ||
| (F::ZERO, F::NEG_ONE, F::ZERO), | ||
| (F::ZERO, F::INFINITY, F::INFINITY), | ||
| (F::ZERO, F::NEG_INFINITY, F::ZERO), | ||
| (F::ZERO, F::NAN, F::ZERO), | ||
| (F::ZERO, F::NEG_NAN, F::ZERO), | ||
| (F::NEG_ZERO, F::ZERO, F::ZERO), | ||
| (F::NEG_ZERO, F::NEG_ZERO, F::NEG_ZERO), | ||
| (F::NEG_ZERO, F::ONE, F::ONE), | ||
| (F::NEG_ZERO, F::NEG_ONE, F::NEG_ZERO), | ||
| (F::NEG_ZERO, F::INFINITY, F::INFINITY), | ||
| (F::NEG_ZERO, F::NEG_INFINITY, F::NEG_ZERO), | ||
| (F::NEG_ZERO, F::NAN, F::NEG_ZERO), | ||
| (F::NEG_ZERO, F::NEG_NAN, F::NEG_ZERO), | ||
| (F::ONE, F::ZERO, F::ONE), | ||
| (F::ZERO, F::NEG_ONE, F::ZERO), | ||
| (F::ONE, F::NEG_ZERO, F::ONE), | ||
| (F::ONE, F::ONE, F::ONE), | ||
| (F::ONE, F::NEG_ONE, F::ONE), | ||
| (F::ONE, F::INFINITY, F::INFINITY), | ||
| (F::ONE, F::NEG_INFINITY, F::ONE), | ||
| (F::ONE, F::NAN, F::ONE), | ||
| (F::ONE, F::NEG_NAN, F::ONE), | ||
| (F::NEG_ONE, F::ZERO, F::ZERO), | ||
| (F::NEG_ONE, F::NEG_ZERO, F::NEG_ZERO), | ||
| (F::NEG_ONE, F::ONE, F::ONE), | ||
| (F::NEG_ONE, F::NEG_ONE, F::NEG_ONE), | ||
| (F::NEG_ONE, F::INFINITY, F::INFINITY), | ||
| (F::NEG_ONE, F::NEG_INFINITY, F::NEG_ONE), | ||
| (F::NEG_ONE, F::NAN, F::NEG_ONE), | ||
| (F::NEG_ONE, F::NEG_NAN, F::NEG_ONE), | ||
| (F::INFINITY, F::ZERO, F::INFINITY), | ||
| (F::INFINITY, F::NEG_ZERO, F::INFINITY), | ||
| (F::INFINITY, F::ONE, F::INFINITY), | ||
| (F::INFINITY, F::NEG_ONE, F::INFINITY), | ||
| (F::INFINITY, F::INFINITY, F::INFINITY), | ||
| (F::INFINITY, F::NEG_INFINITY, F::INFINITY), | ||
| (F::INFINITY, F::NAN, F::INFINITY), | ||
| (F::INFINITY, F::NEG_NAN, F::INFINITY), | ||
| (F::NEG_INFINITY, F::ZERO, F::ZERO), | ||
| (F::NEG_INFINITY, F::NEG_ZERO, F::NEG_ZERO), | ||
| (F::NEG_INFINITY, F::ONE, F::ONE), | ||
| (F::NEG_INFINITY, F::NEG_ONE, F::NEG_ONE), | ||
| (F::NEG_INFINITY, F::INFINITY, F::INFINITY), | ||
| (F::NEG_INFINITY, F::NEG_INFINITY, F::NEG_INFINITY), | ||
| (F::NEG_INFINITY, F::NAN, F::NEG_INFINITY), | ||
| (F::NEG_INFINITY, F::NEG_NAN, F::NEG_INFINITY), | ||
| (F::NAN, F::ZERO, F::ZERO), | ||
| (F::ZERO, F::NAN, F::ZERO), | ||
| (F::NAN, F::NEG_ZERO, F::NEG_ZERO), | ||
| (F::NAN, F::ONE, F::ONE), | ||
| (F::NAN, F::NEG_ONE, F::NEG_ONE), | ||
| (F::NAN, F::INFINITY, F::INFINITY), | ||
| (F::NAN, F::NEG_INFINITY, F::NEG_INFINITY), | ||
| (F::NAN, F::NAN, F::NAN), | ||
| (F::ZERO, F::NEG_ZERO, F::ZERO), | ||
| (F::NEG_ZERO, F::ZERO, F::ZERO), | ||
| (F::NEG_NAN, F::ZERO, F::ZERO), | ||
| (F::NEG_NAN, F::NEG_ZERO, F::NEG_ZERO), | ||
| (F::NEG_NAN, F::ONE, F::ONE), | ||
| (F::NEG_NAN, F::NEG_ONE, F::NEG_ONE), | ||
| (F::NEG_NAN, F::INFINITY, F::INFINITY), | ||
| (F::NEG_NAN, F::NEG_INFINITY, F::NEG_INFINITY), | ||
| ]; | ||
| for (x, y, res) in cases { | ||
| let val = f(x, y); | ||
| assert_biteq!(val, res, "fmaximum_num({}, {})", Hexf(x), Hexf(y)); | ||
| for (x, y, expected) in cases { | ||
| let actual = f(x, y); | ||
| assert_biteq!(actual, expected, "fmaximum_num({}, {})", Hexf(x), Hexf(y)); | ||
| } | ||
| // Ordering between NaNs does not matter | ||
| assert!(f(F::NAN, F::NEG_NAN).is_nan()); | ||
| assert!(f(F::NEG_NAN, F::NAN).is_nan()); | ||
| assert!(f(F::NEG_NAN, F::NEG_NAN).is_nan()); | ||
| } | ||
@@ -151,0 +257,0 @@ |
@@ -5,3 +5,3 @@ /// Return the lesser of two arguments or, if either argument is NaN, the other argument. | ||
| #[cfg(f16_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fminimumf16(x: f16, y: f16) -> f16 { | ||
@@ -14,3 +14,3 @@ super::generic::fminimum(x, y) | ||
| /// This coincides with IEEE 754-2019 `minimum`. The result orders -0.0 < 0.0. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fminimum(x: f64, y: f64) -> f64 { | ||
@@ -23,3 +23,3 @@ super::generic::fminimum(x, y) | ||
| /// This coincides with IEEE 754-2019 `minimum`. The result orders -0.0 < 0.0. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fminimumf(x: f32, y: f32) -> f32 { | ||
@@ -33,3 +33,3 @@ super::generic::fminimum(x, y) | ||
| #[cfg(f128_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fminimumf128(x: f128, y: f128) -> f128 { | ||
@@ -43,3 +43,3 @@ super::generic::fminimum(x, y) | ||
| #[cfg(f16_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fmaximumf16(x: f16, y: f16) -> f16 { | ||
@@ -52,3 +52,3 @@ super::generic::fmaximum(x, y) | ||
| /// This coincides with IEEE 754-2019 `maximum`. The result orders -0.0 < 0.0. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fmaximumf(x: f32, y: f32) -> f32 { | ||
@@ -61,3 +61,3 @@ super::generic::fmaximum(x, y) | ||
| /// This coincides with IEEE 754-2019 `maximum`. The result orders -0.0 < 0.0. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fmaximum(x: f64, y: f64) -> f64 { | ||
@@ -71,3 +71,3 @@ super::generic::fmaximum(x, y) | ||
| #[cfg(f128_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fmaximumf128(x: f128, y: f128) -> f128 { | ||
@@ -85,14 +85,50 @@ super::generic::fmaximum(x, y) | ||
| (F::ZERO, F::ZERO, F::ZERO), | ||
| (F::ONE, F::ONE, F::ONE), | ||
| (F::ZERO, F::NEG_ZERO, F::NEG_ZERO), | ||
| (F::ZERO, F::ONE, F::ZERO), | ||
| (F::ZERO, F::NEG_ONE, F::NEG_ONE), | ||
| (F::ZERO, F::INFINITY, F::ZERO), | ||
| (F::ZERO, F::NEG_INFINITY, F::NEG_INFINITY), | ||
| (F::ZERO, F::NAN, F::NAN), | ||
| (F::NEG_ZERO, F::ZERO, F::NEG_ZERO), | ||
| (F::NEG_ZERO, F::NEG_ZERO, F::NEG_ZERO), | ||
| (F::NEG_ZERO, F::ONE, F::NEG_ZERO), | ||
| (F::NEG_ZERO, F::NEG_ONE, F::NEG_ONE), | ||
| (F::NEG_ZERO, F::INFINITY, F::NEG_ZERO), | ||
| (F::NEG_ZERO, F::NEG_INFINITY, F::NEG_INFINITY), | ||
| (F::NEG_ZERO, F::NAN, F::NAN), | ||
| (F::ONE, F::ZERO, F::ZERO), | ||
| (F::ZERO, F::NEG_ONE, F::NEG_ONE), | ||
| (F::ONE, F::NEG_ZERO, F::NEG_ZERO), | ||
| (F::ONE, F::ONE, F::ONE), | ||
| (F::ONE, F::NEG_ONE, F::NEG_ONE), | ||
| (F::ONE, F::INFINITY, F::ONE), | ||
| (F::ONE, F::NEG_INFINITY, F::NEG_INFINITY), | ||
| (F::ONE, F::NAN, F::NAN), | ||
| (F::NEG_ONE, F::ZERO, F::NEG_ONE), | ||
| (F::NEG_ONE, F::NEG_ZERO, F::NEG_ONE), | ||
| (F::NEG_ONE, F::ONE, F::NEG_ONE), | ||
| (F::NEG_ONE, F::NEG_ONE, F::NEG_ONE), | ||
| (F::NEG_ONE, F::INFINITY, F::NEG_ONE), | ||
| (F::NEG_ONE, F::NEG_INFINITY, F::NEG_INFINITY), | ||
| (F::NEG_ONE, F::NAN, F::NAN), | ||
| (F::INFINITY, F::ZERO, F::ZERO), | ||
| (F::INFINITY, F::NEG_ZERO, F::NEG_ZERO), | ||
| (F::INFINITY, F::ONE, F::ONE), | ||
| (F::INFINITY, F::NEG_ONE, F::NEG_ONE), | ||
| (F::INFINITY, F::INFINITY, F::INFINITY), | ||
| (F::INFINITY, F::NEG_INFINITY, F::NEG_INFINITY), | ||
| (F::INFINITY, F::NAN, F::NAN), | ||
| (F::NEG_INFINITY, F::ZERO, F::NEG_INFINITY), | ||
| (F::NEG_INFINITY, F::NEG_ZERO, F::NEG_INFINITY), | ||
| (F::NEG_INFINITY, F::ONE, F::NEG_INFINITY), | ||
| (F::NEG_INFINITY, F::NEG_ONE, F::NEG_INFINITY), | ||
| (F::NEG_INFINITY, F::INFINITY, F::NEG_INFINITY), | ||
| (F::NEG_INFINITY, F::NEG_INFINITY, F::NEG_INFINITY), | ||
| (F::NEG_INFINITY, F::NAN, F::NAN), | ||
| (F::NAN, F::ZERO, F::NAN), | ||
| (F::ZERO, F::NAN, F::NAN), | ||
| (F::NAN, F::NEG_ZERO, F::NAN), | ||
| (F::NAN, F::ONE, F::NAN), | ||
| (F::NAN, F::NEG_ONE, F::NAN), | ||
| (F::NAN, F::INFINITY, F::NAN), | ||
| (F::NAN, F::NEG_INFINITY, F::NAN), | ||
| (F::NAN, F::NAN, F::NAN), | ||
| (F::ZERO, F::NEG_ZERO, F::NEG_ZERO), | ||
| (F::NEG_ZERO, F::ZERO, F::NEG_ZERO), | ||
| ]; | ||
@@ -104,2 +140,19 @@ | ||
| } | ||
| // Ordering between NaNs does not matter | ||
| assert!(f(F::NAN, F::NEG_NAN).is_nan()); | ||
| assert!(f(F::NEG_NAN, F::NAN).is_nan()); | ||
| assert!(f(F::ZERO, F::NEG_NAN).is_nan()); | ||
| assert!(f(F::NEG_ZERO, F::NEG_NAN).is_nan()); | ||
| assert!(f(F::ONE, F::NEG_NAN).is_nan()); | ||
| assert!(f(F::NEG_ONE, F::NEG_NAN).is_nan()); | ||
| assert!(f(F::INFINITY, F::NEG_NAN).is_nan()); | ||
| assert!(f(F::NEG_INFINITY, F::NEG_NAN).is_nan()); | ||
| assert!(f(F::NEG_NAN, F::ZERO).is_nan()); | ||
| assert!(f(F::NEG_NAN, F::NEG_ZERO).is_nan()); | ||
| assert!(f(F::NEG_NAN, F::ONE).is_nan()); | ||
| assert!(f(F::NEG_NAN, F::NEG_ONE).is_nan()); | ||
| assert!(f(F::NEG_NAN, F::INFINITY).is_nan()); | ||
| assert!(f(F::NEG_NAN, F::NEG_INFINITY).is_nan()); | ||
| assert!(f(F::NEG_NAN, F::NEG_NAN).is_nan()); | ||
| } | ||
@@ -132,14 +185,50 @@ | ||
| (F::ZERO, F::ZERO, F::ZERO), | ||
| (F::ONE, F::ONE, F::ONE), | ||
| (F::ZERO, F::NEG_ZERO, F::ZERO), | ||
| (F::ZERO, F::ONE, F::ONE), | ||
| (F::ZERO, F::NEG_ONE, F::ZERO), | ||
| (F::ZERO, F::INFINITY, F::INFINITY), | ||
| (F::ZERO, F::NEG_INFINITY, F::ZERO), | ||
| (F::ZERO, F::NAN, F::NAN), | ||
| (F::NEG_ZERO, F::ZERO, F::ZERO), | ||
| (F::NEG_ZERO, F::NEG_ZERO, F::NEG_ZERO), | ||
| (F::NEG_ZERO, F::ONE, F::ONE), | ||
| (F::NEG_ZERO, F::NEG_ONE, F::NEG_ZERO), | ||
| (F::NEG_ZERO, F::INFINITY, F::INFINITY), | ||
| (F::NEG_ZERO, F::NEG_INFINITY, F::NEG_ZERO), | ||
| (F::NEG_ZERO, F::NAN, F::NAN), | ||
| (F::ONE, F::ZERO, F::ONE), | ||
| (F::ZERO, F::NEG_ONE, F::ZERO), | ||
| (F::ONE, F::NEG_ZERO, F::ONE), | ||
| (F::ONE, F::ONE, F::ONE), | ||
| (F::ONE, F::NEG_ONE, F::ONE), | ||
| (F::ONE, F::INFINITY, F::INFINITY), | ||
| (F::ONE, F::NEG_INFINITY, F::ONE), | ||
| (F::ONE, F::NAN, F::NAN), | ||
| (F::NEG_ONE, F::ZERO, F::ZERO), | ||
| (F::NEG_ONE, F::NEG_ZERO, F::NEG_ZERO), | ||
| (F::NEG_ONE, F::ONE, F::ONE), | ||
| (F::NEG_ONE, F::NEG_ONE, F::NEG_ONE), | ||
| (F::NEG_ONE, F::INFINITY, F::INFINITY), | ||
| (F::NEG_ONE, F::NEG_INFINITY, F::NEG_ONE), | ||
| (F::NEG_ONE, F::NAN, F::NAN), | ||
| (F::INFINITY, F::ZERO, F::INFINITY), | ||
| (F::INFINITY, F::NEG_ZERO, F::INFINITY), | ||
| (F::INFINITY, F::ONE, F::INFINITY), | ||
| (F::INFINITY, F::NEG_ONE, F::INFINITY), | ||
| (F::INFINITY, F::INFINITY, F::INFINITY), | ||
| (F::INFINITY, F::NEG_INFINITY, F::INFINITY), | ||
| (F::INFINITY, F::NAN, F::NAN), | ||
| (F::NEG_INFINITY, F::ZERO, F::ZERO), | ||
| (F::NEG_INFINITY, F::NEG_ZERO, F::NEG_ZERO), | ||
| (F::NEG_INFINITY, F::ONE, F::ONE), | ||
| (F::NEG_INFINITY, F::NEG_ONE, F::NEG_ONE), | ||
| (F::NEG_INFINITY, F::INFINITY, F::INFINITY), | ||
| (F::NEG_INFINITY, F::NEG_INFINITY, F::NEG_INFINITY), | ||
| (F::NEG_INFINITY, F::NAN, F::NAN), | ||
| (F::NAN, F::ZERO, F::NAN), | ||
| (F::ZERO, F::NAN, F::NAN), | ||
| (F::NAN, F::NEG_ZERO, F::NAN), | ||
| (F::NAN, F::ONE, F::NAN), | ||
| (F::NAN, F::NEG_ONE, F::NAN), | ||
| (F::NAN, F::INFINITY, F::NAN), | ||
| (F::NAN, F::NEG_INFINITY, F::NAN), | ||
| (F::NAN, F::NAN, F::NAN), | ||
| (F::ZERO, F::NEG_ZERO, F::ZERO), | ||
| (F::NEG_ZERO, F::ZERO, F::ZERO), | ||
| ]; | ||
@@ -151,2 +240,19 @@ | ||
| } | ||
| // Ordering between NaNs does not matter | ||
| assert!(f(F::NAN, F::NEG_NAN).is_nan()); | ||
| assert!(f(F::NEG_NAN, F::NAN).is_nan()); | ||
| assert!(f(F::ZERO, F::NEG_NAN).is_nan()); | ||
| assert!(f(F::NEG_ZERO, F::NEG_NAN).is_nan()); | ||
| assert!(f(F::ONE, F::NEG_NAN).is_nan()); | ||
| assert!(f(F::NEG_ONE, F::NEG_NAN).is_nan()); | ||
| assert!(f(F::INFINITY, F::NEG_NAN).is_nan()); | ||
| assert!(f(F::NEG_INFINITY, F::NEG_NAN).is_nan()); | ||
| assert!(f(F::NEG_NAN, F::ZERO).is_nan()); | ||
| assert!(f(F::NEG_NAN, F::NEG_ZERO).is_nan()); | ||
| assert!(f(F::NEG_NAN, F::ONE).is_nan()); | ||
| assert!(f(F::NEG_NAN, F::NEG_ONE).is_nan()); | ||
| assert!(f(F::NEG_NAN, F::INFINITY).is_nan()); | ||
| assert!(f(F::NEG_NAN, F::NEG_INFINITY).is_nan()); | ||
| assert!(f(F::NEG_NAN, F::NEG_NAN).is_nan()); | ||
| } | ||
@@ -153,0 +259,0 @@ |
+4
-4
| /// Calculate the remainder of `x / y`, the precise result of `x - trunc(x / y) * y`. | ||
| #[cfg(f16_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fmodf16(x: f16, y: f16) -> f16 { | ||
@@ -9,3 +9,3 @@ super::generic::fmod(x, y) | ||
| /// Calculate the remainder of `x / y`, the precise result of `x - trunc(x / y) * y`. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fmodf(x: f32, y: f32) -> f32 { | ||
@@ -16,3 +16,3 @@ super::generic::fmod(x, y) | ||
| /// Calculate the remainder of `x / y`, the precise result of `x - trunc(x / y) * y`. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fmod(x: f64, y: f64) -> f64 { | ||
@@ -24,5 +24,5 @@ super::generic::fmod(x, y) | ||
| #[cfg(f128_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn fmodf128(x: f128, y: f128) -> f128 { | ||
| super::generic::fmod(x, y) | ||
| } |
@@ -1,2 +0,2 @@ | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn frexp(x: f64) -> (f64, i32) { | ||
@@ -3,0 +3,0 @@ let mut y = x.to_bits(); |
@@ -1,2 +0,2 @@ | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn frexpf(x: f32) -> (f32, i32) { | ||
@@ -3,0 +3,0 @@ let mut y = x.to_bits(); |
@@ -22,4 +22,3 @@ /* SPDX-License-Identifier: MIT OR Apache-2.0 */ | ||
| let res = if x.is_nan() || x < y { y } else { x }; | ||
| // Canonicalize | ||
| res * F::ONE | ||
| res.canonicalize() | ||
| } |
@@ -7,6 +7,6 @@ /* SPDX-License-Identifier: MIT OR Apache-2.0 */ | ||
| //! - `y` if `y > x` | ||
| //! - +0.0 if x and y are zero with opposite signs | ||
| //! - Either `x` or `y` if `x == y` and the signs are the same | ||
| //! - Non-NaN if one operand is NaN | ||
| //! - Logic following +0.0 > -0.0 | ||
| //! - Either `x` or `y` if `x == y` and the signs are the same | ||
| //! - qNaN if either operand is a NaN | ||
| //! - qNaN if both operands are NaNx | ||
| //! | ||
@@ -19,11 +19,13 @@ //! Excluded from our implementation is sNaN handling. | ||
| pub fn fmaximum_num<F: Float>(x: F, y: F) -> F { | ||
| let res = | ||
| if x.is_nan() || x < y || (x.to_bits() == F::NEG_ZERO.to_bits() && y.is_sign_positive()) { | ||
| y | ||
| } else { | ||
| x | ||
| }; | ||
| let res = if x > y || y.is_nan() { | ||
| x | ||
| } else if y > x || x.is_nan() { | ||
| y | ||
| } else if x.is_sign_positive() { | ||
| x | ||
| } else { | ||
| y | ||
| }; | ||
| // Canonicalize | ||
| res * F::ONE | ||
| res.canonicalize() | ||
| } |
@@ -7,4 +7,4 @@ /* SPDX-License-Identifier: MIT OR Apache-2.0 */ | ||
| //! - `y` if `y > x` | ||
| //! - +0.0 if x and y are zero with opposite signs | ||
| //! - qNaN if either operation is NaN | ||
| //! - Logic following +0.0 > -0.0 | ||
| //! | ||
@@ -21,3 +21,3 @@ //! Excluded from our implementation is sNaN handling. | ||
| y | ||
| } else if x > y || (y.to_bits() == F::NEG_ZERO.to_bits() && x.is_sign_positive()) { | ||
| } else if x > y || (y.biteq(F::NEG_ZERO) && x.is_sign_positive()) { | ||
| x | ||
@@ -28,4 +28,3 @@ } else { | ||
| // Canonicalize | ||
| res * F::ONE | ||
| res.canonicalize() | ||
| } |
@@ -22,4 +22,3 @@ /* SPDX-License-Identifier: MIT OR Apache-2.0 */ | ||
| let res = if y.is_nan() || x < y { x } else { y }; | ||
| // Canonicalize | ||
| res * F::ONE | ||
| res.canonicalize() | ||
| } |
@@ -7,6 +7,6 @@ /* SPDX-License-Identifier: MIT OR Apache-2.0 */ | ||
| //! - `y` if `y < x` | ||
| //! - -0.0 if x and y are zero with opposite signs | ||
| //! - Either `x` or `y` if `x == y` and the signs are the same | ||
| //! - Non-NaN if one operand is NaN | ||
| //! - Logic following +0.0 > -0.0 | ||
| //! - Either `x` or `y` if `x == y` and the signs are the same | ||
| //! - qNaN if either operand is a NaN | ||
| //! - qNaN if both operands are NaNx | ||
| //! | ||
@@ -19,11 +19,13 @@ //! Excluded from our implementation is sNaN handling. | ||
| pub fn fminimum_num<F: Float>(x: F, y: F) -> F { | ||
| let res = | ||
| if y.is_nan() || x < y || (x.to_bits() == F::NEG_ZERO.to_bits() && y.is_sign_positive()) { | ||
| x | ||
| } else { | ||
| y | ||
| }; | ||
| let res = if x > y || x.is_nan() { | ||
| y | ||
| } else if y > x || y.is_nan() { | ||
| x | ||
| } else if x.is_sign_positive() { | ||
| y | ||
| } else { | ||
| x | ||
| }; | ||
| // Canonicalize | ||
| res * F::ONE | ||
| res.canonicalize() | ||
| } |
@@ -7,4 +7,4 @@ /* SPDX-License-Identifier: MIT OR Apache-2.0 */ | ||
| //! - `y` if `y < x` | ||
| //! - -0.0 if x and y are zero with opposite signs | ||
| //! - qNaN if either operation is NaN | ||
| //! - Logic following +0.0 > -0.0 | ||
| //! | ||
@@ -21,3 +21,3 @@ //! Excluded from our implementation is sNaN handling. | ||
| y | ||
| } else if x < y || (x.to_bits() == F::NEG_ZERO.to_bits() && y.is_sign_positive()) { | ||
| } else if x < y || (x.biteq(F::NEG_ZERO) && y.is_sign_positive()) { | ||
| x | ||
@@ -28,4 +28,3 @@ } else { | ||
| // Canonicalize | ||
| res * F::ONE | ||
| res.canonicalize() | ||
| } |
| /* SPDX-License-Identifier: MIT OR Apache-2.0 */ | ||
| use crate::support::{CastFrom, Float, Int, MinInt}; | ||
| use crate::support::{CastFrom, CastInto, Float, HInt, Int, MinInt, NarrowingDiv}; | ||
| #[inline] | ||
| pub fn fmod<F: Float>(x: F, y: F) -> F { | ||
| pub fn fmod<F: Float>(x: F, y: F) -> F | ||
| where | ||
| F::Int: HInt, | ||
| <F::Int as HInt>::D: NarrowingDiv, | ||
| { | ||
| let _1 = F::Int::ONE; | ||
@@ -32,3 +36,3 @@ let sx = x.to_bits() & F::SIGN_MASK; | ||
| // evaluate `rem = (num << (ex - ey)) % div` ... | ||
| let rem = reduction(num, ex - ey, div); | ||
| let rem = reduction::<F>(num, ex - ey, div); | ||
| // ... so the result will be `rem << ey` | ||
@@ -62,9 +66,53 @@ | ||
| /// Compute the remainder `(x * 2.pow(e)) % y` without overflow. | ||
| fn reduction<I: Int>(mut x: I, e: u32, y: I) -> I { | ||
| x %= y; | ||
| for _ in 0..e { | ||
| x <<= 1; | ||
| x = x.checked_sub(y).unwrap_or(x); | ||
| fn reduction<F>(mut x: F::Int, e: u32, y: F::Int) -> F::Int | ||
| where | ||
| F: Float, | ||
| F::Int: HInt, | ||
| <<F as Float>::Int as HInt>::D: NarrowingDiv, | ||
| { | ||
| // `f16` only has 5 exponent bits, so even `f16::MAX = 65504.0` is only | ||
| // a 40-bit integer multiple of the smallest subnormal. | ||
| if F::BITS == 16 { | ||
| debug_assert!(F::EXP_MAX - F::EXP_MIN == 29); | ||
| debug_assert!(e <= 29); | ||
| let u: u16 = x.cast(); | ||
| let v: u16 = y.cast(); | ||
| let u = (u as u64) << e; | ||
| let v = v as u64; | ||
| return F::Int::cast_from((u % v) as u16); | ||
| } | ||
| x | ||
| // Ensure `x < 2y` for later steps | ||
| if x >= (y << 1) { | ||
| // This case is only reached with subnormal divisors, | ||
| // but it might be better to just normalize all significands | ||
| // to make this unnecessary. The further calls could potentially | ||
| // benefit from assuming a specific fixed leading bit position. | ||
| x %= y; | ||
| } | ||
| // The simple implementation seems to be fastest for a short reduction | ||
| // at this size. The limit here was chosen empirically on an Intel Nehalem. | ||
| // Less old CPUs that have faster `u64 * u64 -> u128` might not benefit, | ||
| // and 32-bit systems or architectures without hardware multipliers might | ||
| // want to do this in more cases. | ||
| if F::BITS == 64 && e < 32 { | ||
| // Assumes `x < 2y` | ||
| for _ in 0..e { | ||
| x = x.checked_sub(y).unwrap_or(x); | ||
| x <<= 1; | ||
| } | ||
| return x.checked_sub(y).unwrap_or(x); | ||
| } | ||
| // Fast path for short reductions | ||
| if e < F::BITS { | ||
| let w = x.widen() << e; | ||
| if let Some((_, r)) = w.checked_narrowing_div_rem(y) { | ||
| return r; | ||
| } | ||
| } | ||
| // Assumes `x < 2y` | ||
| crate::support::linear_mul_reduction(x, e, y) | ||
| } |
@@ -99,3 +99,3 @@ use crate::support::{CastFrom, CastInto, Float, IntTy, MinInt}; | ||
| // since it needs to construct the scale, but works better in the general case. | ||
| let add = -(n + sig_total_bits as i32).clamp(exp_min, sig_total_bits as i32); | ||
| let add = -(n + sig_total_bits as i32).max(exp_min); | ||
| let mul = F::from_parts(false, (F::EXP_BIAS as i32 - add) as u32, zero); | ||
@@ -107,3 +107,3 @@ | ||
| if n < exp_min { | ||
| let add = -(n + sig_total_bits as i32).clamp(exp_min, sig_total_bits as i32); | ||
| let add = -(n + sig_total_bits as i32).max(exp_min); | ||
| let mul = F::from_parts(false, (F::EXP_BIAS as i32 - add) as u32, zero); | ||
@@ -110,0 +110,0 @@ |
@@ -1,3 +0,1 @@ | ||
| use core::f64; | ||
| use super::sqrt; | ||
@@ -20,3 +18,3 @@ | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn hypot(mut x: f64, mut y: f64) -> f64 { | ||
@@ -23,0 +21,0 @@ let x1p700 = f64::from_bits(0x6bb0000000000000); // 0x1p700 === 2 ^ 700 |
@@ -1,6 +0,4 @@ | ||
| use core::f32; | ||
| use super::sqrtf; | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn hypotf(mut x: f32, mut y: f32) -> f32 { | ||
@@ -7,0 +5,0 @@ let x1p90 = f32::from_bits(0x6c800000); // 0x1p90f === 2 ^ 90 |
| const FP_ILOGBNAN: i32 = -1 - 0x7fffffff; | ||
| const FP_ILOGB0: i32 = FP_ILOGBNAN; | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn ilogb(x: f64) -> i32 { | ||
@@ -6,0 +6,0 @@ let mut i: u64 = x.to_bits(); |
| const FP_ILOGBNAN: i32 = -1 - 0x7fffffff; | ||
| const FP_ILOGB0: i32 = FP_ILOGBNAN; | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn ilogbf(x: f32) -> i32 { | ||
@@ -6,0 +6,0 @@ let mut i = x.to_bits(); |
+2
-2
@@ -113,3 +113,3 @@ /* origin: FreeBSD /usr/src/lib/msun/src/e_j0.c */ | ||
| /// Zeroth order of the [Bessel function](https://en.wikipedia.org/wiki/Bessel_function) of the first kind (f64). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn j0(mut x: f64) -> f64 { | ||
@@ -169,3 +169,3 @@ let z: f64; | ||
| /// Zeroth order of the [Bessel function](https://en.wikipedia.org/wiki/Bessel_function) of the second kind (f64). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn y0(x: f64) -> f64 { | ||
@@ -172,0 +172,0 @@ let z: f64; |
+2
-2
@@ -66,3 +66,3 @@ /* origin: FreeBSD /usr/src/lib/msun/src/e_j0f.c */ | ||
| /// Zeroth order of the [Bessel function](https://en.wikipedia.org/wiki/Bessel_function) of the first kind (f32). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn j0f(mut x: f32) -> f32 { | ||
@@ -114,3 +114,3 @@ let z: f32; | ||
| /// Zeroth order of the [Bessel function](https://en.wikipedia.org/wiki/Bessel_function) of the second kind (f32). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn y0f(x: f32) -> f32 { | ||
@@ -117,0 +117,0 @@ let z: f32; |
+2
-2
@@ -117,3 +117,3 @@ /* origin: FreeBSD /usr/src/lib/msun/src/e_j1.c */ | ||
| /// First order of the [Bessel function](https://en.wikipedia.org/wiki/Bessel_function) of the first kind (f64). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn j1(x: f64) -> f64 { | ||
@@ -165,3 +165,3 @@ let mut z: f64; | ||
| /// First order of the [Bessel function](https://en.wikipedia.org/wiki/Bessel_function) of the second kind (f64). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn y1(x: f64) -> f64 { | ||
@@ -168,0 +168,0 @@ let z: f64; |
+3
-4
@@ -67,3 +67,3 @@ /* origin: FreeBSD /usr/src/lib/msun/src/e_j1f.c */ | ||
| /// First order of the [Bessel function](https://en.wikipedia.org/wiki/Bessel_function) of the first kind (f32). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn j1f(x: f32) -> f32 { | ||
@@ -114,3 +114,3 @@ let mut z: f32; | ||
| /// First order of the [Bessel function](https://en.wikipedia.org/wiki/Bessel_function) of the second kind (f32). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn y1f(x: f32) -> f32 { | ||
@@ -366,4 +366,2 @@ let z: f32; | ||
| // PowerPC tests are failing on LLVM 13: https://github.com/rust-lang/rust/issues/88520 | ||
| #[cfg(not(target_arch = "powerpc64"))] | ||
| #[cfg(test)] | ||
@@ -377,2 +375,3 @@ mod tests { | ||
| } | ||
| #[test] | ||
@@ -379,0 +378,0 @@ fn test_y1f_2002() { |
+2
-2
@@ -42,3 +42,3 @@ /* origin: FreeBSD /usr/src/lib/msun/src/e_jn.c */ | ||
| /// Integer order of the [Bessel function](https://en.wikipedia.org/wiki/Bessel_function) of the first kind (f64). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn jn(n: i32, mut x: f64) -> f64 { | ||
@@ -253,3 +253,3 @@ let mut ix: u32; | ||
| /// Integer order of the [Bessel function](https://en.wikipedia.org/wiki/Bessel_function) of the second kind (f64). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn yn(n: i32, x: f64) -> f64 { | ||
@@ -256,0 +256,0 @@ let mut ix: u32; |
+2
-2
@@ -19,3 +19,3 @@ /* origin: FreeBSD /usr/src/lib/msun/src/e_jnf.c */ | ||
| /// Integer order of the [Bessel function](https://en.wikipedia.org/wiki/Bessel_function) of the first kind (f32). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn jnf(n: i32, mut x: f32) -> f32 { | ||
@@ -196,3 +196,3 @@ let mut ix: u32; | ||
| /// Integer order of the [Bessel function](https://en.wikipedia.org/wiki/Bessel_function) of the second kind (f32). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn ynf(n: i32, x: f32) -> f32 { | ||
@@ -199,0 +199,0 @@ let mut ix: u32; |
@@ -54,3 +54,3 @@ // origin: FreeBSD /usr/src/lib/msun/src/k_cos.c | ||
| // any extra precision in w. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub(crate) fn k_cos(x: f64, y: f64) -> f64 { | ||
@@ -57,0 +57,0 @@ let z = x * x; |
@@ -23,3 +23,3 @@ /* origin: FreeBSD /usr/src/lib/msun/src/k_cosf.c */ | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub(crate) fn k_cosf(x: f64) -> f32 { | ||
@@ -26,0 +26,0 @@ let z = x * x; |
@@ -7,3 +7,3 @@ use super::exp; | ||
| /* expf(x)/2 for x >= log(FLT_MAX), slightly better than 0.5f*expf(x/2)*expf(x/2) */ | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub(crate) fn k_expo2(x: f64) -> f64 { | ||
@@ -10,0 +10,0 @@ let k_ln2 = f64::from_bits(0x40962066151add8b); |
@@ -7,3 +7,3 @@ use super::expf; | ||
| /* expf(x)/2 for x >= log(FLT_MAX), slightly better than 0.5f*expf(x/2)*expf(x/2) */ | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub(crate) fn k_expo2f(x: f32) -> f32 { | ||
@@ -10,0 +10,0 @@ let k_ln2 = f32::from_bits(0x4322e3bc); |
@@ -46,3 +46,3 @@ // origin: FreeBSD /usr/src/lib/msun/src/k_sin.c | ||
| // sin(x) = x + (S1*x + (x *(r-y/2)+y)) | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub(crate) fn k_sin(x: f64, y: f64, iy: i32) -> f64 { | ||
@@ -49,0 +49,0 @@ let z = x * x; |
@@ -23,3 +23,3 @@ /* origin: FreeBSD /usr/src/lib/msun/src/k_sinf.c */ | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub(crate) fn k_sinf(x: f64) -> f32 { | ||
@@ -26,0 +26,0 @@ let z = x * x; |
@@ -61,3 +61,3 @@ // origin: FreeBSD /usr/src/lib/msun/src/k_tan.c */ | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub(crate) fn k_tan(mut x: f64, mut y: f64, odd: i32) -> f64 { | ||
@@ -64,0 +64,0 @@ let hx = (f64::to_bits(x) >> 32) as u32; |
@@ -22,3 +22,3 @@ /* origin: FreeBSD /usr/src/lib/msun/src/k_tan.c */ | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub(crate) fn k_tanf(x: f64, odd: bool) -> f32 { | ||
@@ -25,0 +25,0 @@ let z = x * x; |
| #[cfg(f16_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn ldexpf16(x: f16, n: i32) -> f16 { | ||
@@ -7,3 +7,3 @@ super::scalbnf16(x, n) | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn ldexpf(x: f32, n: i32) -> f32 { | ||
@@ -13,3 +13,3 @@ super::scalbnf(x, n) | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn ldexp(x: f64, n: i32) -> f64 { | ||
@@ -20,5 +20,5 @@ super::scalbn(x, n) | ||
| #[cfg(f128_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn ldexpf128(x: f128, n: i32) -> f128 { | ||
| super::scalbnf128(x, n) | ||
| } |
@@ -168,3 +168,3 @@ /* origin: FreeBSD /usr/src/lib/msun/src/e_lgamma_r.c */ | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn lgamma_r(mut x: f64) -> (f64, i32) { | ||
@@ -171,0 +171,0 @@ let u: u64 = x.to_bits(); |
@@ -5,5 +5,5 @@ use super::lgamma_r; | ||
| /// [Gamma function](https://en.wikipedia.org/wiki/Gamma_function) (f64). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn lgamma(x: f64) -> f64 { | ||
| lgamma_r(x).0 | ||
| } |
@@ -103,3 +103,3 @@ /* origin: FreeBSD /usr/src/lib/msun/src/e_lgammaf_r.c */ | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn lgammaf_r(mut x: f32) -> (f32, i32) { | ||
@@ -106,0 +106,0 @@ let u = x.to_bits(); |
@@ -5,5 +5,5 @@ use super::lgammaf_r; | ||
| /// [Gamma function](https://en.wikipedia.org/wiki/Gamma_function) (f32). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn lgammaf(x: f32) -> f32 { | ||
| lgammaf_r(x).0 | ||
| } |
+1
-1
@@ -74,3 +74,3 @@ /* origin: FreeBSD /usr/src/lib/msun/src/e_log.c */ | ||
| /// The natural logarithm of `x` (f64). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn log(mut x: f64) -> f64 { | ||
@@ -77,0 +77,0 @@ let x1p54 = f64::from_bits(0x4350000000000000); // 0x1p54 === 2 ^ 54 |
@@ -20,4 +20,2 @@ /* origin: FreeBSD /usr/src/lib/msun/src/e_log10.c */ | ||
| use core::f64; | ||
| const IVLN10HI: f64 = 4.34294481878168880939e-01; /* 0x3fdbcb7b, 0x15200000 */ | ||
@@ -36,3 +34,3 @@ const IVLN10LO: f64 = 2.50829467116452752298e-11; /* 0x3dbb9438, 0xca9aadd5 */ | ||
| /// The base 10 logarithm of `x` (f64). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn log10(mut x: f64) -> f64 { | ||
@@ -39,0 +37,0 @@ let x1p54 = f64::from_bits(0x4350000000000000); // 0x1p54 === 2 ^ 54 |
@@ -16,4 +16,2 @@ /* origin: FreeBSD /usr/src/lib/msun/src/e_log10f.c */ | ||
| use core::f32; | ||
| const IVLN10HI: f32 = 4.3432617188e-01; /* 0x3ede6000 */ | ||
@@ -30,3 +28,3 @@ const IVLN10LO: f32 = -3.1689971365e-05; /* 0xb804ead9 */ | ||
| /// The base 10 logarithm of `x` (f32). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn log10f(mut x: f32) -> f32 { | ||
@@ -33,0 +31,0 @@ let x1p25f = f32::from_bits(0x4c000000); // 0x1p25f === 2 ^ 25 |
@@ -56,4 +56,2 @@ /* origin: FreeBSD /usr/src/lib/msun/src/s_log1p.c */ | ||
| use core::f64; | ||
| const LN2_HI: f64 = 6.93147180369123816490e-01; /* 3fe62e42 fee00000 */ | ||
@@ -70,3 +68,3 @@ const LN2_LO: f64 = 1.90821492927058770002e-10; /* 3dea39ef 35793c76 */ | ||
| /// The natural logarithm of 1+`x` (f64). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn log1p(x: f64) -> f64 { | ||
@@ -73,0 +71,0 @@ let mut ui: u64 = x.to_bits(); |
@@ -13,4 +13,2 @@ /* origin: FreeBSD /usr/src/lib/msun/src/s_log1pf.c */ | ||
| use core::f32; | ||
| const LN2_HI: f32 = 6.9313812256e-01; /* 0x3f317180 */ | ||
@@ -25,3 +23,3 @@ const LN2_LO: f32 = 9.0580006145e-06; /* 0x3717f7d1 */ | ||
| /// The natural logarithm of 1+`x` (f32). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn log1pf(x: f32) -> f32 { | ||
@@ -28,0 +26,0 @@ let mut ui: u32 = x.to_bits(); |
+1
-3
@@ -20,4 +20,2 @@ /* origin: FreeBSD /usr/src/lib/msun/src/e_log2.c */ | ||
| use core::f64; | ||
| const IVLN2HI: f64 = 1.44269504072144627571e+00; /* 0x3ff71547, 0x65200000 */ | ||
@@ -34,3 +32,3 @@ const IVLN2LO: f64 = 1.67517131648865118353e-10; /* 0x3de705fc, 0x2eefa200 */ | ||
| /// The base 2 logarithm of `x` (f64). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn log2(mut x: f64) -> f64 { | ||
@@ -37,0 +35,0 @@ let x1p54 = f64::from_bits(0x4350000000000000); // 0x1p54 === 2 ^ 54 |
@@ -16,4 +16,2 @@ /* origin: FreeBSD /usr/src/lib/msun/src/e_log2f.c */ | ||
| use core::f32; | ||
| const IVLN2HI: f32 = 1.4428710938e+00; /* 0x3fb8b000 */ | ||
@@ -28,3 +26,3 @@ const IVLN2LO: f32 = -1.7605285393e-04; /* 0xb9389ad4 */ | ||
| /// The base 2 logarithm of `x` (f32). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn log2f(mut x: f32) -> f32 { | ||
@@ -31,0 +29,0 @@ let x1p25f = f32::from_bits(0x4c000000); // 0x1p25f === 2 ^ 25 |
+1
-1
@@ -25,3 +25,3 @@ /* origin: FreeBSD /usr/src/lib/msun/src/e_logf.c */ | ||
| /// The natural logarithm of `x` (f32). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn logf(mut x: f32) -> f32 { | ||
@@ -28,0 +28,0 @@ let x1p25 = f32::from_bits(0x4c000000); // 0x1p25f === 2 ^ 25 |
+2
-0
@@ -0,1 +1,3 @@ | ||
| #![allow(clippy::approx_constant)] // many false positives | ||
| macro_rules! force_eval { | ||
@@ -2,0 +4,0 @@ ($e:expr) => { |
+1
-1
@@ -1,2 +0,2 @@ | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn modf(x: f64) -> (f64, f64) { | ||
@@ -3,0 +3,0 @@ let rv2: f64; |
@@ -1,2 +0,2 @@ | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn modff(x: f32) -> (f32, f32) { | ||
@@ -3,0 +3,0 @@ let rv2: f32; |
@@ -1,2 +0,2 @@ | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn nextafter(x: f64, y: f64) -> f64 { | ||
@@ -3,0 +3,0 @@ if x.is_nan() || y.is_nan() { |
@@ -1,2 +0,2 @@ | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn nextafterf(x: f32, y: f32) -> f32 { | ||
@@ -3,0 +3,0 @@ if x.is_nan() || y.is_nan() { |
+1
-1
@@ -93,3 +93,3 @@ /* origin: FreeBSD /usr/src/lib/msun/src/e_pow.c */ | ||
| /// Returns `x` to the power of `y` (f64). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn pow(x: f64, y: f64) -> f64 { | ||
@@ -96,0 +96,0 @@ let t1: f64; |
+1
-1
@@ -49,3 +49,3 @@ /* origin: FreeBSD /usr/src/lib/msun/src/e_powf.c */ | ||
| /// Returns `x` to the power of `y` (f32). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn powf(x: f32, y: f32) -> f32 { | ||
@@ -52,0 +52,0 @@ let mut z: f32; |
@@ -14,3 +14,3 @@ #![allow(unused_unsafe)] | ||
| use super::{floor, scalbn}; | ||
| use super::scalbn; | ||
@@ -150,3 +150,3 @@ // initial value for jk | ||
| // | ||
| // y[] ouput result in an array of double precision numbers. | ||
| // y[] output result in an array of double precision numbers. | ||
| // The dimension of y[] is: | ||
@@ -226,4 +226,12 @@ // 24-bit precision 1 | ||
| /// independent of the exponent of the input. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub(crate) fn rem_pio2_large(x: &[f64], y: &mut [f64], e0: i32, prec: usize) -> i32 { | ||
| // FIXME(rust-lang/rust#144518): Inline assembly would cause `no_panic` to fail | ||
| // on the callers of this function. As a workaround, avoid inlining `floor` here | ||
| // when implemented with assembly. | ||
| #[cfg_attr(x86_no_sse, inline(never))] | ||
| extern "C" fn floor(x: f64) -> f64 { | ||
| super::floor(x) | ||
| } | ||
| let x1p24 = f64::from_bits(0x4170000000000000); // 0x1p24 === 2 ^ 24 | ||
@@ -230,0 +238,0 @@ let x1p_24 = f64::from_bits(0x3e70000000000000); // 0x1p_24 === 2 ^ (-24) |
@@ -44,3 +44,3 @@ // origin: FreeBSD /usr/src/lib/msun/src/e_rem_pio2.c | ||
| // caller must handle the case when reduction is not needed: |x| ~<= pi/4 */ | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub(crate) fn rem_pio2(x: f64) -> (i32, f64, f64) { | ||
@@ -199,3 +199,3 @@ let x1p24 = f64::from_bits(0x4170000000000000); | ||
| // FIXME(correctness): inaccurate results on i586 | ||
| #[cfg_attr(all(target_arch = "x86", not(target_feature = "sse")), ignore)] | ||
| #[cfg_attr(x86_no_sse, ignore)] | ||
| fn test_near_pi() { | ||
@@ -202,0 +202,0 @@ let arg = 3.141592025756836; |
@@ -17,4 +17,2 @@ /* origin: FreeBSD /usr/src/lib/msun/src/e_rem_pio2f.c */ | ||
| use core::f64; | ||
| use super::rem_pio2_large; | ||
@@ -35,3 +33,3 @@ | ||
| /// use __rem_pio2_large() for large x | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub(crate) fn rem_pio2f(x: f32) -> (i32, f64) { | ||
@@ -38,0 +36,0 @@ let x64 = x as f64; |
@@ -1,2 +0,2 @@ | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn remainder(x: f64, y: f64) -> f64 { | ||
@@ -3,0 +3,0 @@ let (result, _) = super::remquo(x, y); |
@@ -1,2 +0,2 @@ | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn remainderf(x: f32, y: f32) -> f32 { | ||
@@ -3,0 +3,0 @@ let (result, _) = super::remquof(x, y); |
@@ -1,2 +0,2 @@ | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn remquo(mut x: f64, mut y: f64) -> (f64, i32) { | ||
@@ -3,0 +3,0 @@ let ux: u64 = x.to_bits(); |
@@ -1,2 +0,2 @@ | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn remquof(mut x: f32, mut y: f32) -> (f32, i32) { | ||
@@ -3,0 +3,0 @@ let ux: u32 = x.to_bits(); |
+4
-4
@@ -5,3 +5,3 @@ use super::support::Round; | ||
| #[cfg(f16_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn rintf16(x: f16) -> f16 { | ||
@@ -18,3 +18,3 @@ select_implementation! { | ||
| /// Round `x` to the nearest integer, breaking ties toward even. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn rintf(x: f32) -> f32 { | ||
@@ -34,3 +34,3 @@ select_implementation! { | ||
| /// Round `x` to the nearest integer, breaking ties toward even. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn rint(x: f64) -> f64 { | ||
@@ -51,5 +51,5 @@ select_implementation! { | ||
| #[cfg(f128_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn rintf128(x: f128) -> f128 { | ||
| super::generic::rint_round(x, Round::Nearest).val | ||
| } |
| /// Round `x` to the nearest integer, breaking ties away from zero. | ||
| #[cfg(f16_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn roundf16(x: f16) -> f16 { | ||
@@ -9,3 +9,3 @@ super::generic::round(x) | ||
| /// Round `x` to the nearest integer, breaking ties away from zero. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn roundf(x: f32) -> f32 { | ||
@@ -16,3 +16,3 @@ super::generic::round(x) | ||
| /// Round `x` to the nearest integer, breaking ties away from zero. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn round(x: f64) -> f64 { | ||
@@ -24,5 +24,5 @@ super::generic::round(x) | ||
| #[cfg(f128_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn roundf128(x: f128) -> f128 { | ||
| super::generic::round(x) | ||
| } |
@@ -6,3 +6,3 @@ use super::support::{Float, Round}; | ||
| #[cfg(f16_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn roundevenf16(x: f16) -> f16 { | ||
@@ -14,3 +14,3 @@ roundeven_impl(x) | ||
| /// `roundToIntegralTiesToEven`. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn roundevenf(x: f32) -> f32 { | ||
@@ -22,3 +22,3 @@ roundeven_impl(x) | ||
| /// `roundToIntegralTiesToEven`. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn roundeven(x: f64) -> f64 { | ||
@@ -31,3 +31,3 @@ roundeven_impl(x) | ||
| #[cfg(f128_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn roundevenf128(x: f128) -> f128 { | ||
@@ -34,0 +34,0 @@ roundeven_impl(x) |
| #[cfg(f16_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn scalbnf16(x: f16, n: i32) -> f16 { | ||
@@ -7,3 +7,3 @@ super::generic::scalbn(x, n) | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn scalbnf(x: f32, n: i32) -> f32 { | ||
@@ -13,3 +13,3 @@ super::generic::scalbn(x, n) | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn scalbn(x: f64, n: i32) -> f64 { | ||
@@ -20,3 +20,3 @@ super::generic::scalbn(x, n) | ||
| #[cfg(f128_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn scalbnf128(x: f128, n: i32) -> f128 { | ||
@@ -23,0 +23,0 @@ super::generic::scalbn(x, n) |
+1
-1
@@ -47,3 +47,3 @@ // origin: FreeBSD /usr/src/lib/msun/src/s_sin.c */ | ||
| /// `x` is specified in radians. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn sin(x: f64) -> f64 { | ||
@@ -50,0 +50,0 @@ let x1p120 = f64::from_bits(0x4770000000000000); // 0x1p120f === 2 ^ 120 |
@@ -18,3 +18,3 @@ /* origin: FreeBSD /usr/src/lib/msun/src/s_sin.c */ | ||
| /// `x` is specified in radians and the return value is (sin(x), cos(x)). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn sincos(x: f64) -> (f64, f64) { | ||
@@ -21,0 +21,0 @@ let s: f64; |
@@ -29,3 +29,3 @@ /* origin: FreeBSD /usr/src/lib/msun/src/s_sinf.c */ | ||
| /// `x` is specified in radians and the return value is (sin(x), cos(x)). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn sincosf(x: f32) -> (f32, f32) { | ||
@@ -32,0 +32,0 @@ let s: f32; |
+1
-1
@@ -30,3 +30,3 @@ /* origin: FreeBSD /usr/src/lib/msun/src/s_sinf.c */ | ||
| /// `x` is specified in radians. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn sinf(x: f32) -> f32 { | ||
@@ -33,0 +33,0 @@ let x64 = x as f64; |
+1
-1
@@ -9,3 +9,3 @@ use super::{expm1, expo2}; | ||
| /// The hyperbolic sine of `x` (f64). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn sinh(x: f64) -> f64 { | ||
@@ -12,0 +12,0 @@ // union {double f; uint64_t i;} u = {.f = x}; |
| use super::{expm1f, k_expo2f}; | ||
| /// The hyperbolic sine of `x` (f32). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn sinhf(x: f32) -> f32 { | ||
@@ -6,0 +6,0 @@ let mut h = 0.5f32; |
+4
-4
| /// The square root of `x` (f16). | ||
| #[cfg(f16_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn sqrtf16(x: f16) -> f16 { | ||
@@ -15,3 +15,3 @@ select_implementation! { | ||
| /// The square root of `x` (f32). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn sqrtf(x: f32) -> f32 { | ||
@@ -32,3 +32,3 @@ select_implementation! { | ||
| /// The square root of `x` (f64). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn sqrt(x: f64) -> f64 { | ||
@@ -50,5 +50,5 @@ select_implementation! { | ||
| #[cfg(f128_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn sqrtf128(x: f128) -> f128 { | ||
| return super::generic::sqrt(x); | ||
| } |
+79
-54
@@ -14,6 +14,6 @@ //! Integers used for wide operations, larger than `u128`. | ||
| #[allow(non_camel_case_types)] | ||
| #[derive(Clone, Copy, Debug, PartialEq, PartialOrd)] | ||
| #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord)] | ||
| pub struct u256 { | ||
| pub hi: u128, | ||
| pub lo: u128, | ||
| pub hi: u128, | ||
| } | ||
@@ -32,3 +32,3 @@ | ||
| lo: self.lo, | ||
| hi: self.hi, | ||
| hi: self.hi as i128, | ||
| } | ||
@@ -40,6 +40,6 @@ } | ||
| #[allow(non_camel_case_types)] | ||
| #[derive(Clone, Copy, Debug, PartialEq, PartialOrd)] | ||
| #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord)] | ||
| pub struct i256 { | ||
| pub hi: i128, | ||
| pub lo: u128, | ||
| pub hi: u128, | ||
| } | ||
@@ -53,3 +53,3 @@ | ||
| lo: self.lo, | ||
| hi: self.hi, | ||
| hi: self.hi as u128, | ||
| } | ||
@@ -80,3 +80,3 @@ } | ||
| const SIGNED: bool = false; | ||
| const SIGNED: bool = true; | ||
| const BITS: u32 = 256; | ||
@@ -86,8 +86,8 @@ const ZERO: Self = Self { lo: 0, hi: 0 }; | ||
| const MIN: Self = Self { | ||
| lo: 0, | ||
| hi: 1 << 127, | ||
| lo: u128::MIN, | ||
| hi: i128::MIN, | ||
| }; | ||
| const MAX: Self = Self { | ||
| lo: u128::MAX, | ||
| hi: u128::MAX << 1, | ||
| hi: i128::MAX, | ||
| }; | ||
@@ -118,56 +118,82 @@ } | ||
| impl ops::Shl<u32> for $ty { | ||
| impl ops::Add<Self> for $ty { | ||
| type Output = Self; | ||
| fn shl(self, _rhs: u32) -> Self::Output { | ||
| unimplemented!("only used to meet trait bounds") | ||
| fn add(self, rhs: Self) -> Self::Output { | ||
| let (lo, carry) = self.lo.overflowing_add(rhs.lo); | ||
| let (hi, of) = Int::carrying_add(self.hi, rhs.hi, carry); | ||
| debug_assert!(!of, "attempt to add with overflow"); | ||
| Self { lo, hi } | ||
| } | ||
| } | ||
| }; | ||
| } | ||
| impl_common!(i256); | ||
| impl_common!(u256); | ||
| impl ops::Sub<Self> for $ty { | ||
| type Output = Self; | ||
| impl ops::Add<Self> for u256 { | ||
| type Output = Self; | ||
| fn sub(self, rhs: Self) -> Self::Output { | ||
| let (lo, borrow) = self.lo.overflowing_sub(rhs.lo); | ||
| let (hi, of) = Int::borrowing_sub(self.hi, rhs.hi, borrow); | ||
| debug_assert!(!of, "attempt to subtract with overflow"); | ||
| Self { lo, hi } | ||
| } | ||
| } | ||
| fn add(self, rhs: Self) -> Self::Output { | ||
| let (lo, carry) = self.lo.overflowing_add(rhs.lo); | ||
| let hi = self.hi.wrapping_add(carry as u128).wrapping_add(rhs.hi); | ||
| impl ops::Shl<u32> for $ty { | ||
| type Output = Self; | ||
| Self { lo, hi } | ||
| } | ||
| } | ||
| fn shl(mut self, rhs: u32) -> Self::Output { | ||
| debug_assert!(rhs < Self::BITS, "attempt to shift left with overflow"); | ||
| impl ops::Shr<u32> for u256 { | ||
| type Output = Self; | ||
| let half_bits = Self::BITS / 2; | ||
| let low_mask = half_bits - 1; | ||
| let s = rhs & low_mask; | ||
| fn shr(mut self, rhs: u32) -> Self::Output { | ||
| debug_assert!(rhs < Self::BITS, "attempted to shift right with overflow"); | ||
| if rhs >= Self::BITS { | ||
| return Self::ZERO; | ||
| } | ||
| let lo = self.lo; | ||
| let hi = self.hi; | ||
| if rhs == 0 { | ||
| return self; | ||
| } | ||
| self.lo = lo << s; | ||
| if rhs < 128 { | ||
| self.lo >>= rhs; | ||
| self.lo |= self.hi << (128 - rhs); | ||
| } else { | ||
| self.lo = self.hi >> (rhs - 128); | ||
| if rhs & half_bits == 0 { | ||
| self.hi = (lo >> (low_mask ^ s) >> 1) as _; | ||
| self.hi |= hi << s; | ||
| } else { | ||
| self.hi = self.lo as _; | ||
| self.lo = 0; | ||
| } | ||
| self | ||
| } | ||
| } | ||
| if rhs < 128 { | ||
| self.hi >>= rhs; | ||
| } else { | ||
| self.hi = 0; | ||
| impl ops::Shr<u32> for $ty { | ||
| type Output = Self; | ||
| fn shr(mut self, rhs: u32) -> Self::Output { | ||
| debug_assert!(rhs < Self::BITS, "attempt to shift right with overflow"); | ||
| let half_bits = Self::BITS / 2; | ||
| let low_mask = half_bits - 1; | ||
| let s = rhs & low_mask; | ||
| let lo = self.lo; | ||
| let hi = self.hi; | ||
| self.hi = hi >> s; | ||
| #[allow(unused_comparisons)] | ||
| if rhs & half_bits == 0 { | ||
| self.lo = (hi << (low_mask ^ s) << 1) as _; | ||
| self.lo |= lo >> s; | ||
| } else { | ||
| self.lo = self.hi as _; | ||
| self.hi = if hi < 0 { !0 } else { 0 }; | ||
| } | ||
| self | ||
| } | ||
| } | ||
| self | ||
| } | ||
| }; | ||
| } | ||
| impl_common!(i256); | ||
| impl_common!(u256); | ||
| impl HInt for u128 { | ||
@@ -210,3 +236,3 @@ type D = u256; | ||
| fn widen_hi(self) -> Self::D { | ||
| self.widen() << <Self as MinInt>::BITS | ||
| u256 { lo: 0, hi: self } | ||
| } | ||
@@ -219,7 +245,6 @@ } | ||
| fn widen(self) -> Self::D { | ||
| let mut ret = self.unsigned().zero_widen().signed(); | ||
| if self.is_negative() { | ||
| ret.hi = u128::MAX; | ||
| i256 { | ||
| lo: self as u128, | ||
| hi: if self < 0 { -1 } else { 0 }, | ||
| } | ||
| ret | ||
| } | ||
@@ -240,3 +265,3 @@ | ||
| fn widen_hi(self) -> Self::D { | ||
| self.widen() << <Self as MinInt>::BITS | ||
| i256 { lo: 0, hi: self } | ||
| } | ||
@@ -265,4 +290,4 @@ } | ||
| fn hi(self) -> Self::H { | ||
| self.hi as i128 | ||
| self.hi | ||
| } | ||
| } |
@@ -6,2 +6,3 @@ extern crate std; | ||
| use super::{HInt, MinInt, i256, u256}; | ||
| use crate::support::{Int as _, NarrowingDiv}; | ||
@@ -40,3 +41,3 @@ const LOHI_SPLIT: u128 = 0xaaaaaaaaaaaaaaaaffffffffffffffff; | ||
| lo: LOHI_SPLIT, | ||
| hi: u128::MAX | ||
| hi: -1, | ||
| } | ||
@@ -280,1 +281,87 @@ ); | ||
| } | ||
| #[test] | ||
| fn u256_ord() { | ||
| let _1 = u256::ONE; | ||
| let _2 = _1 + _1; | ||
| for x in u8::MIN..u8::MAX { | ||
| let y = x + 1; | ||
| let wx = (x as u128).widen_hi(); | ||
| let wy = (y as u128).widen_hi(); | ||
| assert!([wx, wx + _1, wx + _2, wy, wy + _1, wy + _2].is_sorted()); | ||
| } | ||
| } | ||
| #[test] | ||
| fn i256_ord() { | ||
| let _1 = i256::ONE; | ||
| let _2 = _1 + _1; | ||
| for x in i8::MIN..i8::MAX { | ||
| let y = x + 1; | ||
| let wx = (x as i128).widen_hi(); | ||
| let wy = (y as i128).widen_hi(); | ||
| assert!([wx, wx + _1, wx + _2, wy - _2, wy - _1, wy].is_sorted()); | ||
| } | ||
| } | ||
| #[test] | ||
| fn u256_shifts() { | ||
| let _1 = u256::ONE; | ||
| for k in 0..255 { | ||
| let x = _1 << k; | ||
| let x2 = _1 << (k + 1); | ||
| assert!(x < x2); | ||
| assert_eq!(x << 1, x2); | ||
| assert_eq!(x + x, x2); | ||
| assert_eq!(x >> k, _1); | ||
| assert_eq!(x2 >> (k + 1), _1); | ||
| } | ||
| } | ||
| #[test] | ||
| fn i256_shifts() { | ||
| let _1 = i256::ONE; | ||
| for k in 0..254 { | ||
| let x = _1 << k; | ||
| let x2 = _1 << (k + 1); | ||
| assert!(x < x2); | ||
| assert_eq!(x << 1, x2); | ||
| assert_eq!(x + x, x2); | ||
| assert_eq!(x >> k, _1); | ||
| assert_eq!(x2 >> (k + 1), _1); | ||
| } | ||
| let min = _1 << 255; | ||
| assert_eq!(min, i256::MIN); | ||
| let mut x = min; | ||
| for k in 0..255 { | ||
| assert_eq!(x, min >> k); | ||
| let y = x >> 1; | ||
| assert_eq!(y + y, x); | ||
| assert!(x < y); | ||
| x = y; | ||
| } | ||
| } | ||
| #[test] | ||
| fn div_u256_by_u128() { | ||
| for j in i8::MIN..=i8::MAX { | ||
| let y: u128 = (j as i128).rotate_right(4).unsigned(); | ||
| if y == 0 { | ||
| continue; | ||
| } | ||
| for i in i8::MIN..=i8::MAX { | ||
| let x: u128 = (i as i128).rotate_right(4).unsigned(); | ||
| let xy = x.widen_mul(y); | ||
| assert_eq!(xy.checked_narrowing_div_rem(y), Some((x, 0))); | ||
| if y != 1 { | ||
| assert_eq!((xy + u256::ONE).checked_narrowing_div_rem(y), Some((x, 1))); | ||
| } | ||
| if x != 0 { | ||
| assert_eq!( | ||
| (xy - u256::ONE).checked_narrowing_div_rem(y), | ||
| Some((x - 1, y - 1)) | ||
| ); | ||
| } | ||
| let r = ((y as f64) * 0.12345) as u128; | ||
| assert_eq!((xy + r.widen()).checked_narrowing_div_rem(y), Some((x, r))); | ||
| } | ||
| } | ||
| } |
@@ -9,2 +9,3 @@ #![allow(unknown_lints)] // FIXME(msrv) we shouldn't need this | ||
| // #[allow(dead_code)] | ||
| #[allow(dead_code)] // Some constants are only used with tests | ||
| pub trait Float: | ||
@@ -193,2 +194,11 @@ Copy | ||
| } | ||
| /// Make a best-effort attempt to canonicalize the number. Note that this is allowed | ||
| /// to be a nop and does not always quiet sNaNs. | ||
| fn canonicalize(self) -> Self { | ||
| // FIXME: LLVM often removes this. We should determine whether we can remove the operation, | ||
| // or switch to something based on `llvm.canonicalize` (which has crashes, | ||
| // <https://github.com/llvm/llvm-project/issues/32650>). | ||
| self * Self::ONE | ||
| } | ||
| } | ||
@@ -284,3 +294,6 @@ | ||
| if #[cfg(intrinsics_enabled)] { | ||
| unsafe{ core::intrinsics::$fma_intrinsic(self, y, z) } | ||
| // FIXME(msrv,bench): once our benchmark rustc version is above the | ||
| // 2022-09-23 nightly, this can be removed. | ||
| #[allow(unused_unsafe)] | ||
| unsafe { core::intrinsics::$fma_intrinsic(self, y, z) } | ||
| } else { | ||
@@ -359,2 +372,3 @@ super::super::$fma_fn(self, y, z) | ||
| /// `f32::to_bits` | ||
| #[allow(dead_code)] // workaround for false positive RUST-144060 | ||
| #[allow(unnecessary_transmutes)] // lint appears in newer versions of Rust | ||
@@ -374,2 +388,3 @@ pub const fn f32_to_bits(x: f32) -> u32 { | ||
| /// `f64::to_bits` | ||
| #[allow(dead_code)] // workaround for false positive RUST-144060 | ||
| #[allow(unnecessary_transmutes)] // lint appears in newer versions of Rust | ||
@@ -376,0 +391,0 @@ pub const fn f64_to_bits(x: f64) -> u64 { |
| //! Utilities for working with hex float formats. | ||
| use core::fmt; | ||
| use super::{Round, Status, f32_from_bits, f64_from_bits}; | ||
| use super::{Float, Round, Status, f32_from_bits, f64_from_bits}; | ||
| /// Construct a 16-bit float from hex float representation (C-style) | ||
@@ -355,124 +353,131 @@ #[cfg(f16_enabled)] | ||
| /// Format a floating point number as its IEEE hex (`%a`) representation. | ||
| pub struct Hexf<F>(pub F); | ||
| #[cfg(any(test, feature = "unstable-public-internals"))] | ||
| mod hex_fmt { | ||
| use core::fmt; | ||
| // Adapted from https://github.com/ericseppanen/hexfloat2/blob/a5c27932f0ff/src/format.rs | ||
| #[cfg(not(feature = "compiler-builtins"))] | ||
| fn fmt_any_hex<F: Float>(x: &F, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| if x.is_sign_negative() { | ||
| write!(f, "-")?; | ||
| } | ||
| use crate::support::Float; | ||
| if x.is_nan() { | ||
| return write!(f, "NaN"); | ||
| } else if x.is_infinite() { | ||
| return write!(f, "inf"); | ||
| } else if *x == F::ZERO { | ||
| return write!(f, "0x0p+0"); | ||
| } | ||
| /// Format a floating point number as its IEEE hex (`%a`) representation. | ||
| pub struct Hexf<F>(pub F); | ||
| let mut exponent = x.exp_unbiased(); | ||
| let sig = x.to_bits() & F::SIG_MASK; | ||
| // Adapted from https://github.com/ericseppanen/hexfloat2/blob/a5c27932f0ff/src/format.rs | ||
| #[cfg(not(feature = "compiler-builtins"))] | ||
| pub(super) fn fmt_any_hex<F: Float>(x: &F, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| if x.is_sign_negative() { | ||
| write!(f, "-")?; | ||
| } | ||
| let bias = F::EXP_BIAS as i32; | ||
| // The mantissa MSB needs to be shifted up to the nearest nibble. | ||
| let mshift = (4 - (F::SIG_BITS % 4)) % 4; | ||
| let sig = sig << mshift; | ||
| // The width is rounded up to the nearest char (4 bits) | ||
| let mwidth = (F::SIG_BITS as usize + 3) / 4; | ||
| let leading = if exponent == -bias { | ||
| // subnormal number means we shift our output by 1 bit. | ||
| exponent += 1; | ||
| "0." | ||
| } else { | ||
| "1." | ||
| }; | ||
| if x.is_nan() { | ||
| return write!(f, "NaN"); | ||
| } else if x.is_infinite() { | ||
| return write!(f, "inf"); | ||
| } else if *x == F::ZERO { | ||
| return write!(f, "0x0p+0"); | ||
| } | ||
| write!(f, "0x{leading}{sig:0mwidth$x}p{exponent:+}") | ||
| } | ||
| let mut exponent = x.exp_unbiased(); | ||
| let sig = x.to_bits() & F::SIG_MASK; | ||
| #[cfg(feature = "compiler-builtins")] | ||
| fn fmt_any_hex<F: Float>(_x: &F, _f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| unimplemented!() | ||
| } | ||
| let bias = F::EXP_BIAS as i32; | ||
| // The mantissa MSB needs to be shifted up to the nearest nibble. | ||
| let mshift = (4 - (F::SIG_BITS % 4)) % 4; | ||
| let sig = sig << mshift; | ||
| // The width is rounded up to the nearest char (4 bits) | ||
| let mwidth = (F::SIG_BITS as usize + 3) / 4; | ||
| let leading = if exponent == -bias { | ||
| // subnormal number means we shift our output by 1 bit. | ||
| exponent += 1; | ||
| "0." | ||
| } else { | ||
| "1." | ||
| }; | ||
| impl<F: Float> fmt::LowerHex for Hexf<F> { | ||
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| cfg_if! { | ||
| if #[cfg(feature = "compiler-builtins")] { | ||
| let _ = f; | ||
| unimplemented!() | ||
| } else { | ||
| fmt_any_hex(&self.0, f) | ||
| write!(f, "0x{leading}{sig:0mwidth$x}p{exponent:+}") | ||
| } | ||
| #[cfg(feature = "compiler-builtins")] | ||
| pub(super) fn fmt_any_hex<F: Float>(_x: &F, _f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| unimplemented!() | ||
| } | ||
| impl<F: Float> fmt::LowerHex for Hexf<F> { | ||
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| cfg_if! { | ||
| if #[cfg(feature = "compiler-builtins")] { | ||
| let _ = f; | ||
| unimplemented!() | ||
| } else { | ||
| fmt_any_hex(&self.0, f) | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| impl<F: Float> fmt::LowerHex for Hexf<(F, F)> { | ||
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| cfg_if! { | ||
| if #[cfg(feature = "compiler-builtins")] { | ||
| let _ = f; | ||
| unimplemented!() | ||
| } else { | ||
| write!(f, "({:x}, {:x})", Hexf(self.0.0), Hexf(self.0.1)) | ||
| impl<F: Float> fmt::LowerHex for Hexf<(F, F)> { | ||
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| cfg_if! { | ||
| if #[cfg(feature = "compiler-builtins")] { | ||
| let _ = f; | ||
| unimplemented!() | ||
| } else { | ||
| write!(f, "({:x}, {:x})", Hexf(self.0.0), Hexf(self.0.1)) | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| impl<F: Float> fmt::LowerHex for Hexf<(F, i32)> { | ||
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| cfg_if! { | ||
| if #[cfg(feature = "compiler-builtins")] { | ||
| let _ = f; | ||
| unimplemented!() | ||
| } else { | ||
| write!(f, "({:x}, {:x})", Hexf(self.0.0), Hexf(self.0.1)) | ||
| impl<F: Float> fmt::LowerHex for Hexf<(F, i32)> { | ||
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| cfg_if! { | ||
| if #[cfg(feature = "compiler-builtins")] { | ||
| let _ = f; | ||
| unimplemented!() | ||
| } else { | ||
| write!(f, "({:x}, {:x})", Hexf(self.0.0), Hexf(self.0.1)) | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| impl fmt::LowerHex for Hexf<i32> { | ||
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| cfg_if! { | ||
| if #[cfg(feature = "compiler-builtins")] { | ||
| let _ = f; | ||
| unimplemented!() | ||
| } else { | ||
| fmt::LowerHex::fmt(&self.0, f) | ||
| impl fmt::LowerHex for Hexf<i32> { | ||
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| cfg_if! { | ||
| if #[cfg(feature = "compiler-builtins")] { | ||
| let _ = f; | ||
| unimplemented!() | ||
| } else { | ||
| fmt::LowerHex::fmt(&self.0, f) | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| impl<T> fmt::Debug for Hexf<T> | ||
| where | ||
| Hexf<T>: fmt::LowerHex, | ||
| { | ||
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| cfg_if! { | ||
| if #[cfg(feature = "compiler-builtins")] { | ||
| let _ = f; | ||
| unimplemented!() | ||
| } else { | ||
| fmt::LowerHex::fmt(self, f) | ||
| impl<T> fmt::Debug for Hexf<T> | ||
| where | ||
| Hexf<T>: fmt::LowerHex, | ||
| { | ||
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| cfg_if! { | ||
| if #[cfg(feature = "compiler-builtins")] { | ||
| let _ = f; | ||
| unimplemented!() | ||
| } else { | ||
| fmt::LowerHex::fmt(self, f) | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| impl<T> fmt::Display for Hexf<T> | ||
| where | ||
| Hexf<T>: fmt::LowerHex, | ||
| { | ||
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| cfg_if! { | ||
| if #[cfg(feature = "compiler-builtins")] { | ||
| let _ = f; | ||
| unimplemented!() | ||
| } else { | ||
| fmt::LowerHex::fmt(self, f) | ||
| impl<T> fmt::Display for Hexf<T> | ||
| where | ||
| Hexf<T>: fmt::LowerHex, | ||
| { | ||
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| cfg_if! { | ||
| if #[cfg(feature = "compiler-builtins")] { | ||
| let _ = f; | ||
| unimplemented!() | ||
| } else { | ||
| fmt::LowerHex::fmt(self, f) | ||
| } | ||
| } | ||
@@ -483,2 +488,5 @@ } | ||
| #[cfg(any(test, feature = "unstable-public-internals"))] | ||
| pub use hex_fmt::*; | ||
| #[cfg(test)] | ||
@@ -1069,2 +1077,3 @@ mod parse_tests { | ||
| use super::*; | ||
| use crate::support::Float; | ||
@@ -1071,0 +1080,0 @@ #[test] |
| use core::{cmp, fmt, ops}; | ||
| mod narrowing_div; | ||
| pub use narrowing_div::NarrowingDiv; | ||
| /// Minimal integer implementations needed on all integer types, including wide integers. | ||
| #[allow(dead_code)] // Some constants are only used with tests | ||
| pub trait MinInt: | ||
@@ -39,4 +43,2 @@ Copy | ||
| + fmt::LowerHex | ||
| + PartialEq | ||
| + PartialOrd | ||
| + ops::AddAssign | ||
@@ -82,2 +84,3 @@ + ops::SubAssign | ||
| fn abs(self) -> Self; | ||
| fn unsigned_abs(self) -> Self::Unsigned; | ||
@@ -105,3 +108,6 @@ fn from_bool(b: bool) -> Self; | ||
| fn overflowing_sub(self, other: Self) -> (Self, bool); | ||
| fn carrying_add(self, other: Self, carry: bool) -> (Self, bool); | ||
| fn borrowing_sub(self, other: Self, borrow: bool) -> (Self, bool); | ||
| fn leading_zeros(self) -> u32; | ||
| fn trailing_zeros(self) -> u32; | ||
| fn ilog2(self) -> u32; | ||
@@ -172,2 +178,6 @@ } | ||
| fn trailing_zeros(self) -> u32 { | ||
| <Self>::trailing_zeros(self) | ||
| } | ||
| fn ilog2(self) -> u32 { | ||
@@ -179,2 +189,16 @@ // On our older MSRV, this resolves to the trait method. Which won't actually work, | ||
| } | ||
| fn carrying_add(self, other: Self, carry: bool) -> (Self, bool) { | ||
| let (ab, of1) = self.overflowing_add(other); | ||
| let (abc, of2) = ab.overflowing_add(Self::from_bool(carry)); | ||
| // `of1 && of2` is possible with signed integers if a negative sum | ||
| // overflows to `MAX` and adding the carry overflows again back to `MIN` | ||
| (abc, of1 ^ of2) | ||
| } | ||
| fn borrowing_sub(self, other: Self, borrow: bool) -> (Self, bool) { | ||
| let (ab, of1) = self.overflowing_sub(other); | ||
| let (abc, of2) = ab.overflowing_sub(Self::from_bool(borrow)); | ||
| (abc, of1 ^ of2) | ||
| } | ||
| }; | ||
@@ -211,2 +235,6 @@ } | ||
| fn unsigned_abs(self) -> Self { | ||
| unimplemented!() | ||
| } | ||
| // It makes writing macros easier if this is implemented for both signed and unsigned | ||
@@ -251,2 +279,6 @@ #[allow(clippy::wrong_self_convention)] | ||
| fn unsigned_abs(self) -> Self::Unsigned { | ||
| self.unsigned_abs() | ||
| } | ||
| fn from_unsigned(me: $uty) -> Self { | ||
@@ -274,3 +306,10 @@ me as $ity | ||
| /// primitives except for `u8`, because there is not a smaller primitive. | ||
| pub trait DInt: MinInt { | ||
| pub trait DInt: | ||
| MinInt | ||
| + ops::Add<Output = Self> | ||
| + ops::Sub<Output = Self> | ||
| + ops::Shl<u32, Output = Self> | ||
| + ops::Shr<u32, Output = Self> | ||
| + Ord | ||
| { | ||
| /// Integer that is half the bit width of the integer this trait is implemented for | ||
@@ -376,5 +415,9 @@ type H: HInt<D = Self>; | ||
| /// By default, casts should be exact. | ||
| #[track_caller] | ||
| fn cast(self) -> T; | ||
| /// Call for casts that are expected to truncate. | ||
| /// | ||
| /// In practice, this is exactly the same as `cast`; the main difference is to document intent | ||
| /// in code. `cast` may panic in debug mode. | ||
| fn cast_lossy(self) -> T; | ||
@@ -385,2 +428,3 @@ } | ||
| /// By default, casts should be exact. | ||
| #[track_caller] | ||
| fn cast_from(value: T) -> Self; | ||
@@ -387,0 +431,0 @@ |
@@ -140,12 +140,14 @@ /// `libm` cannot have dependencies, so this is vendored directly from the `cfg-if` crate | ||
| ($left:expr, $right:expr, $($tt:tt)*) => {{ | ||
| use $crate::support::Int; | ||
| let l = $left; | ||
| let r = $right; | ||
| let bits = Int::leading_zeros(l.to_bits() - l.to_bits()); // hack to get the width from the value | ||
| // hack to get width from a value | ||
| let bits = $crate::support::Int::leading_zeros(l.to_bits() - l.to_bits()); | ||
| assert!( | ||
| l.biteq(r), | ||
| "{}\nl: {l:?} ({lb:#0width$x})\nr: {r:?} ({rb:#0width$x})", | ||
| $crate::support::Float::biteq(l, r), | ||
| "{}\nl: {l:?} ({lb:#0width$x} {lh})\nr: {r:?} ({rb:#0width$x} {rh})", | ||
| format_args!($($tt)*), | ||
| lb = l.to_bits(), | ||
| lh = $crate::support::Hexf(l), | ||
| rb = r.to_bits(), | ||
| rh = $crate::support::Hexf(r), | ||
| width = ((bits / 4) + 2) as usize, | ||
@@ -152,0 +154,0 @@ |
@@ -11,5 +11,9 @@ #[macro_use] | ||
| mod int_traits; | ||
| mod modular; | ||
| #[allow(unused_imports)] | ||
| pub use big::{i256, u256}; | ||
| // Clippy seems to have a false positive | ||
| #[allow(unused_imports, clippy::single_component_path_imports)] | ||
| pub(crate) use cfg_if; | ||
| pub use env::{FpResult, Round, Status}; | ||
@@ -19,2 +23,4 @@ #[allow(unused_imports)] | ||
| pub(crate) use float_traits::{f32_from_bits, f64_from_bits}; | ||
| #[cfg(any(test, feature = "unstable-public-internals"))] | ||
| pub use hex_float::Hexf; | ||
| #[cfg(f16_enabled)] | ||
@@ -27,4 +33,5 @@ #[allow(unused_imports)] | ||
| #[allow(unused_imports)] | ||
| pub use hex_float::{Hexf, hf32, hf64}; | ||
| pub use int_traits::{CastFrom, CastInto, DInt, HInt, Int, MinInt}; | ||
| pub use hex_float::{hf32, hf64}; | ||
| pub use int_traits::{CastFrom, CastInto, DInt, HInt, Int, MinInt, NarrowingDiv}; | ||
| pub use modular::linear_mul_reduction; | ||
@@ -31,0 +38,0 @@ /// Hint to the compiler that the current path is cold. |
+1
-1
@@ -46,3 +46,3 @@ // origin: FreeBSD /usr/src/lib/msun/src/s_tan.c */ | ||
| /// `x` is specified in radians. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn tan(x: f64) -> f64 { | ||
@@ -49,0 +49,0 @@ let x1p120 = f32::from_bits(0x7b800000); // 0x1p120f === 2 ^ 120 |
+1
-1
@@ -30,3 +30,3 @@ /* origin: FreeBSD /usr/src/lib/msun/src/s_tanf.c */ | ||
| /// `x` is specified in radians. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn tanf(x: f32) -> f32 { | ||
@@ -33,0 +33,0 @@ let x64 = x as f64; |
+1
-1
@@ -11,3 +11,3 @@ use super::expm1; | ||
| /// `x` is specified in radians. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn tanh(mut x: f64) -> f64 { | ||
@@ -14,0 +14,0 @@ let mut uf: f64 = x; |
@@ -6,3 +6,3 @@ use super::expm1f; | ||
| /// `x` is specified in radians. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn tanhf(mut x: f32) -> f32 { | ||
@@ -9,0 +9,0 @@ /* x = |x| */ |
@@ -134,3 +134,3 @@ /* | ||
| /// The [Gamma function](https://en.wikipedia.org/wiki/Gamma_function) (f64). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn tgamma(mut x: f64) -> f64 { | ||
@@ -137,0 +137,0 @@ let u: u64 = x.to_bits(); |
| use super::tgamma; | ||
| /// The [Gamma function](https://en.wikipedia.org/wiki/Gamma_function) (f32). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn tgammaf(x: f32) -> f32 { | ||
| tgamma(x as f64) as f32 | ||
| } |
@@ -5,3 +5,3 @@ /// Rounds the number toward 0 to the closest integral value (f16). | ||
| #[cfg(f16_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn truncf16(x: f16) -> f16 { | ||
@@ -14,3 +14,3 @@ super::generic::trunc(x) | ||
| /// This effectively removes the decimal part of the number, leaving the integral part. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn truncf(x: f32) -> f32 { | ||
@@ -29,3 +29,3 @@ select_implementation! { | ||
| /// This effectively removes the decimal part of the number, leaving the integral part. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn trunc(x: f64) -> f64 { | ||
@@ -45,3 +45,3 @@ select_implementation! { | ||
| #[cfg(f128_enabled)] | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] | ||
| pub fn truncf128(x: f128) -> f128 { | ||
@@ -48,0 +48,0 @@ super::generic::trunc(x) |
| /// Sign of Y, magnitude of X (f32) | ||
| /// | ||
| /// Constructs a number with the magnitude (absolute value) of its | ||
| /// first argument, `x`, and the sign of its second argument, `y`. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| pub fn copysignf(x: f32, y: f32) -> f32 { | ||
| super::generic::copysign(x, y) | ||
| } |
| /// Sign of Y, magnitude of X (f128) | ||
| /// | ||
| /// Constructs a number with the magnitude (absolute value) of its | ||
| /// first argument, `x`, and the sign of its second argument, `y`. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| pub fn copysignf128(x: f128, y: f128) -> f128 { | ||
| super::generic::copysign(x, y) | ||
| } |
| /// Sign of Y, magnitude of X (f16) | ||
| /// | ||
| /// Constructs a number with the magnitude (absolute value) of its | ||
| /// first argument, `x`, and the sign of its second argument, `y`. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| pub fn copysignf16(x: f16, y: f16) -> f16 { | ||
| super::generic::copysign(x, y) | ||
| } |
| /// Absolute value (magnitude) (f32) | ||
| /// | ||
| /// Calculates the absolute value (magnitude) of the argument `x`, | ||
| /// by direct manipulation of the bit representation of `x`. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| pub fn fabsf(x: f32) -> f32 { | ||
| select_implementation! { | ||
| name: fabsf, | ||
| use_arch: all(target_arch = "wasm32", intrinsics_enabled), | ||
| args: x, | ||
| } | ||
| super::generic::fabs(x) | ||
| } | ||
| // PowerPC tests are failing on LLVM 13: https://github.com/rust-lang/rust/issues/88520 | ||
| #[cfg(not(target_arch = "powerpc64"))] | ||
| #[cfg(test)] | ||
| mod tests { | ||
| use super::*; | ||
| #[test] | ||
| fn sanity_check() { | ||
| assert_eq!(fabsf(-1.0), 1.0); | ||
| assert_eq!(fabsf(2.8), 2.8); | ||
| } | ||
| /// The spec: https://en.cppreference.com/w/cpp/numeric/math/fabs | ||
| #[test] | ||
| fn spec_tests() { | ||
| assert!(fabsf(f32::NAN).is_nan()); | ||
| for f in [0.0, -0.0].iter().copied() { | ||
| assert_eq!(fabsf(f), 0.0); | ||
| } | ||
| for f in [f32::INFINITY, f32::NEG_INFINITY].iter().copied() { | ||
| assert_eq!(fabsf(f), f32::INFINITY); | ||
| } | ||
| } | ||
| } |
| /// Absolute value (magnitude) (f128) | ||
| /// | ||
| /// Calculates the absolute value (magnitude) of the argument `x`, | ||
| /// by direct manipulation of the bit representation of `x`. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| pub fn fabsf128(x: f128) -> f128 { | ||
| super::generic::fabs(x) | ||
| } | ||
| #[cfg(test)] | ||
| mod tests { | ||
| use super::*; | ||
| #[test] | ||
| fn sanity_check() { | ||
| assert_eq!(fabsf128(-1.0), 1.0); | ||
| assert_eq!(fabsf128(2.8), 2.8); | ||
| } | ||
| /// The spec: https://en.cppreference.com/w/cpp/numeric/math/fabs | ||
| #[test] | ||
| fn spec_tests() { | ||
| assert!(fabsf128(f128::NAN).is_nan()); | ||
| for f in [0.0, -0.0].iter().copied() { | ||
| assert_eq!(fabsf128(f), 0.0); | ||
| } | ||
| for f in [f128::INFINITY, f128::NEG_INFINITY].iter().copied() { | ||
| assert_eq!(fabsf128(f), f128::INFINITY); | ||
| } | ||
| } | ||
| } |
| /// Absolute value (magnitude) (f16) | ||
| /// | ||
| /// Calculates the absolute value (magnitude) of the argument `x`, | ||
| /// by direct manipulation of the bit representation of `x`. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| pub fn fabsf16(x: f16) -> f16 { | ||
| super::generic::fabs(x) | ||
| } | ||
| #[cfg(test)] | ||
| mod tests { | ||
| use super::*; | ||
| #[test] | ||
| fn sanity_check() { | ||
| assert_eq!(fabsf16(-1.0), 1.0); | ||
| assert_eq!(fabsf16(2.8), 2.8); | ||
| } | ||
| /// The spec: https://en.cppreference.com/w/cpp/numeric/math/fabs | ||
| #[test] | ||
| fn spec_tests() { | ||
| assert!(fabsf16(f16::NAN).is_nan()); | ||
| for f in [0.0, -0.0].iter().copied() { | ||
| assert_eq!(fabsf16(f), 0.0); | ||
| } | ||
| for f in [f16::INFINITY, f16::NEG_INFINITY].iter().copied() { | ||
| assert_eq!(fabsf16(f), f16::INFINITY); | ||
| } | ||
| } | ||
| } |
| /// Positive difference (f32) | ||
| /// | ||
| /// Determines the positive difference between arguments, returning: | ||
| /// * x - y if x > y, or | ||
| /// * +0 if x <= y, or | ||
| /// * NAN if either argument is NAN. | ||
| /// | ||
| /// A range error may occur. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| pub fn fdimf(x: f32, y: f32) -> f32 { | ||
| super::generic::fdim(x, y) | ||
| } |
| /// Positive difference (f128) | ||
| /// | ||
| /// Determines the positive difference between arguments, returning: | ||
| /// * x - y if x > y, or | ||
| /// * +0 if x <= y, or | ||
| /// * NAN if either argument is NAN. | ||
| /// | ||
| /// A range error may occur. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| pub fn fdimf128(x: f128, y: f128) -> f128 { | ||
| super::generic::fdim(x, y) | ||
| } |
| /// Positive difference (f16) | ||
| /// | ||
| /// Determines the positive difference between arguments, returning: | ||
| /// * x - y if x > y, or | ||
| /// * +0 if x <= y, or | ||
| /// * NAN if either argument is NAN. | ||
| /// | ||
| /// A range error may occur. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| pub fn fdimf16(x: f16, y: f16) -> f16 { | ||
| super::generic::fdim(x, y) | ||
| } |
| /// Floor (f32) | ||
| /// | ||
| /// Finds the nearest integer less than or equal to `x`. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| pub fn floorf(x: f32) -> f32 { | ||
| select_implementation! { | ||
| name: floorf, | ||
| use_arch: all(target_arch = "wasm32", intrinsics_enabled), | ||
| args: x, | ||
| } | ||
| return super::generic::floor(x); | ||
| } |
| /// Floor (f128) | ||
| /// | ||
| /// Finds the nearest integer less than or equal to `x`. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| pub fn floorf128(x: f128) -> f128 { | ||
| return super::generic::floor(x); | ||
| } |
| /// Floor (f16) | ||
| /// | ||
| /// Finds the nearest integer less than or equal to `x`. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| pub fn floorf16(x: f16) -> f16 { | ||
| return super::generic::floor(x); | ||
| } |
| /// Calculate the remainder of `x / y`, the precise result of `x - trunc(x / y) * y`. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| pub fn fmodf(x: f32, y: f32) -> f32 { | ||
| super::generic::fmod(x, y) | ||
| } |
| /// Calculate the remainder of `x / y`, the precise result of `x - trunc(x / y) * y`. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| pub fn fmodf128(x: f128, y: f128) -> f128 { | ||
| super::generic::fmod(x, y) | ||
| } |
| /// Calculate the remainder of `x / y`, the precise result of `x - trunc(x / y) * y`. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| pub fn fmodf16(x: f16, y: f16) -> f16 { | ||
| super::generic::fmod(x, y) | ||
| } |
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| pub fn ldexpf(x: f32, n: i32) -> f32 { | ||
| super::scalbnf(x, n) | ||
| } |
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| pub fn ldexpf128(x: f128, n: i32) -> f128 { | ||
| super::scalbnf128(x, n) | ||
| } |
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| pub fn ldexpf16(x: f16, n: i32) -> f16 { | ||
| super::scalbnf16(x, n) | ||
| } |
| /// Round `x` to the nearest integer, breaking ties away from zero. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| pub fn roundf(x: f32) -> f32 { | ||
| super::generic::round(x) | ||
| } |
| /// Round `x` to the nearest integer, breaking ties away from zero. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| pub fn roundf128(x: f128) -> f128 { | ||
| super::generic::round(x) | ||
| } |
| /// Round `x` to the nearest integer, breaking ties away from zero. | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| pub fn roundf16(x: f16) -> f16 { | ||
| super::generic::round(x) | ||
| } |
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| pub fn scalbnf(x: f32, n: i32) -> f32 { | ||
| super::generic::scalbn(x, n) | ||
| } |
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| pub fn scalbnf128(x: f128, n: i32) -> f128 { | ||
| super::generic::scalbn(x, n) | ||
| } |
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| pub fn scalbnf16(x: f16, n: i32) -> f16 { | ||
| super::generic::scalbn(x, n) | ||
| } |
| /// The square root of `x` (f32). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| pub fn sqrtf(x: f32) -> f32 { | ||
| select_implementation! { | ||
| name: sqrtf, | ||
| use_arch: any( | ||
| all(target_arch = "aarch64", target_feature = "neon"), | ||
| all(target_arch = "wasm32", intrinsics_enabled), | ||
| target_feature = "sse2" | ||
| ), | ||
| args: x, | ||
| } | ||
| super::generic::sqrt(x) | ||
| } |
| /// The square root of `x` (f128). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| pub fn sqrtf128(x: f128) -> f128 { | ||
| return super::generic::sqrt(x); | ||
| } |
| /// The square root of `x` (f16). | ||
| #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] | ||
| pub fn sqrtf16(x: f16) -> f16 { | ||
| select_implementation! { | ||
| name: sqrtf16, | ||
| use_arch: all(target_arch = "aarch64", target_feature = "fp16"), | ||
| args: x, | ||
| } | ||
| return super::generic::sqrt(x); | ||
| } |
| /// Rounds the number toward 0 to the closest integral value (f32). | ||
| /// | ||
| /// 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 truncf(x: f32) -> f32 { | ||
| select_implementation! { | ||
| name: truncf, | ||
| use_arch: all(target_arch = "wasm32", intrinsics_enabled), | ||
| args: x, | ||
| } | ||
| super::generic::trunc(x) | ||
| } | ||
| // PowerPC tests are failing on LLVM 13: https://github.com/rust-lang/rust/issues/88520 | ||
| #[cfg(not(target_arch = "powerpc64"))] | ||
| #[cfg(test)] | ||
| mod tests { | ||
| #[test] | ||
| fn sanity_check() { | ||
| assert_eq!(super::truncf(1.1), 1.0); | ||
| } | ||
| } |
| /// 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) | ||
| } |
| /// 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) | ||
| } |
Sorry, the diff of this file is not supported yet