Fork of https://github.com/oxigraph/oxigraph.git for the purpose of NextGraph project
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
282 lines
5.9 KiB
282 lines
5.9 KiB
3 years ago
|
use crate::xsd::Float;
|
||
4 years ago
|
use std::cmp::Ordering;
|
||
|
use std::fmt;
|
||
|
use std::hash::{Hash, Hasher};
|
||
|
use std::num::ParseFloatError;
|
||
|
use std::ops::{Add, Div, Mul, Neg, Sub};
|
||
|
use std::str::FromStr;
|
||
|
|
||
|
/// [XML Schema `double` datatype](https://www.w3.org/TR/xmlschema11-2/#double) implementation.
|
||
|
///
|
||
|
/// The "==" implementation is identity, not equality
|
||
|
#[derive(Debug, Clone, Copy, Default)]
|
||
|
#[repr(transparent)]
|
||
|
pub struct Double {
|
||
|
value: f64,
|
||
|
}
|
||
|
|
||
|
impl Double {
|
||
|
#[inline]
|
||
|
pub fn from_be_bytes(bytes: [u8; 8]) -> Self {
|
||
|
Self {
|
||
|
value: f64::from_be_bytes(bytes),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[inline]
|
||
|
pub fn to_be_bytes(self) -> [u8; 8] {
|
||
|
self.value.to_be_bytes()
|
||
|
}
|
||
|
|
||
|
/// [fn:abs](https://www.w3.org/TR/xpath-functions/#func-abs)
|
||
|
#[inline]
|
||
|
pub fn abs(self) -> Self {
|
||
|
self.value.abs().into()
|
||
|
}
|
||
|
|
||
|
/// [fn:ceiling](https://www.w3.org/TR/xpath-functions/#func-ceiling)
|
||
|
#[inline]
|
||
|
pub fn ceil(self) -> Self {
|
||
|
self.value.ceil().into()
|
||
|
}
|
||
|
|
||
|
/// [fn:floor](https://www.w3.org/TR/xpath-functions/#func-floor)
|
||
|
#[inline]
|
||
|
pub fn floor(self) -> Self {
|
||
|
self.value.floor().into()
|
||
|
}
|
||
|
|
||
|
/// [fn:round](https://www.w3.org/TR/xpath-functions/#func-round)
|
||
|
#[inline]
|
||
|
pub fn round(self) -> Self {
|
||
|
self.value.round().into()
|
||
|
}
|
||
|
|
||
|
/// Casts i64 into `Double`
|
||
|
#[inline]
|
||
|
#[allow(clippy::cast_precision_loss)]
|
||
|
pub fn from_i64(value: i64) -> Self {
|
||
|
Self {
|
||
|
value: value as f64,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Casts `Double` into i64 without taking care of loss
|
||
|
#[inline]
|
||
|
#[allow(clippy::cast_possible_truncation)]
|
||
|
pub fn to_i64(self) -> i64 {
|
||
|
self.value as i64
|
||
|
}
|
||
|
|
||
|
/// Casts `Double` into f32 without taking care of loss
|
||
|
#[inline]
|
||
|
#[allow(clippy::cast_possible_truncation)]
|
||
|
pub fn to_f32(self) -> f32 {
|
||
|
self.value as f32
|
||
|
}
|
||
|
|
||
|
/// Creates a `bool` from a `Decimal` according to xsd:boolean cast constraints
|
||
|
#[inline]
|
||
|
pub fn to_bool(self) -> bool {
|
||
|
self.value != 0. && !self.value.is_nan()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl From<Double> for f64 {
|
||
|
#[inline]
|
||
|
fn from(value: Double) -> Self {
|
||
|
value.value
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl From<f64> for Double {
|
||
|
#[inline]
|
||
|
fn from(value: f64) -> Self {
|
||
|
Self { value }
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl From<i8> for Double {
|
||
|
#[inline]
|
||
|
fn from(value: i8) -> Self {
|
||
|
Self {
|
||
|
value: value.into(),
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl From<i16> for Double {
|
||
|
#[inline]
|
||
|
fn from(value: i16) -> Self {
|
||
|
Self {
|
||
|
value: value.into(),
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl From<i32> for Double {
|
||
|
#[inline]
|
||
|
fn from(value: i32) -> Self {
|
||
|
Self {
|
||
|
value: value.into(),
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl From<u8> for Double {
|
||
|
#[inline]
|
||
|
fn from(value: u8) -> Self {
|
||
|
Self {
|
||
|
value: value.into(),
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl From<u16> for Double {
|
||
|
#[inline]
|
||
|
fn from(value: u16) -> Self {
|
||
|
Self {
|
||
|
value: value.into(),
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl From<u32> for Double {
|
||
|
#[inline]
|
||
|
fn from(value: u32) -> Self {
|
||
|
Self {
|
||
|
value: value.into(),
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl From<Float> for Double {
|
||
|
#[inline]
|
||
|
fn from(value: Float) -> Self {
|
||
|
Self {
|
||
|
value: value.into(),
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl FromStr for Double {
|
||
|
type Err = ParseFloatError;
|
||
|
|
||
|
/// Parses decimals lexical mapping
|
||
|
#[inline]
|
||
|
fn from_str(input: &str) -> Result<Self, ParseFloatError> {
|
||
|
Ok(f64::from_str(input)?.into())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl fmt::Display for Double {
|
||
|
#[inline]
|
||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||
|
if self.value == f64::INFINITY {
|
||
|
f.write_str("INF")
|
||
|
} else if self.value == f64::NEG_INFINITY {
|
||
|
f.write_str("-INF")
|
||
|
} else {
|
||
|
self.value.fmt(f)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl PartialEq for Double {
|
||
|
#[inline]
|
||
|
fn eq(&self, other: &Self) -> bool {
|
||
|
self.value.to_ne_bytes() == other.value.to_ne_bytes()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Eq for Double {}
|
||
|
|
||
|
impl PartialOrd for Double {
|
||
|
#[inline]
|
||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||
|
self.value.partial_cmp(&other.value)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Hash for Double {
|
||
|
#[inline]
|
||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||
|
state.write(&self.value.to_ne_bytes())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Neg for Double {
|
||
|
type Output = Self;
|
||
|
|
||
|
#[inline]
|
||
|
fn neg(self) -> Self {
|
||
|
(-self.value).into()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Add for Double {
|
||
|
type Output = Self;
|
||
|
|
||
|
#[inline]
|
||
|
fn add(self, rhs: Self) -> Self {
|
||
|
(self.value + rhs.value).into()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Sub for Double {
|
||
|
type Output = Self;
|
||
|
|
||
|
#[inline]
|
||
|
fn sub(self, rhs: Self) -> Self {
|
||
|
(self.value - rhs.value).into()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Mul for Double {
|
||
|
type Output = Self;
|
||
|
|
||
|
#[inline]
|
||
|
fn mul(self, rhs: Self) -> Self {
|
||
|
(self.value * rhs.value).into()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Div for Double {
|
||
|
type Output = Self;
|
||
|
|
||
|
#[inline]
|
||
|
fn div(self, rhs: Self) -> Self {
|
||
|
(self.value / rhs.value).into()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[cfg(test)]
|
||
|
mod tests {
|
||
|
use super::*;
|
||
|
|
||
|
#[test]
|
||
|
fn eq() {
|
||
|
assert_eq!(Double::from(0_f64), Double::from(0_f64));
|
||
|
assert_eq!(Double::from(f64::NAN), Double::from(f64::NAN));
|
||
|
assert_ne!(Double::from(-0.), Double::from(0.));
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn from_str() -> Result<(), ParseFloatError> {
|
||
|
assert_eq!(Double::from(f64::NAN), Double::from_str("NaN")?);
|
||
|
assert_eq!(Double::from(f64::INFINITY), Double::from_str("INF")?);
|
||
|
assert_eq!(Double::from(f64::INFINITY), Double::from_str("+INF")?);
|
||
|
assert_eq!(Double::from(f64::NEG_INFINITY), Double::from_str("-INF")?);
|
||
|
assert_eq!(Double::from(0.), Double::from_str("0.0E0")?);
|
||
|
assert_eq!(Double::from(-0.), Double::from_str("-0.0E0")?);
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn to_string() {
|
||
|
assert_eq!("NaN", Double::from(f64::NAN).to_string());
|
||
|
assert_eq!("INF", Double::from(f64::INFINITY).to_string());
|
||
|
assert_eq!("-INF", Double::from(f64::NEG_INFINITY).to_string());
|
||
|
}
|
||
|
}
|