artichoke_backend/extn/core/float/mod.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
use std::fmt;
use crate::extn::core::numeric::{self, Coercion, Outcome};
use crate::extn::prelude::*;
pub(in crate::extn) mod mruby;
#[repr(transparent)]
#[derive(Default, Clone, Copy, PartialEq, PartialOrd)]
pub struct Float(f64);
impl fmt::Debug for Float {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl fmt::Display for Float {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl ConvertMut<Float, Value> for Artichoke {
#[inline]
fn convert_mut(&mut self, from: Float) -> Value {
self.convert_mut(from.0)
}
}
impl TryConvert<Value, Float> for Artichoke {
type Error = Error;
#[inline]
fn try_convert(&self, value: Value) -> Result<Float, Self::Error> {
let num = self.try_convert(value)?;
Ok(Float(num))
}
}
impl From<f64> for Float {
#[inline]
fn from(flt: f64) -> Self {
Self(flt)
}
}
impl From<Float> for f64 {
#[inline]
fn from(flt: Float) -> Self {
flt.as_f64()
}
}
impl From<Float> for Outcome {
#[inline]
fn from(flt: Float) -> Self {
Self::Float(flt.into())
}
}
impl From<f64> for Outcome {
#[inline]
fn from(flt: f64) -> Self {
Self::Float(flt)
}
}
impl Float {
/// The minimum number of significant decimal digits in a double-precision
/// floating point.
///
/// Usually defaults to 15.
pub const DIG: i64 = f64::DIGITS as i64;
/// The difference between 1 and the smallest double-precision floating
/// point number greater than 1.
///
/// Usually defaults to 2.2204460492503131e-16.
pub const EPSILON: f64 = f64::EPSILON;
/// An expression representing positive infinity.
pub const INFINITY: f64 = f64::INFINITY;
/// The minimum number of significant decimal digits in a double-precision
/// floating point.
///
/// Usually defaults to 15.
pub const MANT_DIG: i64 = f64::MANTISSA_DIGITS as i64;
/// The largest possible integer in a double-precision floating point
/// number.
///
/// Usually defaults to `1.7976931348623157e+308`.
pub const MAX: f64 = f64::MAX;
/// The largest positive exponent in a double-precision floating point where
/// 10 raised to this power minus 1.
///
/// Usually defaults to 308.
pub const MAX_10_EXP: i64 = f64::MAX_10_EXP as i64;
/// The largest possible exponent value in a double-precision floating
/// point.
///
/// Usually defaults to 1024.
pub const MAX_EXP: i64 = f64::MAX_EXP as i64;
/// The smallest positive normalized number in a double-precision floating
/// point.
///
/// Usually defaults to 2.2250738585072014e-308.
///
/// If the platform supports denormalized numbers, there are numbers between
/// zero and [`Float::MIN`]. `0.0.next_float` returns the smallest positive
/// floating point number including denormalized numbers.
pub const MIN: f64 = f64::MIN;
/// The smallest negative exponent in a double-precision floating point
/// where 10 raised to this power minus 1.
///
/// Usually defaults to -307.
pub const MIN_10_EXP: i64 = f64::MIN_10_EXP as i64;
/// The smallest possible exponent value in a double-precision floating
/// point.
///
/// Usually defaults to -1021.
pub const MIN_EXP: i64 = f64::MIN_EXP as i64;
/// An expression representing a value which is "not a number".
pub const NAN: f64 = f64::NAN;
pub const NEG_INFINITY: f64 = f64::NEG_INFINITY;
/// The base of the floating point, or number of unique digits used to
/// represent the number.
///
/// Usually defaults to 2 on most systems, which would represent a base-10
/// decimal.
pub const RADIX: i64 = f64::RADIX as i64;
/// Represents the rounding mode for floating point addition.
///
/// Usually defaults to 1, rounding to the nearest number.
///
/// Other modes include:
///
/// | mode | value |
/// |------------------------------------|-------|
/// | Indeterminable | -1 |
/// | Rounding towards zero | 0 |
/// | Rounding to the nearest number | 1 |
/// | Rounding towards positive infinity | 2 |
/// | Rounding towards negative infinity | 3 |
///
/// # Rust Caveats
///
/// Rust does not support setting the rounding mode and the behavior from
/// LLVM is not documented. Because of this uncertainty, Artichoke sets its
/// rounding mode to `-1`, Indeterminable.
///
/// The Rust docs say [`f64::round`][round] rounds "half-way cases away from
/// 0.0." Stack Overflow has a
/// [discussion around float rounding semantics][stackoverflow] in Rust and
/// LLVM.
///
/// [stackoverflow]: https://stackoverflow.com/a/28122536
/// [round]: https://doc.rust-lang.org/1.42.0/std/primitive.f64.html#method.round
pub const ROUNDS: i64 = -1;
/// Construct a new, zero, float.
#[inline]
#[must_use]
pub const fn new() -> Self {
Self(0.0)
}
/// Construct a new `Float` with a given [`f64`].
#[inline]
#[must_use]
pub const fn with_f64(f: f64) -> Self {
Self(f)
}
/// Convert self to an `i64` with a saturating cast.
#[inline]
#[must_use]
#[allow(clippy::cast_possible_truncation)]
pub const fn as_i64(self) -> i64 {
self.0 as i64
}
/// Return the inner [`f64`].
#[inline]
#[must_use]
pub const fn as_f64(self) -> f64 {
self.0
}
#[inline]
#[must_use]
#[allow(clippy::cast_precision_loss)]
#[allow(clippy::cast_possible_truncation)]
pub fn try_into_fixnum(self) -> Option<i64> {
const FIXABLE_MAX: f64 = 2_i64.pow(f64::MANTISSA_DIGITS) as f64;
const FIXABLE_MIN: f64 = -(2_i64.pow(f64::MANTISSA_DIGITS)) as f64;
match self.0 {
x if !x.is_finite() => None,
x if x > FIXABLE_MAX => None,
x if x < FIXABLE_MIN => None,
x => Some(x as i64),
}
}
/// Compute the remainder of self and other.
///
/// Equivalent to `self.as_f64() % other.as_f64()`.
#[inline]
#[must_use]
pub fn modulo(self, other: Self) -> Self {
Self(self.0 % other.0)
}
#[inline]
pub fn coerced_modulo(self, interp: &mut Artichoke, other: Value) -> Result<Outcome, Error> {
if let Ruby::Float = other.ruby_type() {
let other = other.try_convert_into::<Float>(interp)?;
return Ok(self.modulo(other).into());
}
let x = interp.convert_mut(self);
let coerced = numeric::coerce(interp, x, other)?;
match coerced {
Coercion::Float(x, y) => Ok((x % y).into()),
Coercion::Integer(x, y) => Ok((x % y).into()),
}
}
}