#![no_std]
#![deny(missing_docs)]
#[cfg(any(test, feature = "std"))]
#[macro_use]
extern crate std;
#[cfg(feature = "serde")]
extern crate serde;
#[cfg(test)]
#[macro_use]
extern crate quickcheck;
use core::cmp::Ordering;
use core::fmt::{self, Display, Formatter, LowerExp, UpperExp};
use core::hash::{Hash, Hasher};
use core::num::FpCategory;
use core::ops::{Add, Div, Mul, Neg, Rem, Sub};
use num_traits::float::FloatCore;
#[cfg(feature = "std")]
use num_traits::Float;
#[cfg(feature = "serde")]
use serde::de::{Deserialize, Deserializer, Error};
#[cfg(feature = "serde")]
use serde::ser::{Serialize, Serializer};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct NaN;
impl Display for NaN {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "NaN")
}
}
#[cfg(feature = "std")]
impl std::error::Error for NaN {}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct ResultFloat<F>(F);
impl<F> Eq for ResultFloat<F> where F: PartialEq {}
impl<F> PartialOrd for ResultFloat<F>
where
F: PartialOrd,
{
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.0.partial_cmp(&other.0)
}
}
impl<F> Ord for ResultFloat<F>
where
F: PartialOrd,
{
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(other).unwrap()
}
}
pub type Result<F> = core::result::Result<ResultFloat<F>, NaN>;
pub type Rf32 = ResultFloat<f32>;
pub type Rf64 = ResultFloat<f64>;
fn rfu<F>(f: F) -> ResultFloat<F>
where
F: FloatCore,
{
rf(f).unwrap()
}
impl<F> ResultFloat<F>
where
F: FloatCore,
{
#[inline]
pub fn new(v: F) -> Result<F> {
if v.is_nan() {
Err(NaN)
} else {
Ok(ResultFloat(v))
}
}
#[inline]
pub fn raw(self) -> F {
self.0
}
#[inline]
pub fn is_infinite(self) -> bool {
self.0.is_infinite()
}
#[inline]
pub fn is_finite(self) -> bool {
self.0.is_finite()
}
#[inline]
pub fn is_normal(self) -> bool {
self.0.is_normal()
}
#[inline]
pub fn classify(self) -> FpCategory {
self.0.classify()
}
#[inline]
pub fn floor(self) -> Self {
rfu(self.0.floor())
}
#[inline]
pub fn ceil(self) -> Self {
rfu(self.0.ceil())
}
#[inline]
pub fn round(self) -> Self {
rfu(self.0.round())
}
#[inline]
pub fn trunc(self) -> Self {
rfu(self.0.trunc())
}
#[inline]
pub fn fract(self) -> Result<F> {
rf(self.0.fract())
}
#[inline]
pub fn abs(self) -> Self {
rfu(self.0.abs())
}
#[inline]
pub fn signum(self) -> Self {
rfu(self.0.signum())
}
#[inline]
pub fn is_sign_positive(self) -> bool {
self.0.is_sign_positive()
}
#[inline]
pub fn is_sign_negative(self) -> bool {
self.0.is_sign_negative()
}
#[inline]
pub fn recip(self) -> Self {
rfu(self.0.recip())
}
#[inline]
pub fn powi(self, n: i32) -> Self {
rfu(self.0.powi(n))
}
#[inline]
pub fn to_degrees(self) -> Result<F> {
rf(self.0.to_degrees())
}
#[inline]
pub fn to_radians(self) -> Result<F> {
rf(self.0.to_radians())
}
}
#[cfg(feature = "std")]
impl<F> ResultFloat<F>
where
F: FloatCore + Float,
{
#[inline]
pub fn mul_add(self, a: Self, b: Self) -> Result<F> {
rf(self.0.mul_add(a.0, b.0))
}
#[inline]
pub fn powf(self, n: Self) -> Result<F> {
rf(self.0.powf(n.0))
}
#[inline]
pub fn sqrt(self) -> Result<F> {
rf(self.0.sqrt())
}
#[inline]
pub fn exp(self) -> Self {
rfu(self.0.exp())
}
#[inline]
pub fn exp2(self) -> Self {
rfu(self.0.exp2())
}
#[inline]
pub fn ln(self) -> Result<F> {
rf(self.0.ln())
}
#[inline]
pub fn log(self, base: Self) -> Result<F> {
rf(self.0.log(base.0))
}
#[inline]
pub fn log2(self) -> Result<F> {
rf(self.0.log2())
}
#[inline]
pub fn log10(self) -> Result<F> {
rf(self.0.log10())
}
#[inline]
pub fn cbrt(self) -> Self {
rfu(self.0.cbrt())
}
#[inline]
pub fn hypot(self, other: Self) -> Self {
rfu(self.0.hypot(other.0))
}
#[inline]
pub fn sin(self) -> Result<F> {
rf(self.0.sin())
}
#[inline]
pub fn cos(self) -> Result<F> {
rf(self.0.cos())
}
#[inline]
pub fn tan(self) -> Result<F> {
rf(self.0.tan())
}
#[inline]
pub fn asin(self) -> Result<F> {
rf(self.0.asin())
}
#[inline]
pub fn acos(self) -> Result<F> {
rf(self.0.acos())
}
#[inline]
pub fn atan(self) -> Self {
rfu(self.0.atan())
}
#[inline]
pub fn atan2(self, other: Self) -> Self {
rfu(self.0.atan2(other.0))
}
#[inline]
pub fn sin_cos(self) -> core::result::Result<(Self, Self), NaN> {
let (a, b) = self.0.sin_cos();
Ok((rf(a)?, rf(b)?))
}
#[inline]
pub fn exp_m1(self) -> Self {
rfu(self.0.exp_m1())
}
#[inline]
pub fn ln_1p(self) -> Result<F> {
rf(self.0.ln_1p())
}
#[inline]
pub fn sinh(self) -> Self {
rfu(self.0.sinh())
}
#[inline]
pub fn cosh(self) -> Self {
rfu(self.0.cosh())
}
#[inline]
pub fn tanh(self) -> Self {
rfu(self.0.tanh())
}
#[inline]
pub fn asinh(self) -> Self {
rfu(self.0.asinh())
}
#[inline]
pub fn acosh(self) -> Result<F> {
rf(self.0.acosh())
}
#[inline]
pub fn atanh(self) -> Result<F> {
rf(self.0.atanh())
}
}
impl ResultFloat<f32> {
#[inline]
pub fn to_bits(self) -> u32 {
self.0.to_bits()
}
#[inline]
pub fn from_bits(v: u32) -> Result<f32> {
rf(f32::from_bits(v))
}
}
impl ResultFloat<f64> {
#[inline]
pub fn to_bits(self) -> u64 {
self.0.to_bits()
}
#[inline]
pub fn from_bits(v: u64) -> Result<f64> {
rf(f64::from_bits(v))
}
}
macro_rules! op_impl {
($( $imp:ident $method:ident $op:tt ) *) => {
$(
impl<F> $imp for ResultFloat<F>
where
F: FloatCore
{
type Output = Result<F>;
#[inline]
fn $method(self, other: Self) -> Result<F> {
rf(self.0 $op other.0)
}
}
)*
};
}
op_impl!(
Add add +
Sub sub -
Mul mul *
Div div /
Rem rem %
);
impl<F> Neg for ResultFloat<F>
where
F: FloatCore,
{
type Output = ResultFloat<F>;
#[inline]
fn neg(self) -> ResultFloat<F> {
rfu(-self.0)
}
}
macro_rules! format_impl {
($($imp:ident)*) => {
$(
impl<F> $imp for ResultFloat<F>
where
F: $imp,
{
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
)*
};
}
format_impl!(Display LowerExp UpperExp);
macro_rules! hash {
($($t:ident $method:ident)*) => {
$(
impl Hash for ResultFloat<$t> {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
state.$method(if self.0 == 0.0 { 0 } else { self.0.to_bits() })
}
}
)*
};
}
hash!(f32 write_u32 f64 write_u64);
impl<F> Default for ResultFloat<F>
where
F: Default + FloatCore,
{
fn default() -> ResultFloat<F> {
rf(F::default()).unwrap()
}
}
#[cfg(feature = "serde")]
impl<F> Serialize for ResultFloat<F>
where
F: Serialize,
{
fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
self.0.serialize(serializer)
}
}
#[cfg(feature = "serde")]
impl<'de, F> Deserialize<'de> for ResultFloat<F>
where
F: Deserialize<'de> + FloatCore,
{
fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
rf(F::deserialize(deserializer)?).map_err(D::Error::custom)
}
}
#[inline]
pub fn rf<F>(v: F) -> Result<F>
where
F: FloatCore,
{
ResultFloat::new(v)
}
#[cfg(test)]
mod tests {
use crate::{rf, Rf64};
use core::hash::{Hash, Hasher};
use quickcheck::TestResult;
struct TestHasher {
value: u64,
shift: u8,
}
impl Hasher for TestHasher {
fn finish(&self) -> u64 {
self.value
}
fn write(&mut self, bytes: &[u8]) {
for &b in bytes {
self.value += u64::from(b) << self.shift;
self.shift += 8;
}
}
}
fn hash(v: impl Hash) -> u64 {
let mut hasher = TestHasher { value: 0, shift: 0 };
v.hash(&mut hasher);
hasher.finish()
}
#[test]
fn test_positive_zero_f32() {
assert_eq!(hash(rf(0.0f32).unwrap()), 0);
}
#[test]
fn test_negative_zero_f32() {
assert_eq!(hash(rf(-0.0f32).unwrap()), 0);
}
#[test]
fn test_positive_zero_f64() {
assert_eq!(hash(rf(0.0).unwrap()), 0);
}
#[test]
fn test_negative_zero_f64() {
assert_eq!(hash(rf(-0.0).unwrap()), 0);
}
macro_rules! rf {
($x:expr) => {{
let x = $x;
if x.is_nan() {
return TestResult::discard();
}
rf(x).unwrap()
}};
}
quickcheck! {
fn floor(x: f64) -> TestResult {
TestResult::from_bool(rf!(x).floor().raw() == x.floor())
}
fn hash_f32(x: f32) -> TestResult {
let x = rf!(x);
if x == rf!(0.0f32) && x.is_sign_negative() {
TestResult::discard()
} else {
TestResult::from_bool(hash(x) == x.to_bits().into())
}
}
fn hash_f64(x: f64) -> TestResult {
let x = rf!(x);
if x == rf!(0.0f64) && x.is_sign_negative() {
TestResult::discard()
} else {
TestResult::from_bool(hash(x) == x.to_bits())
}
}
}
#[test]
fn test_default() {
assert_eq!(Rf64::default(), rf(0.0).unwrap());
}
#[cfg(feature = "serde")]
#[test]
fn test_ser_de() {
extern crate serde_test;
serde_test::assert_tokens(&rf(3.14).unwrap(), &[serde_test::Token::F64(3.14)]);
}
#[test]
fn test_display() {
assert_eq!(format!("{}", rf(3.14).unwrap()), "3.14");
}
}