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`.
This commit is contained in:
ConnorSkees 2020-05-19 23:23:04 -04:00
parent cf987abb53
commit 8520048b26
4 changed files with 370 additions and 89 deletions

View File

@ -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()

View File

@ -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());

View File

@ -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<A: Into<BigInt>, B: Into<BigInt>>(a: A, b: B) -> Self {
Number::new(BigRational::new(a.into(), b.into()))
pub fn machine_ratio<A: Into<i64>, B: Into<i64>>(a: A, b: B) -> Self {
Number::new_machine(Rational64::new(a.into(), b.into()))
}
#[allow(dead_code)]
pub fn big_ratio<A: Into<BigInt>, B: Into<BigInt>>(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<A: Into<Number> + Zero, B: Into<Number>>(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<f64> 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<i64> for Number {
fn from(val: i64) -> Self {
Number::new_machine(Rational64::from_integer(val))
}
}
// todo: implement std::convertTryFrom instead
impl From<f64> 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<Ordering> {
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),
}
}
}

View File

@ -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::<i64>().unwrap(), 1i64);
return Some(Ok(IntermediateValue::Value(
Value::Dimension(Number::new_machine(n), unit).span(span),
)));
}
BigRational::new_raw(val.num.parse::<BigInt>().unwrap(), BigInt::one())
} else {
if val.num.len() <= 18 && val.times_ten.is_empty() {
let n =
Rational64::new(val.num.parse::<i64>().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),
)
}
'(' => {