use crate::{Boolean, Double, Integer};
use std::cmp::Ordering;
use std::fmt;
use std::num::ParseFloatError;
use std::ops::{Add, Div, Mul, Neg, Sub};
use std::str::FromStr;
#[derive(Debug, Clone, Copy, Default, PartialEq)]
#[repr(transparent)]
pub struct Float {
value: f32,
}
impl Float {
pub const INFINITY: Self = Self {
value: f32::INFINITY,
};
pub const MAX: Self = Self { value: f32::MAX };
pub const MIN: Self = Self { value: f32::MIN };
pub const NAN: Self = Self { value: f32::NAN };
pub const NEG_INFINITY: Self = Self {
value: f32::NEG_INFINITY,
};
#[inline]
#[must_use]
pub fn from_be_bytes(bytes: [u8; 4]) -> Self {
Self {
value: f32::from_be_bytes(bytes),
}
}
#[inline]
#[must_use]
pub fn to_be_bytes(self) -> [u8; 4] {
self.value.to_be_bytes()
}
#[inline]
#[must_use]
pub fn abs(self) -> Self {
self.value.abs().into()
}
#[inline]
#[must_use]
pub fn ceil(self) -> Self {
self.value.ceil().into()
}
#[inline]
#[must_use]
pub fn floor(self) -> Self {
self.value.floor().into()
}
#[inline]
#[must_use]
pub fn round(self) -> Self {
self.value.round().into()
}
#[inline]
#[must_use]
pub fn is_nan(self) -> bool {
self.value.is_nan()
}
#[inline]
#[must_use]
pub fn is_finite(self) -> bool {
self.value.is_finite()
}
#[inline]
#[must_use]
pub fn is_identical_with(self, other: Self) -> bool {
self.value.to_bits() == other.value.to_bits()
}
}
impl From<Float> for f32 {
#[inline]
fn from(value: Float) -> Self {
value.value
}
}
impl From<Float> for f64 {
#[inline]
fn from(value: Float) -> Self {
value.value.into()
}
}
impl From<f32> for Float {
#[inline]
fn from(value: f32) -> Self {
Self { value }
}
}
impl From<i8> for Float {
#[inline]
fn from(value: i8) -> Self {
Self {
value: value.into(),
}
}
}
impl From<i16> for Float {
#[inline]
fn from(value: i16) -> Self {
Self {
value: value.into(),
}
}
}
impl From<u8> for Float {
#[inline]
fn from(value: u8) -> Self {
Self {
value: value.into(),
}
}
}
impl From<u16> for Float {
#[inline]
fn from(value: u16) -> Self {
Self {
value: value.into(),
}
}
}
impl From<Boolean> for Float {
#[inline]
fn from(value: Boolean) -> Self {
f32::from(bool::from(value)).into()
}
}
impl From<Integer> for Float {
#[inline]
#[allow(clippy::cast_precision_loss)]
fn from(value: Integer) -> Self {
(i64::from(value) as f32).into()
}
}
impl From<Double> for Float {
#[inline]
#[allow(clippy::cast_possible_truncation)]
fn from(value: Double) -> Self {
Self {
value: f64::from(value) as f32,
}
}
}
impl FromStr for Float {
type Err = ParseFloatError;
#[inline]
fn from_str(input: &str) -> Result<Self, Self::Err> {
Ok(f32::from_str(input)?.into())
}
}
impl fmt::Display for Float {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.value == f32::INFINITY {
f.write_str("INF")
} else if self.value == f32::NEG_INFINITY {
f.write_str("-INF")
} else {
self.value.fmt(f)
}
}
}
impl PartialOrd for Float {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.value.partial_cmp(&other.value)
}
}
impl Neg for Float {
type Output = Self;
#[inline]
fn neg(self) -> Self {
(-self.value).into()
}
}
impl Add for Float {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self {
(self.value + rhs.value).into()
}
}
impl Sub for Float {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self {
(self.value - rhs.value).into()
}
}
impl Mul for Float {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self {
(self.value * rhs.value).into()
}
}
impl Div for Float {
type Output = Self;
#[inline]
fn div(self, rhs: Self) -> Self {
(self.value / rhs.value).into()
}
}
#[cfg(test)]
#[allow(clippy::panic_in_result_fn)]
mod tests {
use super::*;
#[test]
fn eq() {
assert_eq!(Float::from(0.), Float::from(0.));
assert_ne!(Float::NAN, Float::NAN);
assert_eq!(Float::from(-0.), Float::from(0.));
}
#[test]
fn cmp() {
assert_eq!(
Float::from(0.).partial_cmp(&Float::from(0.)),
Some(Ordering::Equal)
);
assert_eq!(
Float::INFINITY.partial_cmp(&Float::MAX),
Some(Ordering::Greater)
);
assert_eq!(
Float::NEG_INFINITY.partial_cmp(&Float::MIN),
Some(Ordering::Less)
);
assert_eq!(Float::NAN.partial_cmp(&Float::from(0.)), None);
assert_eq!(Float::NAN.partial_cmp(&Float::NAN), None);
assert_eq!(
Float::from(0.).partial_cmp(&Float::from(-0.)),
Some(Ordering::Equal)
);
}
#[test]
fn is_identical_with() {
assert!(Float::from(0.).is_identical_with(Float::from(0.)));
assert!(Float::NAN.is_identical_with(Float::NAN));
assert!(!Float::from(-0.).is_identical_with(Float::from(0.)));
}
#[test]
fn from_str() -> Result<(), ParseFloatError> {
assert_eq!(Float::from_str("NaN")?.to_string(), "NaN");
assert_eq!(Float::from_str("INF")?.to_string(), "INF");
assert_eq!(Float::from_str("+INF")?.to_string(), "INF");
assert_eq!(Float::from_str("-INF")?.to_string(), "-INF");
assert_eq!(Float::from_str("0.0E0")?.to_string(), "0");
assert_eq!(Float::from_str("-0.0E0")?.to_string(), "-0");
assert_eq!(Float::from_str("0.1e1")?.to_string(), "1");
assert_eq!(Float::from_str("-0.1e1")?.to_string(), "-1");
assert_eq!(Float::from_str("1.e1")?.to_string(), "10");
assert_eq!(Float::from_str("-1.e1")?.to_string(), "-10");
assert_eq!(Float::from_str("1")?.to_string(), "1");
assert_eq!(Float::from_str("-1")?.to_string(), "-1");
assert_eq!(Float::from_str("1.")?.to_string(), "1");
assert_eq!(Float::from_str("-1.")?.to_string(), "-1");
assert_eq!(Float::from_str(&f32::MIN.to_string())?, Float::MIN);
assert_eq!(Float::from_str(&f32::MAX.to_string())?, Float::MAX);
Ok(())
}
}