Move FloatWrapper into types.

This commit is contained in:
Stephen Chung 2022-11-08 23:17:31 +08:00
parent ae1e19c98a
commit ad018aaae3
9 changed files with 143 additions and 152 deletions

View File

@ -20,16 +20,6 @@ use std::{
num::{NonZeroU64, NonZeroU8, NonZeroUsize},
};
#[cfg(not(feature = "no_float"))]
use std::{
hash::Hasher,
ops::{Deref, DerefMut},
str::FromStr,
};
#[cfg(not(feature = "no_float"))]
use num_traits::float::FloatCore as Float;
/// _(internals)_ A binary expression.
/// Exported under the `internals` feature only.
#[derive(Debug, Clone, Hash)]
@ -273,120 +263,6 @@ impl FnCallExpr {
}
}
/// A type that wraps a floating-point number and implements [`Hash`].
///
/// Not available under `no_float`.
#[cfg(not(feature = "no_float"))]
#[derive(Clone, Copy, PartialEq, PartialOrd)]
pub struct FloatWrapper<F>(F);
#[cfg(not(feature = "no_float"))]
impl Hash for FloatWrapper<crate::FLOAT> {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.to_ne_bytes().hash(state);
}
}
#[cfg(not(feature = "no_float"))]
impl<F: Float> AsRef<F> for FloatWrapper<F> {
#[inline(always)]
#[must_use]
fn as_ref(&self) -> &F {
&self.0
}
}
#[cfg(not(feature = "no_float"))]
impl<F: Float> AsMut<F> for FloatWrapper<F> {
#[inline(always)]
#[must_use]
fn as_mut(&mut self) -> &mut F {
&mut self.0
}
}
#[cfg(not(feature = "no_float"))]
impl<F: Float> Deref for FloatWrapper<F> {
type Target = F;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[cfg(not(feature = "no_float"))]
impl<F: Float> DerefMut for FloatWrapper<F> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[cfg(not(feature = "no_float"))]
impl<F: Float + fmt::Debug> fmt::Debug for FloatWrapper<F> {
#[cold]
#[inline(never)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}
#[cfg(not(feature = "no_float"))]
impl<F: Float + fmt::Display + fmt::LowerExp + From<f32>> fmt::Display for FloatWrapper<F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let abs = self.0.abs();
if abs.is_zero() {
f.write_str("0.0")
} else if abs > Self::MAX_NATURAL_FLOAT_FOR_DISPLAY.into()
|| abs < Self::MIN_NATURAL_FLOAT_FOR_DISPLAY.into()
{
write!(f, "{:e}", self.0)
} else {
fmt::Display::fmt(&self.0, f)?;
if abs.fract().is_zero() {
f.write_str(".0")?;
}
Ok(())
}
}
}
#[cfg(not(feature = "no_float"))]
impl<F: Float> From<F> for FloatWrapper<F> {
#[inline(always)]
fn from(value: F) -> Self {
Self::new(value)
}
}
#[cfg(not(feature = "no_float"))]
impl<F: Float + FromStr> FromStr for FloatWrapper<F> {
type Err = <F as FromStr>::Err;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
F::from_str(s).map(Into::into)
}
}
#[cfg(not(feature = "no_float"))]
impl<F: Float> FloatWrapper<F> {
/// Maximum floating-point number for natural display before switching to scientific notation.
pub const MAX_NATURAL_FLOAT_FOR_DISPLAY: f32 = 10_000_000_000_000.0;
/// Minimum floating-point number for natural display before switching to scientific notation.
pub const MIN_NATURAL_FLOAT_FOR_DISPLAY: f32 = 0.000_000_000_000_1;
/// Create a new [`FloatWrapper`].
#[inline(always)]
#[must_use]
pub const fn new(value: F) -> Self {
Self(value)
}
}
/// _(internals)_ An expression sub-tree.
/// Exported under the `internals` feature only.
#[derive(Clone, Hash)]
@ -405,7 +281,7 @@ pub enum Expr {
IntegerConstant(INT, Position),
/// Floating-point constant.
#[cfg(not(feature = "no_float"))]
FloatConstant(FloatWrapper<crate::FLOAT>, Position),
FloatConstant(crate::types::FloatWrapper<crate::FLOAT>, Position),
/// Character constant.
CharConstant(char, Position),
/// [String][ImmutableString] constant.

View File

@ -26,9 +26,6 @@ pub use stmt::{
SwitchCasesCollection, TryCatchBlock,
};
#[cfg(not(feature = "no_float"))]
pub use expr::FloatWrapper;
/// _(internals)_ Placeholder for a script-defined function.
/// Exported under the `internals` feature only.
#[cfg(feature = "no_function")]

View File

@ -9,11 +9,11 @@ use std::{
ops::{Deref, DerefMut},
};
// Calculate an offset+len pair given an actual length of the underlying array.
//
// Negative starting positions count from the end.
//
// Values going over bounds are limited to the actual length.
/// Calculate an offset+len pair given an actual length of the underlying array.
///
/// Negative starting positions count from the end.
///
/// Values going over bounds are limited to the actual length.
#[inline]
#[allow(dead_code)]
pub fn calc_offset_len(length: usize, start: crate::INT, len: crate::INT) -> (usize, usize) {
@ -41,11 +41,11 @@ pub fn calc_offset_len(length: usize, start: crate::INT, len: crate::INT) -> (us
(start, len)
}
// Calculate an offset+len pair given an actual length of the underlying array.
//
// Negative starting positions count from the end.
//
// Values going over bounds call the provided closure to return a default value or an error.
/// Calculate an offset+len pair given an actual length of the underlying array.
///
/// Negative starting positions count from the end.
///
/// Values going over bounds call the provided closure to return a default value or an error.
#[inline]
#[allow(dead_code)]
pub fn calc_index<E>(

View File

@ -299,6 +299,10 @@ pub type OptimizationLevel = ();
#[cfg(feature = "internals")]
pub use types::dynamic::{AccessMode, DynamicReadLock, DynamicWriteLock, Variant};
#[cfg(feature = "internals")]
#[cfg(not(feature = "no_float"))]
pub use types::FloatWrapper;
#[cfg(feature = "internals")]
pub use tokenizer::{get_next_token, parse_string_literal};
@ -333,10 +337,6 @@ pub use ast::Namespace;
#[cfg(not(feature = "no_function"))]
pub use ast::EncapsulatedEnviron;
#[cfg(feature = "internals")]
#[cfg(not(feature = "no_float"))]
pub use ast::FloatWrapper;
#[cfg(feature = "internals")]
pub use eval::{Caches, FnResolutionCache, FnResolutionCacheEntry, GlobalRuntimeState};

View File

@ -134,26 +134,26 @@ mod print_debug_functions {
#[cfg(not(feature = "no_float"))]
#[rhai_fn(name = "print", name = "to_string")]
pub fn print_f64(number: f64) -> ImmutableString {
crate::ast::FloatWrapper::new(number).to_string().into()
crate::types::FloatWrapper::new(number).to_string().into()
}
/// Convert the value of `number` into a string.
#[cfg(not(feature = "no_float"))]
#[rhai_fn(name = "print", name = "to_string")]
pub fn print_f32(number: f32) -> ImmutableString {
crate::ast::FloatWrapper::new(number).to_string().into()
crate::types::FloatWrapper::new(number).to_string().into()
}
/// Convert the value of `number` into a string.
#[cfg(not(feature = "no_float"))]
#[rhai_fn(name = "debug", name = "to_debug")]
pub fn debug_f64(number: f64) -> ImmutableString {
let number = crate::ast::FloatWrapper::new(number);
let number = crate::types::FloatWrapper::new(number);
format!("{number:?}").into()
}
/// Convert the value of `number` into a string.
#[cfg(not(feature = "no_float"))]
#[rhai_fn(name = "debug", name = "to_debug")]
pub fn debug_f32(number: f32) -> ImmutableString {
let number = crate::ast::FloatWrapper::new(number);
let number = crate::types::FloatWrapper::new(number);
format!("{number:?}").into()
}

View File

@ -382,7 +382,7 @@ pub enum Token {
///
/// Reserved under the `no_float` feature.
#[cfg(not(feature = "no_float"))]
FloatConstant(crate::ast::FloatWrapper<crate::FLOAT>),
FloatConstant(crate::types::FloatWrapper<crate::FLOAT>),
/// A [`Decimal`][rust_decimal::Decimal] constant.
///
/// Requires the `decimal` feature.
@ -1682,7 +1682,7 @@ fn get_next_token_inner(
// If integer parsing is unnecessary, try float instead
#[cfg(not(feature = "no_float"))]
let num = num.or_else(|_| {
crate::ast::FloatWrapper::from_str(&result).map(Token::FloatConstant)
crate::types::FloatWrapper::from_str(&result).map(Token::FloatConstant)
});
// Then try decimal

View File

@ -70,7 +70,7 @@ pub enum Union {
Int(INT, Tag, AccessMode),
/// A floating-point value.
#[cfg(not(feature = "no_float"))]
Float(crate::ast::FloatWrapper<crate::FLOAT>, Tag, AccessMode),
Float(super::FloatWrapper<crate::FLOAT>, Tag, AccessMode),
/// _(decimal)_ A fixed-precision decimal value.
/// Exported under the `decimal` feature only.
#[cfg(feature = "decimal")]
@ -867,7 +867,7 @@ impl Dynamic {
#[must_use]
pub const fn from_float(value: crate::FLOAT) -> Self {
Self(Union::Float(
crate::ast::FloatWrapper::new(value),
super::FloatWrapper::new(value),
DEFAULT_TAG_VALUE,
ReadWrite,
))
@ -1859,9 +1859,9 @@ impl From<crate::FLOAT> for Dynamic {
}
}
#[cfg(not(feature = "no_float"))]
impl From<crate::ast::FloatWrapper<crate::FLOAT>> for Dynamic {
impl From<super::FloatWrapper<crate::FLOAT>> for Dynamic {
#[inline(always)]
fn from(value: crate::ast::FloatWrapper<crate::FLOAT>) -> Self {
fn from(value: super::FloatWrapper<crate::FLOAT>) -> Self {
Self(Union::Float(value, DEFAULT_TAG_VALUE, ReadWrite))
}
}

115
src/types/float.rs Normal file
View File

@ -0,0 +1,115 @@
#![cfg(not(feature = "no_float"))]
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
use std::{
fmt,
hash::{Hash, Hasher},
ops::{Deref, DerefMut},
str::FromStr,
};
use num_traits::float::FloatCore as Float;
/// A type that wraps a floating-point number and implements [`Hash`].
///
/// Not available under `no_float`.
#[derive(Clone, Copy, PartialEq, PartialOrd)]
pub struct FloatWrapper<F>(F);
impl Hash for FloatWrapper<crate::FLOAT> {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.to_ne_bytes().hash(state);
}
}
impl<F: Float> AsRef<F> for FloatWrapper<F> {
#[inline(always)]
#[must_use]
fn as_ref(&self) -> &F {
&self.0
}
}
impl<F: Float> AsMut<F> for FloatWrapper<F> {
#[inline(always)]
#[must_use]
fn as_mut(&mut self) -> &mut F {
&mut self.0
}
}
impl<F: Float> Deref for FloatWrapper<F> {
type Target = F;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<F: Float> DerefMut for FloatWrapper<F> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<F: Float + fmt::Debug> fmt::Debug for FloatWrapper<F> {
#[cold]
#[inline(never)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}
impl<F: Float + fmt::Display + fmt::LowerExp + From<f32>> fmt::Display for FloatWrapper<F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let abs = self.0.abs();
if abs.is_zero() {
f.write_str("0.0")
} else if abs > Self::MAX_NATURAL_FLOAT_FOR_DISPLAY.into()
|| abs < Self::MIN_NATURAL_FLOAT_FOR_DISPLAY.into()
{
write!(f, "{:e}", self.0)
} else {
fmt::Display::fmt(&self.0, f)?;
if abs.fract().is_zero() {
f.write_str(".0")?;
}
Ok(())
}
}
}
impl<F: Float> From<F> for FloatWrapper<F> {
#[inline(always)]
fn from(value: F) -> Self {
Self::new(value)
}
}
impl<F: Float + FromStr> FromStr for FloatWrapper<F> {
type Err = <F as FromStr>::Err;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
F::from_str(s).map(Into::into)
}
}
impl<F: Float> FloatWrapper<F> {
/// Maximum floating-point number for natural display before switching to scientific notation.
pub const MAX_NATURAL_FLOAT_FOR_DISPLAY: f32 = 10_000_000_000_000.0;
/// Minimum floating-point number for natural display before switching to scientific notation.
pub const MIN_NATURAL_FLOAT_FOR_DISPLAY: f32 = 0.000_000_000_000_1;
/// Create a new [`FloatWrapper`].
#[inline(always)]
#[must_use]
pub const fn new(value: F) -> Self {
Self(value)
}
}

View File

@ -4,6 +4,7 @@ pub mod bloom_filter;
pub mod custom_types;
pub mod dynamic;
pub mod error;
pub mod float;
pub mod fn_ptr;
pub mod immutable_string;
pub mod interner;
@ -18,6 +19,8 @@ pub use dynamic::Dynamic;
#[cfg(not(feature = "no_time"))]
pub use dynamic::Instant;
pub use error::EvalAltResult;
#[cfg(not(feature = "no_float"))]
pub use float::FloatWrapper;
pub use fn_ptr::FnPtr;
pub use immutable_string::ImmutableString;
pub use interner::StringsInterner;