diff --git a/lib/src/model/literal.rs b/lib/src/model/literal.rs index 71c2b724..3c8717f4 100644 --- a/lib/src/model/literal.rs +++ b/lib/src/model/literal.rs @@ -265,6 +265,13 @@ impl From for Literal { impl From for Literal { #[inline] fn from(value: f32) -> Self { + Float::from(value).into() + } +} + +impl From for Literal { + #[inline] + fn from(value: Float) -> Self { Self(LiteralContent::TypedLiteral { value: value.to_string(), datatype: xsd::FLOAT.into(), @@ -275,6 +282,13 @@ impl From for Literal { impl From for Literal { #[inline] fn from(value: f64) -> Self { + Double::from(value).into() + } +} + +impl From for Literal { + #[inline] + fn from(value: Double) -> Self { Self(LiteralContent::TypedLiteral { value: value.to_string(), datatype: xsd::DOUBLE.into(), @@ -621,4 +635,14 @@ mod tests { LiteralRef::new_typed_literal("foo", xsd::STRING) ); } + + #[test] + fn test_float_format() { + assert_eq!("INF", Literal::from(f32::INFINITY).value()); + assert_eq!("INF", Literal::from(f64::INFINITY).value()); + assert_eq!("-INF", Literal::from(f32::NEG_INFINITY).value()); + assert_eq!("-INF", Literal::from(f64::NEG_INFINITY).value()); + assert_eq!("NaN", Literal::from(f32::NAN).value()); + assert_eq!("NaN", Literal::from(f64::NAN).value()); + } } diff --git a/lib/src/model/xsd/decimal.rs b/lib/src/model/xsd/decimal.rs index afe7a933..c3715e2c 100644 --- a/lib/src/model/xsd/decimal.rs +++ b/lib/src/model/xsd/decimal.rs @@ -1,3 +1,5 @@ +use crate::model::xsd::double::Double; +use crate::model::xsd::Float; use std::convert::{TryFrom, TryInto}; use std::error::Error; use std::fmt; @@ -85,12 +87,14 @@ impl Decimal { } /// TODO: XSD? is well defined for not integer + #[inline] pub fn checked_rem(&self, rhs: impl Into) -> Option { Some(Self { value: self.value.checked_rem(rhs.into().value)?, }) } + #[inline] pub fn checked_rem_euclid(&self, rhs: impl Into) -> Option { Some(Self { value: self.value.checked_rem_euclid(rhs.into().value)?, @@ -142,59 +146,64 @@ impl Decimal { } } + #[inline] pub const fn is_negative(&self) -> bool { self.value < 0 } + #[inline] pub const fn is_positive(&self) -> bool { self.value > 0 } - /// Creates a `Decimal` from a `f32` without taking care of precision + /// Creates a `Decimal` from a `Float` without taking care of precision #[inline] - pub(crate) fn from_f32(v: f32) -> Self { - Self::from_f64(v.into()) + pub(crate) fn from_float(v: Float) -> Self { + Self::from_double(v.into()) } - /// Creates a `f32` from a `Decimal` without taking care of precision + /// Creates a `Float` from a `Decimal` without taking care of precision #[inline] #[allow(clippy::cast_possible_truncation)] - pub fn to_f32(self) -> f32 { - self.to_f64() as f32 + pub fn to_float(self) -> Float { + (f64::from(self.to_double()) as f32).into() } - /// Creates a `Decimal` from a `f64` without taking care of precision + /// Creates a `Decimal` from a `Double` without taking care of precision #[inline] #[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)] - pub(crate) fn from_f64(v: f64) -> Self { + pub(crate) fn from_double(v: Double) -> Self { Self { - value: (v * (DECIMAL_PART_POW as f64)) as i128, + value: (f64::from(v) * (DECIMAL_PART_POW as f64)) as i128, } } - /// Creates a `f64` from a `Decimal` without taking care of precision + /// Creates a `Double` from a `Decimal` without taking care of precision #[inline] #[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)] - pub fn to_f64(self) -> f64 { - (self.value as f64) / (DECIMAL_PART_POW as f64) + pub fn to_double(self) -> Double { + ((self.value as f64) / (DECIMAL_PART_POW as f64)).into() + } + + /// Creates a `bool` from a `Decimal` according to xsd:boolean cast constraints + #[inline] + pub fn to_bool(self) -> bool { + self.value != 0 } + #[inline] pub(super) const fn as_i128(&self) -> i128 { self.value / DECIMAL_PART_POW } #[cfg(test)] pub(super) const fn min_value() -> Self { - Self { - value: i128::min_value(), - } + Self { value: i128::MIN } } #[cfg(test)] pub(super) const fn max_value() -> Self { - Self { - value: i128::max_value(), - } + Self { value: i128::MAX } } #[cfg(test)] @@ -211,49 +220,63 @@ impl From for Decimal { } } } + impl From for Decimal { + #[inline] fn from(value: i16) -> Self { Self { value: i128::from(value) * DECIMAL_PART_POW, } } } + impl From for Decimal { + #[inline] fn from(value: i32) -> Self { Self { value: i128::from(value) * DECIMAL_PART_POW, } } } + impl From for Decimal { + #[inline] fn from(value: i64) -> Self { Self { value: i128::from(value) * DECIMAL_PART_POW, } } } + impl From for Decimal { + #[inline] fn from(value: u8) -> Self { Self { value: i128::from(value) * DECIMAL_PART_POW, } } } + impl From for Decimal { + #[inline] fn from(value: u16) -> Self { Self { value: i128::from(value) * DECIMAL_PART_POW, } } } + impl From for Decimal { + #[inline] fn from(value: u32) -> Self { Self { value: i128::from(value) * DECIMAL_PART_POW, } } } + impl From for Decimal { + #[inline] fn from(value: u64) -> Self { Self { value: i128::from(value) * DECIMAL_PART_POW, @@ -264,6 +287,7 @@ impl From for Decimal { impl TryFrom for Decimal { type Error = DecimalOverflowError; + #[inline] fn try_from(value: i128) -> Result { Ok(Self { value: value @@ -276,6 +300,7 @@ impl TryFrom for Decimal { impl TryFrom for Decimal { type Error = DecimalOverflowError; + #[inline] fn try_from(value: u128) -> Result { Ok(Self { value: i128::try_from(value) diff --git a/lib/src/model/xsd/double.rs b/lib/src/model/xsd/double.rs new file mode 100644 index 00000000..df3434c7 --- /dev/null +++ b/lib/src/model/xsd/double.rs @@ -0,0 +1,281 @@ +use crate::model::xsd::Float; +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 for f64 { + #[inline] + fn from(value: Double) -> Self { + value.value + } +} + +impl From for Double { + #[inline] + fn from(value: f64) -> Self { + Self { value } + } +} + +impl From for Double { + #[inline] + fn from(value: i8) -> Self { + Self { + value: value.into(), + } + } +} + +impl From for Double { + #[inline] + fn from(value: i16) -> Self { + Self { + value: value.into(), + } + } +} + +impl From for Double { + #[inline] + fn from(value: i32) -> Self { + Self { + value: value.into(), + } + } +} + +impl From for Double { + #[inline] + fn from(value: u8) -> Self { + Self { + value: value.into(), + } + } +} + +impl From for Double { + #[inline] + fn from(value: u16) -> Self { + Self { + value: value.into(), + } + } +} + +impl From for Double { + #[inline] + fn from(value: u32) -> Self { + Self { + value: value.into(), + } + } +} + +impl From 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 { + 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 { + self.value.partial_cmp(&other.value) + } +} + +impl Hash for Double { + #[inline] + fn hash(&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()); + } +} diff --git a/lib/src/model/xsd/float.rs b/lib/src/model/xsd/float.rs new file mode 100644 index 00000000..7a7d9a8a --- /dev/null +++ b/lib/src/model/xsd/float.rs @@ -0,0 +1,253 @@ +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 `float` datatype](https://www.w3.org/TR/xmlschema11-2/#float) implementation. +/// +/// The "==" implementation is identity, not equality +#[derive(Debug, Clone, Copy, Default)] +#[repr(transparent)] +pub struct Float { + value: f32, +} + +impl Float { + #[inline] + pub fn from_be_bytes(bytes: [u8; 4]) -> Self { + Self { + value: f32::from_be_bytes(bytes), + } + } + + #[inline] + pub fn to_be_bytes(self) -> [u8; 4] { + 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 `Float` + #[inline] + #[allow(clippy::cast_precision_loss)] + pub fn from_i64(value: i64) -> Self { + Self { + value: value as f32, + } + } + + /// Casts `Float` into i64 without taking care of loss + #[inline] + #[allow(clippy::cast_possible_truncation)] + pub fn to_i64(self) -> i64 { + self.value as i64 + } + + /// 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 for f32 { + #[inline] + fn from(value: Float) -> Self { + value.value + } +} + +impl From for f64 { + #[inline] + fn from(value: Float) -> Self { + value.value.into() + } +} + +impl From for Float { + #[inline] + fn from(value: f32) -> Self { + Self { value } + } +} + +impl From for Float { + #[inline] + fn from(value: i8) -> Self { + Self { + value: value.into(), + } + } +} + +impl From for Float { + #[inline] + fn from(value: i16) -> Self { + Self { + value: value.into(), + } + } +} + +impl From for Float { + #[inline] + fn from(value: u8) -> Self { + Self { + value: value.into(), + } + } +} + +impl From for Float { + #[inline] + fn from(value: u16) -> Self { + Self { + value: value.into(), + } + } +} + +impl FromStr for Float { + type Err = ParseFloatError; + + /// Parses decimals lexical mapping + #[inline] + fn from_str(input: &str) -> Result { + 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 PartialEq for Float { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.value.to_ne_bytes() == other.value.to_ne_bytes() + } +} + +impl Eq for Float {} + +impl PartialOrd for Float { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + self.value.partial_cmp(&other.value) + } +} + +impl Hash for Float { + #[inline] + fn hash(&self, state: &mut H) { + state.write(&self.value.to_ne_bytes()) + } +} + +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)] +mod tests { + use super::*; + + #[test] + fn eq() { + assert_eq!(Float::from(0_f32), Float::from(0_f32)); + assert_eq!(Float::from(f32::NAN), Float::from(f32::NAN)); + assert_ne!(Float::from(-0.), Float::from(0.)); + } + + #[test] + fn from_str() -> Result<(), ParseFloatError> { + assert_eq!(Float::from(f32::NAN), Float::from_str("NaN")?); + assert_eq!(Float::from(f32::INFINITY), Float::from_str("INF")?); + assert_eq!(Float::from(f32::INFINITY), Float::from_str("+INF")?); + assert_eq!(Float::from(f32::NEG_INFINITY), Float::from_str("-INF")?); + assert_eq!(Float::from(0.), Float::from_str("0.0E0")?); + assert_eq!(Float::from(-0.), Float::from_str("-0.0E0")?); + Ok(()) + } + + #[test] + fn to_string() { + assert_eq!("NaN", Float::from(f32::NAN).to_string()); + assert_eq!("INF", Float::from(f32::INFINITY).to_string()); + assert_eq!("-INF", Float::from(f32::NEG_INFINITY).to_string()); + } +} diff --git a/lib/src/model/xsd/mod.rs b/lib/src/model/xsd/mod.rs index 3f0466a2..31916fa8 100644 --- a/lib/src/model/xsd/mod.rs +++ b/lib/src/model/xsd/mod.rs @@ -1,9 +1,13 @@ pub mod date_time; pub mod decimal; +mod double; mod duration; +mod float; mod parser; pub use self::date_time::{Date, DateTime, GDay, GMonth, GMonthDay, GYear, GYearMonth, Time}; pub use self::decimal::Decimal; +pub use self::double::Double; pub use self::duration::{DayTimeDuration, Duration, YearMonthDuration}; +pub use self::float::Float; pub use self::parser::XsdParseError; diff --git a/lib/src/sparql/eval.rs b/lib/src/sparql/eval.rs index 469a32a6..c3537171 100644 --- a/lib/src/sparql/eval.rs +++ b/lib/src/sparql/eval.rs @@ -1446,10 +1446,10 @@ impl SimpleEvaluator { PlanExpression::IsTriple(e) => Some(self.eval_expression(e, tuple)?.is_triple().into()), PlanExpression::BooleanCast(e) => match self.eval_expression(e, tuple)? { EncodedTerm::BooleanLiteral(value) => Some(value.into()), - EncodedTerm::FloatLiteral(value) => Some((value != 0. && !value.is_nan()).into()), - EncodedTerm::DoubleLiteral(value) => Some((value != 0. && !value.is_nan()).into()), + EncodedTerm::FloatLiteral(value) => Some(value.to_bool().into()), + EncodedTerm::DoubleLiteral(value) => Some(value.to_bool().into()), EncodedTerm::IntegerLiteral(value) => Some((value != 0).into()), - EncodedTerm::DecimalLiteral(value) => Some((value != Decimal::default()).into()), + EncodedTerm::DecimalLiteral(value) => Some(value.to_bool().into()), EncodedTerm::SmallStringLiteral(value) => parse_boolean_str(&value), EncodedTerm::BigStringLiteral { value_id } => { parse_boolean_str(&*self.dataset.get_str(&value_id).ok()??) @@ -1460,7 +1460,7 @@ impl SimpleEvaluator { EncodedTerm::FloatLiteral(value) => Some(f64::from(value).into()), EncodedTerm::DoubleLiteral(value) => Some(value.into()), EncodedTerm::IntegerLiteral(value) => Some((value as f64).into()), - EncodedTerm::DecimalLiteral(value) => Some(value.to_f64().into()), + EncodedTerm::DecimalLiteral(value) => Some(value.to_double().into()), EncodedTerm::BooleanLiteral(value) => { Some(if value { 1_f64 } else { 0_f64 }.into()) } @@ -1472,9 +1472,9 @@ impl SimpleEvaluator { }, PlanExpression::FloatCast(e) => match self.eval_expression(e, tuple)? { EncodedTerm::FloatLiteral(value) => Some(value.into()), - EncodedTerm::DoubleLiteral(value) => Some((value as f32).into()), + EncodedTerm::DoubleLiteral(value) => Some(value.to_f32().into()), EncodedTerm::IntegerLiteral(value) => Some((value as f32).into()), - EncodedTerm::DecimalLiteral(value) => Some(value.to_f32().into()), + EncodedTerm::DecimalLiteral(value) => Some(value.to_float().into()), EncodedTerm::BooleanLiteral(value) => { Some(if value { 1_f32 } else { 0_f32 }.into()) } @@ -1485,8 +1485,8 @@ impl SimpleEvaluator { _ => None, }, PlanExpression::IntegerCast(e) => match self.eval_expression(e, tuple)? { - EncodedTerm::FloatLiteral(value) => Some((value as i64).into()), - EncodedTerm::DoubleLiteral(value) => Some((value as i64).into()), + EncodedTerm::FloatLiteral(value) => Some(value.to_i64().into()), + EncodedTerm::DoubleLiteral(value) => Some(value.to_i64().into()), EncodedTerm::IntegerLiteral(value) => Some(value.into()), EncodedTerm::DecimalLiteral(value) => Some(i64::try_from(value).ok()?.into()), EncodedTerm::BooleanLiteral(value) => Some(if value { 1 } else { 0 }.into()), @@ -1497,8 +1497,8 @@ impl SimpleEvaluator { _ => None, }, PlanExpression::DecimalCast(e) => match self.eval_expression(e, tuple)? { - EncodedTerm::FloatLiteral(value) => Some(Decimal::from_f32(value).into()), - EncodedTerm::DoubleLiteral(value) => Some(Decimal::from_f64(value).into()), + EncodedTerm::FloatLiteral(value) => Some(Decimal::from_float(value).into()), + EncodedTerm::DoubleLiteral(value) => Some(Decimal::from_double(value).into()), EncodedTerm::IntegerLiteral(value) => Some(Decimal::from(value).into()), EncodedTerm::DecimalLiteral(value) => Some(value.into()), EncodedTerm::BooleanLiteral(value) => { @@ -1579,8 +1579,8 @@ impl SimpleEvaluator { EncodedTerm::BigStringLiteral { value_id } => { Some(!self.dataset.get_str(value_id).ok()??.is_empty()) } - EncodedTerm::FloatLiteral(value) => Some(*value != 0_f32), - EncodedTerm::DoubleLiteral(value) => Some(*value != 0_f64), + EncodedTerm::FloatLiteral(value) => Some(*value != Float::default()), + EncodedTerm::DoubleLiteral(value) => Some(*value != Double::default()), EncodedTerm::IntegerLiteral(value) => Some(*value != 0), EncodedTerm::DecimalLiteral(value) => Some(*value != Decimal::default()), _ => None, @@ -1913,31 +1913,31 @@ impl SimpleEvaluator { }, EncodedTerm::FloatLiteral(a) => match b { EncodedTerm::FloatLiteral(b) => Some(a == b), - EncodedTerm::DoubleLiteral(b) => Some(f64::from(*a) == *b), - EncodedTerm::IntegerLiteral(b) => Some(*a == *b as f32), - EncodedTerm::DecimalLiteral(b) => Some(*a == b.to_f32()), + EncodedTerm::DoubleLiteral(b) => Some(Double::from(*a) == *b), + EncodedTerm::IntegerLiteral(b) => Some(*a == Float::from_i64(*b)), + EncodedTerm::DecimalLiteral(b) => Some(*a == b.to_float()), _ if b.is_unknown_typed_literal() => None, _ => Some(false), }, EncodedTerm::DoubleLiteral(a) => match b { - EncodedTerm::FloatLiteral(b) => Some(*a == f64::from(*b)), + EncodedTerm::FloatLiteral(b) => Some(*a == Double::from(*b)), EncodedTerm::DoubleLiteral(b) => Some(a == b), - EncodedTerm::IntegerLiteral(b) => Some(*a == (*b as f64)), - EncodedTerm::DecimalLiteral(b) => Some(*a == b.to_f64()), + EncodedTerm::IntegerLiteral(b) => Some(*a == Double::from_i64(*b)), + EncodedTerm::DecimalLiteral(b) => Some(*a == b.to_double()), _ if b.is_unknown_typed_literal() => None, _ => Some(false), }, EncodedTerm::IntegerLiteral(a) => match b { - EncodedTerm::FloatLiteral(b) => Some((*a as f32) == *b), - EncodedTerm::DoubleLiteral(b) => Some((*a as f64) == *b), + EncodedTerm::FloatLiteral(b) => Some(Float::from_i64(*a) == *b), + EncodedTerm::DoubleLiteral(b) => Some(Double::from_i64(*a) == *b), EncodedTerm::IntegerLiteral(b) => Some(a == b), EncodedTerm::DecimalLiteral(b) => Some(Decimal::from(*a) == *b), _ if b.is_unknown_typed_literal() => None, _ => Some(false), }, EncodedTerm::DecimalLiteral(a) => match b { - EncodedTerm::FloatLiteral(b) => Some(a.to_f32() == *b), - EncodedTerm::DoubleLiteral(b) => Some(a.to_f64() == *b), + EncodedTerm::FloatLiteral(b) => Some(a.to_float() == *b), + EncodedTerm::DoubleLiteral(b) => Some(a.to_double() == *b), EncodedTerm::IntegerLiteral(b) => Some(*a == Decimal::from(*b)), EncodedTerm::DecimalLiteral(b) => Some(a == b), _ if b.is_unknown_typed_literal() => None, @@ -2106,105 +2106,105 @@ impl SimpleEvaluator { _ => None, }, EncodedTerm::FloatLiteral(a) => match b { - EncodedTerm::FloatLiteral(ref b) => a.partial_cmp(b), - EncodedTerm::DoubleLiteral(ref b) => f64::from(*a).partial_cmp(b), - EncodedTerm::IntegerLiteral(b) => a.partial_cmp(&((*b) as f32)), - EncodedTerm::DecimalLiteral(b) => a.partial_cmp(&b.to_f32()), + EncodedTerm::FloatLiteral(b) => a.partial_cmp(b), + EncodedTerm::DoubleLiteral(b) => Double::from(*a).partial_cmp(b), + EncodedTerm::IntegerLiteral(b) => a.partial_cmp(&Float::from_i64(*b)), + EncodedTerm::DecimalLiteral(b) => a.partial_cmp(&b.to_float()), _ => None, }, EncodedTerm::DoubleLiteral(a) => match b { EncodedTerm::FloatLiteral(b) => a.partial_cmp(&(*b).into()), - EncodedTerm::DoubleLiteral(ref b) => a.partial_cmp(b), - EncodedTerm::IntegerLiteral(b) => a.partial_cmp(&((*b) as f64)), - EncodedTerm::DecimalLiteral(b) => a.partial_cmp(&b.to_f64()), + EncodedTerm::DoubleLiteral(b) => a.partial_cmp(b), + EncodedTerm::IntegerLiteral(b) => a.partial_cmp(&Double::from_i64(*b)), + EncodedTerm::DecimalLiteral(b) => a.partial_cmp(&b.to_double()), _ => None, }, EncodedTerm::IntegerLiteral(a) => match b { - EncodedTerm::FloatLiteral(ref b) => ((*a) as f32).partial_cmp(b), - EncodedTerm::DoubleLiteral(ref b) => ((*a) as f64).partial_cmp(b), - EncodedTerm::IntegerLiteral(ref b) => a.partial_cmp(b), + EncodedTerm::FloatLiteral(b) => Float::from_i64(*a).partial_cmp(b), + EncodedTerm::DoubleLiteral(b) => Double::from_i64(*a).partial_cmp(b), + EncodedTerm::IntegerLiteral(b) => a.partial_cmp(b), EncodedTerm::DecimalLiteral(b) => Decimal::from(*a).partial_cmp(b), _ => None, }, EncodedTerm::DecimalLiteral(a) => match b { - EncodedTerm::FloatLiteral(ref b) => a.to_f32().partial_cmp(b), - EncodedTerm::DoubleLiteral(ref b) => a.to_f64().partial_cmp(b), + EncodedTerm::FloatLiteral(b) => a.to_float().partial_cmp(b), + EncodedTerm::DoubleLiteral(b) => a.to_double().partial_cmp(b), EncodedTerm::IntegerLiteral(b) => a.partial_cmp(&Decimal::from(*b)), - EncodedTerm::DecimalLiteral(ref b) => a.partial_cmp(b), + EncodedTerm::DecimalLiteral(b) => a.partial_cmp(b), _ => None, }, EncodedTerm::DateTimeLiteral(a) => { - if let EncodedTerm::DateTimeLiteral(ref b) = b { + if let EncodedTerm::DateTimeLiteral(b) = b { a.partial_cmp(b) } else { None } } EncodedTerm::TimeLiteral(a) => { - if let EncodedTerm::TimeLiteral(ref b) = b { + if let EncodedTerm::TimeLiteral(b) = b { a.partial_cmp(b) } else { None } } EncodedTerm::DateLiteral(a) => { - if let EncodedTerm::DateLiteral(ref b) = b { + if let EncodedTerm::DateLiteral(b) = b { a.partial_cmp(b) } else { None } } EncodedTerm::GYearMonthLiteral(a) => { - if let EncodedTerm::GYearMonthLiteral(ref b) = b { + if let EncodedTerm::GYearMonthLiteral(b) = b { a.partial_cmp(b) } else { None } } EncodedTerm::GYearLiteral(a) => { - if let EncodedTerm::GYearLiteral(ref b) = b { + if let EncodedTerm::GYearLiteral(b) = b { a.partial_cmp(b) } else { None } } EncodedTerm::GMonthDayLiteral(a) => { - if let EncodedTerm::GMonthDayLiteral(ref b) = b { + if let EncodedTerm::GMonthDayLiteral(b) = b { a.partial_cmp(b) } else { None } } EncodedTerm::GDayLiteral(a) => { - if let EncodedTerm::GDayLiteral(ref b) = b { + if let EncodedTerm::GDayLiteral(b) = b { a.partial_cmp(b) } else { None } } EncodedTerm::GMonthLiteral(a) => { - if let EncodedTerm::GMonthLiteral(ref b) = b { + if let EncodedTerm::GMonthLiteral(b) = b { a.partial_cmp(b) } else { None } } EncodedTerm::DurationLiteral(a) => match b { - EncodedTerm::DurationLiteral(ref b) => a.partial_cmp(b), - EncodedTerm::YearMonthDurationLiteral(ref b) => a.partial_cmp(b), - EncodedTerm::DayTimeDurationLiteral(ref b) => a.partial_cmp(b), + EncodedTerm::DurationLiteral(b) => a.partial_cmp(b), + EncodedTerm::YearMonthDurationLiteral(b) => a.partial_cmp(b), + EncodedTerm::DayTimeDurationLiteral(b) => a.partial_cmp(b), _ => None, }, EncodedTerm::YearMonthDurationLiteral(a) => match b { - EncodedTerm::DurationLiteral(ref b) => a.partial_cmp(b), - EncodedTerm::YearMonthDurationLiteral(ref b) => a.partial_cmp(b), - EncodedTerm::DayTimeDurationLiteral(ref b) => a.partial_cmp(b), + EncodedTerm::DurationLiteral(b) => a.partial_cmp(b), + EncodedTerm::YearMonthDurationLiteral(b) => a.partial_cmp(b), + EncodedTerm::DayTimeDurationLiteral(b) => a.partial_cmp(b), _ => None, }, EncodedTerm::DayTimeDurationLiteral(a) => match b { - EncodedTerm::DurationLiteral(ref b) => a.partial_cmp(b), - EncodedTerm::YearMonthDurationLiteral(ref b) => a.partial_cmp(b), - EncodedTerm::DayTimeDurationLiteral(ref b) => a.partial_cmp(b), + EncodedTerm::DurationLiteral(b) => a.partial_cmp(b), + EncodedTerm::YearMonthDurationLiteral(b) => a.partial_cmp(b), + EncodedTerm::DayTimeDurationLiteral(b) => a.partial_cmp(b), _ => None, }, _ => None, @@ -2281,8 +2281,8 @@ impl SimpleEvaluator { } enum NumericBinaryOperands { - Float(f32, f32), - Double(f64, f64), + Float(Float, Float), + Double(Double, Double), Integer(i64, i64), Decimal(Decimal, Decimal), Duration(Duration, Duration), @@ -2312,10 +2312,10 @@ impl NumericBinaryOperands { Some(Self::Double((*v1).into(), *v2)) } (EncodedTerm::FloatLiteral(v1), EncodedTerm::IntegerLiteral(v2)) => { - Some(Self::Float(*v1, *v2 as f32)) + Some(Self::Float(*v1, Float::from_i64(*v2))) } (EncodedTerm::FloatLiteral(v1), EncodedTerm::DecimalLiteral(v2)) => { - Some(Self::Float(*v1, v2.to_f32())) + Some(Self::Float(*v1, v2.to_float())) } (EncodedTerm::DoubleLiteral(v1), EncodedTerm::FloatLiteral(v2)) => { Some(Self::Double(*v1, (*v2).into())) @@ -2324,16 +2324,16 @@ impl NumericBinaryOperands { Some(Self::Double(*v1, *v2)) } (EncodedTerm::DoubleLiteral(v1), EncodedTerm::IntegerLiteral(v2)) => { - Some(Self::Double(*v1, *v2 as f64)) + Some(Self::Double(*v1, Double::from_i64(*v2))) } (EncodedTerm::DoubleLiteral(v1), EncodedTerm::DecimalLiteral(v2)) => { - Some(Self::Double(*v1, v2.to_f64())) + Some(Self::Double(*v1, v2.to_double())) } (EncodedTerm::IntegerLiteral(v1), EncodedTerm::FloatLiteral(v2)) => { - Some(Self::Float(*v1 as f32, *v2)) + Some(Self::Float(Float::from_i64(*v1), *v2)) } (EncodedTerm::IntegerLiteral(v1), EncodedTerm::DoubleLiteral(v2)) => { - Some(Self::Double(*v1 as f64, *v2)) + Some(Self::Double(Double::from_i64(*v1), *v2)) } (EncodedTerm::IntegerLiteral(v1), EncodedTerm::IntegerLiteral(v2)) => { Some(Self::Integer(*v1, *v2)) @@ -2342,10 +2342,10 @@ impl NumericBinaryOperands { Some(Self::Decimal(Decimal::from(*v1), *v2)) } (EncodedTerm::DecimalLiteral(v1), EncodedTerm::FloatLiteral(v2)) => { - Some(Self::Float(v1.to_f32(), *v2)) + Some(Self::Float(v1.to_float(), *v2)) } (EncodedTerm::DecimalLiteral(v1), EncodedTerm::DoubleLiteral(v2)) => { - Some(Self::Double(v1.to_f64(), *v2)) + Some(Self::Double(v1.to_double(), *v2)) } (EncodedTerm::DecimalLiteral(v1), EncodedTerm::IntegerLiteral(v2)) => { Some(Self::Decimal(*v1, Decimal::from(*v2))) diff --git a/lib/src/storage/binary_encoder.rs b/lib/src/storage/binary_encoder.rs index e028ea29..1c9d6362 100644 --- a/lib/src/storage/binary_encoder.rs +++ b/lib/src/storage/binary_encoder.rs @@ -315,12 +315,12 @@ impl TermReader for R { TYPE_FLOAT_LITERAL => { let mut buffer = [0; 4]; self.read_exact(&mut buffer)?; - Ok(EncodedTerm::FloatLiteral(f32::from_be_bytes(buffer))) + Ok(EncodedTerm::FloatLiteral(Float::from_be_bytes(buffer))) } TYPE_DOUBLE_LITERAL => { let mut buffer = [0; 8]; self.read_exact(&mut buffer)?; - Ok(EncodedTerm::DoubleLiteral(f64::from_be_bytes(buffer))) + Ok(EncodedTerm::DoubleLiteral(Double::from_be_bytes(buffer))) } TYPE_INTEGER_LITERAL => { let mut buffer = [0; 8]; diff --git a/lib/src/storage/numeric_encoder.rs b/lib/src/storage/numeric_encoder.rs index 6f5c5084..136d1d40 100644 --- a/lib/src/storage/numeric_encoder.rs +++ b/lib/src/storage/numeric_encoder.rs @@ -84,8 +84,8 @@ pub enum EncodedTerm { datatype_id: StrHash, }, BooleanLiteral(bool), - FloatLiteral(f32), - DoubleLiteral(f64), + FloatLiteral(Float), + DoubleLiteral(Double), IntegerLiteral(i64), DecimalLiteral(Decimal), DateTimeLiteral(DateTime), @@ -186,20 +186,8 @@ impl PartialEq for EncodedTerm { }, ) => value_id_a == value_id_b && datatype_id_a == datatype_id_b, (Self::BooleanLiteral(a), Self::BooleanLiteral(b)) => a == b, - (Self::FloatLiteral(a), Self::FloatLiteral(b)) => { - if a.is_nan() { - b.is_nan() - } else { - a == b - } - } - (Self::DoubleLiteral(a), Self::DoubleLiteral(b)) => { - if a.is_nan() { - b.is_nan() - } else { - a == b - } - } + (Self::FloatLiteral(a), Self::FloatLiteral(b)) => a == b, + (Self::DoubleLiteral(a), Self::DoubleLiteral(b)) => a == b, (Self::IntegerLiteral(a), Self::IntegerLiteral(b)) => a == b, (Self::DecimalLiteral(a), Self::DecimalLiteral(b)) => a == b, (Self::DateTimeLiteral(a), Self::DateTimeLiteral(b)) => a.is_identical_with(b), @@ -262,8 +250,8 @@ impl Hash for EncodedTerm { datatype_id.hash(state); } Self::BooleanLiteral(value) => value.hash(state), - Self::FloatLiteral(value) => state.write(&value.to_ne_bytes()), - Self::DoubleLiteral(value) => state.write(&value.to_ne_bytes()), + Self::FloatLiteral(value) => value.hash(state), + Self::DoubleLiteral(value) => value.hash(state), Self::IntegerLiteral(value) => value.hash(state), Self::DecimalLiteral(value) => value.hash(state), Self::DateTimeLiteral(value) => value.hash(state), @@ -371,14 +359,27 @@ impl From for EncodedTerm { Self::IntegerLiteral(value.into()) } } + impl From for EncodedTerm { fn from(value: f32) -> Self { + Self::FloatLiteral(value.into()) + } +} + +impl From for EncodedTerm { + fn from(value: Float) -> Self { Self::FloatLiteral(value) } } impl From for EncodedTerm { fn from(value: f64) -> Self { + Self::DoubleLiteral(value.into()) + } +} + +impl From for EncodedTerm { + fn from(value: Double) -> Self { Self::DoubleLiteral(value) } }