From 8520048b262a4d322f34d3a11a912325851c546c Mon Sep 17 00:00:00 2001 From: ConnorSkees <39542938+ConnorSkees@users.noreply.github.com> Date: Tue, 19 May 2020 23:23:04 -0400 Subject: [PATCH] implement small int optimizations This allows for numbers within 18 digits of precision (the vast majority of numbers) to be represented by just 2 i64s rather than 2 heap allocated BigInts. In the future these optimizations may become more aggressive and granular, but for now `grass` is already more than an order of magnitude faster than `dart-sass`. --- src/color/mod.rs | 8 +- src/unit/conversion.rs | 34 ++-- src/value/number.rs | 398 ++++++++++++++++++++++++++++++++++------- src/value/parse.rs | 19 +- 4 files changed, 370 insertions(+), 89 deletions(-) diff --git a/src/color/mod.rs b/src/color/mod.rs index fb7aef4..efb2887 100644 --- a/src/color/mod.rs +++ b/src/color/mod.rs @@ -362,16 +362,16 @@ impl Color { let repr = repr(&val, &val, &val, &alpha); return Color::new_hsla(val.clone(), val.clone(), val, alpha, hsla, repr); } - let temporary_1 = if luminance < Number::ratio(1, 2) { + let temporary_1 = if luminance < Number::machine_ratio(1, 2) { luminance.clone() * (Number::one() + saturation) } else { luminance.clone() + saturation.clone() - luminance.clone() * saturation }; let temporary_2 = Number::from(2) * luminance - temporary_1.clone(); hue /= Number::from(360); - let mut temporary_r = hue.clone() + Number::ratio(1, 3); + let mut temporary_r = hue.clone() + Number::machine_ratio(1, 3); let mut temporary_g = hue.clone(); - let mut temporary_b = hue - Number::ratio(1, 3); + let mut temporary_b = hue - Number::machine_ratio(1, 3); macro_rules! clamp_temp { ($temp:ident) => { @@ -397,7 +397,7 @@ impl Color { } else if Number::from(3) * $temp.clone() < Number::from(2) { $temp2.clone() + ($temp1.clone() - $temp2.clone()) - * (Number::ratio(2, 3) - $temp) + * (Number::machine_ratio(2, 3) - $temp) * Number::from(6) } else { $temp2.clone() diff --git a/src/unit/conversion.rs b/src/unit/conversion.rs index f6a70fe..6b7d622 100644 --- a/src/unit/conversion.rs +++ b/src/unit/conversion.rs @@ -16,18 +16,18 @@ pub(crate) static UNIT_CONVERSION_TABLE: Lazy< let mut from_in = HashMap::new(); from_in.insert("in", Number::one()); from_in.insert("cm", Number::one() / Number::from(2.54)); - from_in.insert("pc", Number::ratio(1, 6)); + from_in.insert("pc", Number::machine_ratio(1, 6)); from_in.insert("mm", Number::one() / Number::from(25.4)); from_in.insert("q", Number::one() / Number::from(101.6)); - from_in.insert("pt", Number::ratio(1, 72)); - from_in.insert("px", Number::ratio(1, 96)); + from_in.insert("pt", Number::machine_ratio(1, 72)); + from_in.insert("px", Number::machine_ratio(1, 96)); let mut from_cm = HashMap::new(); from_cm.insert("in", Number::from(2.54)); from_cm.insert("cm", Number::one()); from_cm.insert("pc", Number::from(2.54) / Number::from(6)); - from_cm.insert("mm", Number::ratio(1, 10)); - from_cm.insert("q", Number::ratio(1, 40)); + from_cm.insert("mm", Number::machine_ratio(1, 10)); + from_cm.insert("q", Number::machine_ratio(1, 40)); from_cm.insert("pt", Number::from(2.54) / Number::from(72)); from_cm.insert("px", Number::from(2.54) / Number::from(96)); @@ -37,15 +37,15 @@ pub(crate) static UNIT_CONVERSION_TABLE: Lazy< from_pc.insert("pc", Number::one()); from_pc.insert("mm", Number::from(6) / Number::from(25.4)); from_pc.insert("q", Number::from(6) / Number::from(101.6)); - from_pc.insert("pt", Number::ratio(1, 12)); - from_pc.insert("px", Number::ratio(1, 16)); + from_pc.insert("pt", Number::machine_ratio(1, 12)); + from_pc.insert("px", Number::machine_ratio(1, 16)); let mut from_mm = HashMap::new(); from_mm.insert("in", Number::from(25.4)); from_mm.insert("cm", Number::from(10)); from_mm.insert("pc", Number::from(25.4) / Number::from(6)); from_mm.insert("mm", Number::one()); - from_mm.insert("q", Number::ratio(1, 4)); + from_mm.insert("q", Number::machine_ratio(1, 4)); from_mm.insert("pt", Number::from(25.4) / Number::from(72)); from_mm.insert("px", Number::from(25.4) / Number::from(96)); @@ -65,7 +65,7 @@ pub(crate) static UNIT_CONVERSION_TABLE: Lazy< from_pt.insert("mm", Number::from(72) / Number::from(25.4)); from_pt.insert("q", Number::from(72) / Number::from(101.6)); from_pt.insert("pt", Number::one()); - from_pt.insert("px", Number::ratio(3, 4)); + from_pt.insert("px", Number::machine_ratio(3, 4)); let mut from_px = HashMap::new(); from_px.insert("in", Number::from(96)); @@ -73,17 +73,17 @@ pub(crate) static UNIT_CONVERSION_TABLE: Lazy< from_px.insert("pc", Number::from(16)); from_px.insert("mm", Number::from(96) / Number::from(25.4)); from_px.insert("q", Number::from(96) / Number::from(101.6)); - from_px.insert("pt", Number::ratio(4, 3)); + from_px.insert("pt", Number::machine_ratio(4, 3)); from_px.insert("px", Number::one()); let mut from_deg = HashMap::new(); from_deg.insert("deg", Number::one()); - from_deg.insert("grad", Number::ratio(9, 10)); + from_deg.insert("grad", Number::machine_ratio(9, 10)); from_deg.insert("rad", Number::from(180) / Number::from(PI)); from_deg.insert("turn", Number::from(360)); let mut from_grad = HashMap::new(); - from_grad.insert("deg", Number::ratio(10, 9)); + from_grad.insert("deg", Number::machine_ratio(10, 9)); from_grad.insert("grad", Number::one()); from_grad.insert("rad", Number::from(200) / Number::from(PI)); from_grad.insert("turn", Number::from(400)); @@ -95,14 +95,14 @@ pub(crate) static UNIT_CONVERSION_TABLE: Lazy< from_rad.insert("turn", Number::from(2.0 * PI)); let mut from_turn = HashMap::new(); - from_turn.insert("deg", Number::ratio(1, 360)); - from_turn.insert("grad", Number::ratio(1, 400)); + from_turn.insert("deg", Number::machine_ratio(1, 360)); + from_turn.insert("grad", Number::machine_ratio(1, 400)); from_turn.insert("rad", Number::one() / Number::from(2.0 * PI)); from_turn.insert("turn", Number::one()); let mut from_s = HashMap::new(); from_s.insert("s", Number::one()); - from_s.insert("ms", Number::ratio(1, 1000)); + from_s.insert("ms", Number::machine_ratio(1, 1000)); let mut from_ms = HashMap::new(); from_ms.insert("s", Number::from(1000)); @@ -113,7 +113,7 @@ pub(crate) static UNIT_CONVERSION_TABLE: Lazy< from_hz.insert("kHz", Number::from(1000)); let mut from_khz = HashMap::new(); - from_khz.insert("Hz", Number::ratio(1, 1000)); + from_khz.insert("Hz", Number::machine_ratio(1, 1000)); from_khz.insert("kHz", Number::one()); let mut from_dpi = HashMap::new(); @@ -127,7 +127,7 @@ pub(crate) static UNIT_CONVERSION_TABLE: Lazy< from_dpcm.insert("dppx", Number::from(96) / Number::from(2.54)); let mut from_dppx = HashMap::new(); - from_dppx.insert("dpi", Number::ratio(1, 96)); + from_dppx.insert("dpi", Number::machine_ratio(1, 96)); from_dppx.insert("dpcm", Number::from(2.54) / Number::from(96)); from_dppx.insert("dppx", Number::one()); diff --git a/src/value/number.rs b/src/value/number.rs index 7034199..6843102 100644 --- a/src/value/number.rs +++ b/src/value/number.rs @@ -1,61 +1,90 @@ -use std::convert::From; +use std::cmp::Ordering; +use std::convert::{From, TryFrom}; use std::fmt::{self, Display, Write}; +use std::mem; use std::ops::{ Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign, }; use num_bigint::BigInt; -use num_rational::BigRational; -use num_traits::{Num, One, Signed, Zero}; +use num_rational::{BigRational, Rational64}; +use num_traits::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Num, One, Signed, Zero}; use crate::error::SassError; const PRECISION: usize = 10; -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd)] -pub(crate) struct Number { - val: BigRational, +#[derive(Clone, Eq, PartialEq, Ord)] +pub(crate) enum Number { + Machine(Rational64), + Big(BigRational), } impl Number { - pub const fn new(val: BigRational) -> Number { - Number { val } + pub const fn new_machine(val: Rational64) -> Number { + Number::Machine(val) + } + + pub const fn new_big(val: BigRational) -> Number { + Number::Big(val) } pub fn to_integer(&self) -> BigInt { - self.val.to_integer() + match self { + Self::Machine(val) => BigInt::from(val.to_integer()), + Self::Big(val) => val.to_integer(), + } } - pub fn ratio, B: Into>(a: A, b: B) -> Self { - Number::new(BigRational::new(a.into(), b.into())) + pub fn machine_ratio, B: Into>(a: A, b: B) -> Self { + Number::new_machine(Rational64::new(a.into(), b.into())) + } + + #[allow(dead_code)] + pub fn big_ratio, B: Into>(a: A, b: B) -> Self { + Number::new_big(BigRational::new(a.into(), b.into())) } pub fn round(&self) -> Self { - Number { - val: self.val.round(), + match self { + Self::Machine(val) => Self::Machine(val.round()), + Self::Big(val) => Self::Big(val.round()), } } pub fn ceil(&self) -> Self { - Number { - val: self.val.ceil(), + match self { + Self::Machine(val) => Self::Machine(val.ceil()), + Self::Big(val) => Self::Big(val.ceil()), } } pub fn floor(&self) -> Self { - Number { - val: self.val.floor(), + match self { + Self::Machine(val) => Self::Machine(val.floor()), + Self::Big(val) => Self::Big(val.floor()), } } pub fn abs(&self) -> Self { - Number { - val: self.val.abs(), + match self { + Self::Machine(val) => Self::Machine(val.abs()), + Self::Big(val) => Self::Big(val.abs()), } } pub fn is_decimal(&self) -> bool { - !self.val.denom().is_one() + match self { + Self::Machine(v) => !v.is_integer(), + Self::Big(v) => !v.is_integer(), + } + } + + pub fn fract(&mut self) -> Number { + match self { + Self::Machine(v) => Number::new_machine(v.fract()), + Self::Big(v) => Number::new_big(v.fract()), + } } pub fn clamp + Zero, B: Into>(self, min: A, max: B) -> Self { @@ -77,23 +106,35 @@ impl Number { } } +impl Default for Number { + fn default() -> Self { + Self::zero() + } +} + impl Zero for Number { fn zero() -> Self { - Number::from(0) + Number::new_machine(Rational64::from_integer(0)) } fn is_zero(&self) -> bool { - self.val.is_zero() + match self { + Self::Machine(v) => v.is_zero(), + Self::Big(v) => v.is_zero(), + } } } impl One for Number { fn one() -> Self { - Number::from(1) + Number::new_machine(Rational64::from_integer(1)) } fn is_one(&self) -> bool { - self.val.is_one() + match self { + Self::Machine(v) => v.is_one(), + Self::Big(v) => v.is_one(), + } } } @@ -109,10 +150,8 @@ impl Signed for Number { self.abs() } - fn abs_sub(&self, other: &Self) -> Self { - Number { - val: self.val.abs_sub(&other.val), - } + fn abs_sub(&self, _: &Self) -> Self { + todo!() } fn signum(&self) -> Self { @@ -126,11 +165,17 @@ impl Signed for Number { } fn is_positive(&self) -> bool { - self.val.is_positive() + match self { + Self::Machine(v) => v.is_positive(), + Self::Big(v) => v.is_positive(), + } } fn is_negative(&self) -> bool { - self.val.is_negative() + match self { + Self::Machine(v) => v.is_negative(), + Self::Big(v) => v.is_negative(), + } } } @@ -138,29 +183,44 @@ macro_rules! from_integer { ($ty:ty) => { impl From<$ty> for Number { fn from(b: $ty) -> Self { - Number { - val: BigRational::from_integer(BigInt::from(b)), + if let Ok(v) = i64::try_from(b) { + Number::Machine(Rational64::from_integer(v)) + } else { + Number::Big(BigRational::from_integer(BigInt::from(b))) } } } }; } -// todo: implement std::convertTryFrom instead -impl From for Number { - fn from(b: f64) -> Self { - Number { - val: BigRational::from_float(b).unwrap(), +macro_rules! from_smaller_integer { + ($ty:ty) => { + impl From<$ty> for Number { + fn from(val: $ty) -> Self { + Number::new_machine(Rational64::from_integer(val as i64)) + } } + }; +} + +impl From for Number { + fn from(val: i64) -> Self { + Number::new_machine(Rational64::from_integer(val)) + } +} + +// todo: implement std::convertTryFrom instead +impl From for Number { + fn from(b: f64) -> Self { + Number::Big(BigRational::from_float(b).unwrap()) } } -from_integer!(u16); from_integer!(usize); from_integer!(isize); -from_integer!(i32); -from_integer!(u32); -from_integer!(u8); +from_smaller_integer!(i32); +from_smaller_integer!(u32); +from_smaller_integer!(u8); impl fmt::Debug for Number { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -170,13 +230,13 @@ impl fmt::Debug for Number { impl Display for Number { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut whole = self.val.to_integer().abs(); - let has_decimal = !self.val.denom().is_one(); - let mut frac = self.val.fract().abs(); + let mut whole = self.to_integer().abs(); + let has_decimal = self.is_decimal(); + let mut frac = self.abs().fract(); let mut dec = String::with_capacity(if has_decimal { PRECISION + 1 } else { 0 }); if has_decimal { for _ in 0..(PRECISION - 1) { - frac *= BigInt::from(10); + frac = frac * Self::from(10); write!(dec, "{}", frac.to_integer())?; frac = frac.fract(); if frac.is_zero() { @@ -184,7 +244,7 @@ impl Display for Number { } } if !frac.is_zero() { - let end = (frac * BigInt::from(10)).round().to_integer(); + let end = (frac * Self::from(10)).round().to_integer(); if end == BigInt::from(10) { loop { match dec.pop() { @@ -216,7 +276,7 @@ impl Display for Number { } } - if self.val.is_negative() && (!whole.is_zero() || !dec.is_empty()) { + if self.is_negative() && (!whole.is_zero() || !dec.is_empty()) { f.write_char('-')?; } write!(f, "{}", whole)?; @@ -228,12 +288,67 @@ impl Display for Number { } } +impl PartialOrd for Number { + fn partial_cmp(&self, other: &Self) -> Option { + match self { + Self::Machine(val1) => match other { + Self::Machine(val2) => val1.partial_cmp(val2), + Self::Big(val2) => { + let tuple: (i64, i64) = (*val1).into(); + BigRational::new_raw(BigInt::from(tuple.0), BigInt::from(tuple.1)) + .partial_cmp(val2) + } + }, + Self::Big(val1) => match other { + Self::Machine(val2) => { + let tuple: (i64, i64) = (*val2).into(); + val1.partial_cmp(&BigRational::new_raw( + BigInt::from(tuple.0), + BigInt::from(tuple.1), + )) + } + Self::Big(val2) => val1.partial_cmp(val2), + }, + } + } +} + impl Add for Number { type Output = Self; fn add(self, other: Self) -> Self { - Number { - val: self.val + other.val, + match self { + Self::Machine(val1) => match other { + Self::Machine(val2) => match val1.checked_add(&val2) { + Some(v) => Self::Machine(v), + None => { + let tuple1: (i64, i64) = val1.into(); + let tuple2: (i64, i64) = val2.into(); + Self::Big( + BigRational::new_raw(BigInt::from(tuple1.0), BigInt::from(tuple1.1)) + + BigRational::new_raw( + BigInt::from(tuple2.0), + BigInt::from(tuple2.1), + ), + ) + } + }, + Self::Big(val2) => { + let tuple: (i64, i64) = val1.into(); + Self::Big( + BigRational::new_raw(BigInt::from(tuple.0), BigInt::from(tuple.1)) + val2, + ) + } + }, + Self::Big(val1) => match other { + Self::Big(val2) => Self::Big(val1 + val2), + Self::Machine(val2) => { + let tuple: (i64, i64) = val2.into(); + Self::Big( + val1 + BigRational::new_raw(BigInt::from(tuple.0), BigInt::from(tuple.1)), + ) + } + }, } } } @@ -242,15 +357,46 @@ impl Add<&Self> for Number { type Output = Self; fn add(self, other: &Self) -> Self { - Number { - val: self.val + &other.val, + match self { + Self::Machine(val1) => match other { + Self::Machine(val2) => match val1.checked_add(val2) { + Some(v) => Self::Machine(v), + None => { + let tuple1: (i64, i64) = val1.into(); + let tuple2: (i64, i64) = (*val2).into(); + Self::Big( + BigRational::new_raw(BigInt::from(tuple1.0), BigInt::from(tuple1.1)) + + BigRational::new_raw( + BigInt::from(tuple2.0), + BigInt::from(tuple2.1), + ), + ) + } + }, + Self::Big(val2) => { + let tuple: (i64, i64) = val1.into(); + Self::Big( + BigRational::new_raw(BigInt::from(tuple.0), BigInt::from(tuple.1)) + val2, + ) + } + }, + Self::Big(val1) => match other { + Self::Big(val2) => Self::Big(val1 + val2), + Self::Machine(val2) => { + let tuple: (i64, i64) = (*val2).into(); + Self::Big( + val1 + BigRational::new_raw(BigInt::from(tuple.0), BigInt::from(tuple.1)), + ) + } + }, } } } impl AddAssign for Number { fn add_assign(&mut self, other: Self) { - self.val += other.val + let tmp = mem::take(self); + *self = tmp + other; } } @@ -258,15 +404,46 @@ impl Sub for Number { type Output = Self; fn sub(self, other: Self) -> Self { - Number { - val: self.val - other.val, + match self { + Self::Machine(val1) => match other { + Self::Machine(val2) => match val1.checked_sub(&val2) { + Some(v) => Self::Machine(v), + None => { + let tuple1: (i64, i64) = val1.into(); + let tuple2: (i64, i64) = val2.into(); + Self::Big( + BigRational::new_raw(BigInt::from(tuple1.0), BigInt::from(tuple1.1)) + - BigRational::new_raw( + BigInt::from(tuple2.0), + BigInt::from(tuple2.1), + ), + ) + } + }, + Self::Big(val2) => { + let tuple: (i64, i64) = val1.into(); + Self::Big( + BigRational::new_raw(BigInt::from(tuple.0), BigInt::from(tuple.1)) - val2, + ) + } + }, + Self::Big(val1) => match other { + Self::Big(val2) => Self::Big(val1 - val2), + Self::Machine(val2) => { + let tuple: (i64, i64) = val2.into(); + Self::Big( + val1 - BigRational::new_raw(BigInt::from(tuple.0), BigInt::from(tuple.1)), + ) + } + }, } } } impl SubAssign for Number { fn sub_assign(&mut self, other: Self) { - self.val -= other.val + let tmp = mem::take(self); + *self = tmp - other; } } @@ -274,15 +451,46 @@ impl Mul for Number { type Output = Self; fn mul(self, other: Self) -> Self { - Number { - val: self.val * other.val, + match self { + Self::Machine(val1) => match other { + Self::Machine(val2) => match val1.checked_mul(&val2) { + Some(v) => Self::Machine(v), + None => { + let tuple1: (i64, i64) = val1.into(); + let tuple2: (i64, i64) = val2.into(); + Self::Big( + BigRational::new_raw(BigInt::from(tuple1.0), BigInt::from(tuple1.1)) + * BigRational::new_raw( + BigInt::from(tuple2.0), + BigInt::from(tuple2.1), + ), + ) + } + }, + Self::Big(val2) => { + let tuple: (i64, i64) = val1.into(); + Self::Big( + BigRational::new_raw(BigInt::from(tuple.0), BigInt::from(tuple.1)) * val2, + ) + } + }, + Self::Big(val1) => match other { + Self::Big(val2) => Self::Big(val1 * val2), + Self::Machine(val2) => { + let tuple: (i64, i64) = val2.into(); + Self::Big( + val1 * BigRational::new_raw(BigInt::from(tuple.0), BigInt::from(tuple.1)), + ) + } + }, } } } impl MulAssign for Number { fn mul_assign(&mut self, other: Self) { - self.val *= other.val + let tmp = mem::take(self); + *self = tmp * other; } } @@ -290,15 +498,46 @@ impl Div for Number { type Output = Self; fn div(self, other: Self) -> Self { - Number { - val: self.val / other.val, + match self { + Self::Machine(val1) => match other { + Self::Machine(val2) => match val1.checked_div(&val2) { + Some(v) => Self::Machine(v), + None => { + let tuple1: (i64, i64) = val1.into(); + let tuple2: (i64, i64) = val2.into(); + Self::Big( + BigRational::new_raw(BigInt::from(tuple1.0), BigInt::from(tuple1.1)) + / BigRational::new_raw( + BigInt::from(tuple2.0), + BigInt::from(tuple2.1), + ), + ) + } + }, + Self::Big(val2) => { + let tuple: (i64, i64) = val1.into(); + Self::Big( + BigRational::new_raw(BigInt::from(tuple.0), BigInt::from(tuple.1)) / val2, + ) + } + }, + Self::Big(val1) => match other { + Self::Big(val2) => Self::Big(val1 / val2), + Self::Machine(val2) => { + let tuple: (i64, i64) = val2.into(); + Self::Big( + val1 / BigRational::new_raw(BigInt::from(tuple.0), BigInt::from(tuple.1)), + ) + } + }, } } } impl DivAssign for Number { fn div_assign(&mut self, other: Self) { - self.val /= other.val + let tmp = mem::take(self); + *self = tmp / other; } } @@ -306,15 +545,41 @@ impl Rem for Number { type Output = Self; fn rem(self, other: Self) -> Self { - Number { - val: self.val % other.val, + match self { + Self::Machine(val1) => match other { + // todo: checked_rem for ratio? + Self::Machine(val2) => { + let tuple1: (i64, i64) = val1.into(); + let tuple2: (i64, i64) = val2.into(); + Self::Big( + BigRational::new_raw(BigInt::from(tuple1.0), BigInt::from(tuple1.1)) + % BigRational::new_raw(BigInt::from(tuple2.0), BigInt::from(tuple2.1)), + ) + } + Self::Big(val2) => { + let tuple: (i64, i64) = val1.into(); + Self::Big( + BigRational::new_raw(BigInt::from(tuple.0), BigInt::from(tuple.1)) % val2, + ) + } + }, + Self::Big(val1) => match other { + Self::Big(val2) => Self::Big(val1 % val2), + Self::Machine(val2) => { + let tuple: (i64, i64) = val2.into(); + Self::Big( + val1 % BigRational::new_raw(BigInt::from(tuple.0), BigInt::from(tuple.1)), + ) + } + }, } } } impl RemAssign for Number { fn rem_assign(&mut self, other: Self) { - self.val %= other.val + let tmp = mem::take(self); + *self = tmp % other; } } @@ -322,6 +587,9 @@ impl Neg for Number { type Output = Self; fn neg(self) -> Self { - Number { val: -self.val } + match self { + Self::Machine(v) => Self::Machine(-v), + Self::Big(v) => Self::Big(-v), + } } } diff --git a/src/value/parse.rs b/src/value/parse.rs index 04c2602..f87c781 100644 --- a/src/value/parse.rs +++ b/src/value/parse.rs @@ -2,7 +2,7 @@ use std::iter::Iterator; use std::mem; use num_bigint::BigInt; -use num_rational::BigRational; +use num_rational::{BigRational, Rational64}; use num_traits::{pow, One, ToPrimitive}; use codemap::{Span, Spanned}; @@ -668,14 +668,27 @@ impl Value { }; let n = if val.dec_len == 0 { + if val.num.len() <= 18 && val.times_ten.is_empty() { + let n = Rational64::new_raw(val.num.parse::().unwrap(), 1i64); + return Some(Ok(IntermediateValue::Value( + Value::Dimension(Number::new_machine(n), unit).span(span), + ))); + } BigRational::new_raw(val.num.parse::().unwrap(), BigInt::one()) } else { + if val.num.len() <= 18 && val.times_ten.is_empty() { + let n = + Rational64::new(val.num.parse::().unwrap(), pow(10, val.dec_len)); + return Some(Ok(IntermediateValue::Value( + Value::Dimension(Number::new_machine(n), unit).span(span), + ))); + } BigRational::new(val.num.parse().unwrap(), pow(BigInt::from(10), val.dec_len)) }; if val.times_ten.is_empty() { return Some(Ok(IntermediateValue::Value( - Value::Dimension(Number::new(n), unit).span(span), + Value::Dimension(Number::new_big(n), unit).span(span), ))); } @@ -700,7 +713,7 @@ impl Value { }; IntermediateValue::Value( - Value::Dimension(Number::new(n * times_ten), unit).span(span), + Value::Dimension(Number::new_big(n * times_ten), unit).span(span), ) } '(' => {