diff --git a/src/calc/entries.rs b/src/calc/entries.rs index 99bc850..667c429 100644 --- a/src/calc/entries.rs +++ b/src/calc/entries.rs @@ -8,25 +8,25 @@ use std::fmt; #[derive(Copy, Clone, Debug, Serialize, Deserialize)] pub struct Number { - pub value: f64, + pub value: f64, } impl PartialEq for Number { - fn eq(&self, other: &Self) -> bool { - if self.value.is_nan() && other.value.is_nan() - || self.value.is_infinite() && other.value.is_infinite() - { - true - } else if self.value.is_nan() - || self.value.is_infinite() - || other.value.is_infinite() - || other.value.is_nan() - { - false - } else { - (self.value - other.value).abs() >= f64::EPSILON + fn eq(&self, other: &Self) -> bool { + if self.value.is_nan() && other.value.is_nan() + || self.value.is_infinite() && other.value.is_infinite() + { + true + } else if self.value.is_nan() + || self.value.is_infinite() + || other.value.is_infinite() + || other.value.is_nan() + { + false + } else { + (self.value - other.value).abs() >= f64::EPSILON + } } - } } // #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] @@ -34,400 +34,418 @@ impl PartialEq for Number { // pub value: Vec, // } -#[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize)] +#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "type")] pub enum Entry { - Number(Number), - // Vector(Vec), - // Matrix(Vec>), + Number(Number), + // Vector(Vec), + // Matrix(Vec>), } impl CalculatorEntry for Entry { - fn format_entry(&self, display_mode: &CalculatorDisplayMode) -> String { - match self { - Self::Number(number) => number.format_entry(display_mode), - // Self::Vector(vector) => vector.add(), + fn format_entry(&self, display_mode: &CalculatorDisplayMode) -> String { + match self { + Self::Number(number) => number.format_entry(display_mode), + // Self::Vector(vector) => vector.add(), + } } - } - fn is_valid(&self) -> bool { - match self { - Self::Number(number) => number.is_valid(), - // Self::Vector(vector) => vector.add(), + fn is_valid(&self) -> bool { + match self { + Self::Number(number) => number.is_valid(), + // Self::Vector(vector) => vector.add(), + } } - } - fn add(&self, arg: Self) -> CalculatorResult { - match self { - Self::Number(number) => number.add(arg), - // Self::Vector(vector) => vector.add(), + fn add(&self, arg: Self) -> CalculatorResult { + match self { + Self::Number(number) => number.add(arg), + // Self::Vector(vector) => vector.add(), + } } - } - fn sub(&self, arg: Self) -> CalculatorResult { - match self { - Self::Number(number) => number.sub(arg), - // Self::Vector(vector) => vector.sub(), + fn sub(&self, arg: Self) -> CalculatorResult { + match self { + Self::Number(number) => number.sub(arg), + // Self::Vector(vector) => vector.sub(), + } } - } - fn mul(&self, arg: Self) -> CalculatorResult { - match self { - Self::Number(number) => number.mul(arg), - // Self::Vector(vector) => vector.mul(), + fn mul(&self, arg: Self) -> CalculatorResult { + match self { + Self::Number(number) => number.mul(arg), + // Self::Vector(vector) => vector.mul(), + } } - } - fn div(&self, arg: Self) -> CalculatorResult { - match self { - Self::Number(number) => number.div(arg), - // Self::Vector(vector) => vector.div(), + fn div(&self, arg: Self) -> CalculatorResult { + match self { + Self::Number(number) => number.div(arg), + // Self::Vector(vector) => vector.div(), + } } - } - fn int_divide(&self, arg: Self) -> CalculatorResult { - match self { - Self::Number(number) => number.int_divide(arg), - // Self::Vector(vector) => vector.int_divide(), + fn int_divide(&self, arg: Self) -> CalculatorResult { + match self { + Self::Number(number) => number.int_divide(arg), + // Self::Vector(vector) => vector.int_divide(), + } } - } - fn negate(&self) -> CalculatorResult { - match self { - Self::Number(number) => number.negate(), - // Self::Vector(vector) => vector.negate(), + fn negate(&self) -> CalculatorResult { + match self { + Self::Number(number) => number.negate(), + // Self::Vector(vector) => vector.negate(), + } } - } - fn abs(&self) -> CalculatorResult { - match self { - Self::Number(number) => number.abs(), - // Self::Vector(vector) => vector.abs(), + fn abs(&self) -> CalculatorResult { + match self { + Self::Number(number) => number.abs(), + // Self::Vector(vector) => vector.abs(), + } } - } - fn inverse(&self) -> CalculatorResult { - match self { - Self::Number(number) => number.inverse(), - // Self::Vector(vector) => vector.inverse(), + fn inverse(&self) -> CalculatorResult { + match self { + Self::Number(number) => number.inverse(), + // Self::Vector(vector) => vector.inverse(), + } } - } - fn modulo(&self, arg: Self) -> CalculatorResult { - match self { - Self::Number(number) => number.modulo(arg), - // Self::Vector(vector) => vector.modulo(), + fn modulo(&self, arg: Self) -> CalculatorResult { + match self { + Self::Number(number) => number.modulo(arg), + // Self::Vector(vector) => vector.modulo(), + } } - } - fn sin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { - match self { - Self::Number(number) => number.sin(angle_mode), - // Self::Vector(vector) => vector.sin(), + fn sin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { + match self { + Self::Number(number) => number.sin(angle_mode), + // Self::Vector(vector) => vector.sin(), + } } - } - fn cos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { - match self { - Self::Number(number) => number.cos(angle_mode), - // Self::Vector(vector) => vector.cos(), + fn cos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { + match self { + Self::Number(number) => number.cos(angle_mode), + // Self::Vector(vector) => vector.cos(), + } } - } - fn tan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { - match self { - Self::Number(number) => number.tan(angle_mode), - // Self::Vector(vector) => vector.tan(), + fn tan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { + match self { + Self::Number(number) => number.tan(angle_mode), + // Self::Vector(vector) => vector.tan(), + } } - } - fn asin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { - match self { - Self::Number(number) => number.asin(angle_mode), - // Self::Vector(vector) => vector.asin(), + fn asin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { + match self { + Self::Number(number) => number.asin(angle_mode), + // Self::Vector(vector) => vector.asin(), + } } - } - fn acos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { - match self { - Self::Number(number) => number.acos(angle_mode), - // Self::Vector(vector) => vector.acos(), + fn acos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { + match self { + Self::Number(number) => number.acos(angle_mode), + // Self::Vector(vector) => vector.acos(), + } } - } - fn atan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { - match self { - Self::Number(number) => number.atan(angle_mode), - // Self::Vector(vector) => vector.atan(), + fn atan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { + match self { + Self::Number(number) => number.atan(angle_mode), + // Self::Vector(vector) => vector.atan(), + } } - } - fn sqrt(&self) -> CalculatorResult { - match self { - Self::Number(number) => number.sqrt(), - // Self::Vector(vector) => vector.sqrt(), + fn sqrt(&self) -> CalculatorResult { + match self { + Self::Number(number) => number.sqrt(), + // Self::Vector(vector) => vector.sqrt(), + } } - } - fn log(&self) -> CalculatorResult { - match self { - Self::Number(number) => number.log(), - // Self::Vector(vector) => vector.log(), + fn log(&self) -> CalculatorResult { + match self { + Self::Number(number) => number.log(), + // Self::Vector(vector) => vector.log(), + } } - } - fn ln(&self) -> CalculatorResult { - match self { - Self::Number(number) => number.ln(), - // Self::Vector(vector) => vector.ln(), + fn ln(&self) -> CalculatorResult { + match self { + Self::Number(number) => number.ln(), + // Self::Vector(vector) => vector.ln(), + } } - } - fn pow(&self, arg: Self) -> CalculatorResult { - match self { - Self::Number(number) => number.pow(arg), - // Self::Vector(vector) => vector.pow(), + fn pow(&self, arg: Self) -> CalculatorResult { + match self { + Self::Number(number) => number.pow(arg), + // Self::Vector(vector) => vector.pow(), + } } - } } impl CalculatorEntry for Number { - fn format_entry(&self, display_mode: &CalculatorDisplayMode) -> String { - match display_mode { - CalculatorDisplayMode::Default => format!("{}", self.value), - CalculatorDisplayMode::Separated { separator } => separated(self.value, *separator), - CalculatorDisplayMode::Scientific { precision } => scientific(self.value, *precision), - CalculatorDisplayMode::Engineering { precision } => engineering(self.value, *precision), - CalculatorDisplayMode::Fixed { precision } => { - format!("{:0>.precision$}", self.value, precision = precision) - } + fn format_entry(&self, display_mode: &CalculatorDisplayMode) -> String { + match display_mode { + CalculatorDisplayMode::Default => format!("{}", self.value), + CalculatorDisplayMode::Separated { separator } => { + separated(self.value, *separator) + } + CalculatorDisplayMode::Scientific { precision } => { + scientific(self.value, *precision) + } + CalculatorDisplayMode::Engineering { precision } => { + engineering(self.value, *precision) + } + CalculatorDisplayMode::Fixed { precision } => { + format!("{:0>.precision$}", self.value, precision = precision) + } + } } - } - fn is_valid(&self) -> bool { - !self.value.is_nan() && !self.value.is_infinite() - } - fn add(&self, arg: Entry) -> CalculatorResult { - match arg { - Entry::Number(Number { value }) => Ok(Entry::Number(Number { - value: value + self.value, - })), + fn is_valid(&self) -> bool { + !self.value.is_nan() && !self.value.is_infinite() } - } - fn sub(&self, arg: Entry) -> CalculatorResult { - match arg { - Entry::Number(Number { value }) => Ok(Entry::Number(Number { - value: value - self.value, - })), + fn add(&self, arg: Entry) -> CalculatorResult { + match arg { + Entry::Number(Number { value }) => Ok(Entry::Number(Number { + value: value + self.value, + })), + } } - } - fn mul(&self, arg: Entry) -> CalculatorResult { - match arg { - Entry::Number(Number { value }) => Ok(Entry::Number(Number { - value: value * self.value, - })), + fn sub(&self, arg: Entry) -> CalculatorResult { + match arg { + Entry::Number(Number { value }) => Ok(Entry::Number(Number { + value: value - self.value, + })), + } } - } - fn div(&self, arg: Entry) -> CalculatorResult { - match arg { - Entry::Number(Number { value }) => Ok(Entry::Number(Number { - value: value / self.value, - })), + fn mul(&self, arg: Entry) -> CalculatorResult { + match arg { + Entry::Number(Number { value }) => Ok(Entry::Number(Number { + value: value * self.value, + })), + } } - } - fn int_divide(&self, arg: Entry) -> CalculatorResult { - match arg { - Entry::Number(Number { value }) => Ok(Entry::Number(Number { - value: value.div_euclid(self.value), - })), + fn div(&self, arg: Entry) -> CalculatorResult { + match arg { + Entry::Number(Number { value }) => Ok(Entry::Number(Number { + value: value / self.value, + })), + } } - } - fn negate(&self) -> CalculatorResult { - Ok(Entry::Number(Number { value: -self.value })) - } - fn abs(&self) -> CalculatorResult { - Ok(Entry::Number(Number { - value: self.value.abs(), - })) - } - fn inverse(&self) -> CalculatorResult { - Ok(Entry::Number(Number { - value: self.value.recip(), - })) - } - fn modulo(&self, arg: Entry) -> CalculatorResult { - match arg { - Entry::Number(Number { value }) => Ok(Entry::Number(Number { - value: value % self.value, - })), + fn int_divide(&self, arg: Entry) -> CalculatorResult { + match arg { + Entry::Number(Number { value }) => Ok(Entry::Number(Number { + value: value.div_euclid(self.value), + })), + } } - } - fn sin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { - Ok(Entry::Number(Number { - value: match angle_mode { - CalculatorAngleMode::Degrees => self.value.to_radians().sin(), - CalculatorAngleMode::Radians => self.value.sin(), - CalculatorAngleMode::Grads => (self.value * std::f64::consts::PI / 200.0).sin(), - }, - })) - } - fn cos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { - Ok(Entry::Number(Number { - value: match angle_mode { - CalculatorAngleMode::Degrees => self.value.to_radians().cos(), - CalculatorAngleMode::Radians => self.value.cos(), - CalculatorAngleMode::Grads => (self.value * std::f64::consts::PI / 200.0).cos(), - }, - })) - } - fn tan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { - Ok(Entry::Number(Number { - value: match angle_mode { - CalculatorAngleMode::Degrees => self.value.to_radians().tan(), - CalculatorAngleMode::Radians => self.value.tan(), - CalculatorAngleMode::Grads => (self.value * std::f64::consts::PI / 200.0).tan(), - }, - })) - } - fn asin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { - Ok(Entry::Number(Number { - value: match angle_mode { - CalculatorAngleMode::Degrees => self.value.asin().to_degrees(), - CalculatorAngleMode::Radians => self.value.asin(), - CalculatorAngleMode::Grads => self.value.asin() * std::f64::consts::PI / 200.0, - }, - })) - } - fn acos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { - Ok(Entry::Number(Number { - value: match angle_mode { - CalculatorAngleMode::Degrees => self.value.acos().to_degrees(), - CalculatorAngleMode::Radians => self.value.acos(), - CalculatorAngleMode::Grads => self.value.acos() * std::f64::consts::PI / 200.0, - }, - })) - } - fn atan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { - Ok(Entry::Number(Number { - value: match angle_mode { - CalculatorAngleMode::Degrees => self.value.atan().to_degrees(), - CalculatorAngleMode::Radians => self.value.atan(), - CalculatorAngleMode::Grads => self.value.atan() * std::f64::consts::PI / 200.0, - }, - })) - } - fn sqrt(&self) -> CalculatorResult { - Ok(Entry::Number(Number { - value: self.value.sqrt(), - })) - } - fn log(&self) -> CalculatorResult { - Ok(Entry::Number(Number { - value: self.value.log10(), - })) - } - fn ln(&self) -> CalculatorResult { - Ok(Entry::Number(Number { - value: self.value.ln(), - })) - } - fn pow(&self, arg: Entry) -> CalculatorResult { - match arg { - Entry::Number(Number { value }) => Ok(Entry::Number(Number { - value: value.powf(self.value), - })), + fn negate(&self) -> CalculatorResult { + Ok(Entry::Number(Number { value: -self.value })) + } + fn abs(&self) -> CalculatorResult { + Ok(Entry::Number(Number { + value: self.value.abs(), + })) + } + fn inverse(&self) -> CalculatorResult { + Ok(Entry::Number(Number { + value: self.value.recip(), + })) + } + fn modulo(&self, arg: Entry) -> CalculatorResult { + match arg { + Entry::Number(Number { value }) => Ok(Entry::Number(Number { + value: value % self.value, + })), + } + } + fn sin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { + Ok(Entry::Number(Number { + value: match angle_mode { + CalculatorAngleMode::Degrees => self.value.to_radians().sin(), + CalculatorAngleMode::Radians => self.value.sin(), + CalculatorAngleMode::Grads => { + (self.value * std::f64::consts::PI / 200.0).sin() + } + }, + })) + } + fn cos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { + Ok(Entry::Number(Number { + value: match angle_mode { + CalculatorAngleMode::Degrees => self.value.to_radians().cos(), + CalculatorAngleMode::Radians => self.value.cos(), + CalculatorAngleMode::Grads => { + (self.value * std::f64::consts::PI / 200.0).cos() + } + }, + })) + } + fn tan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { + Ok(Entry::Number(Number { + value: match angle_mode { + CalculatorAngleMode::Degrees => self.value.to_radians().tan(), + CalculatorAngleMode::Radians => self.value.tan(), + CalculatorAngleMode::Grads => { + (self.value * std::f64::consts::PI / 200.0).tan() + } + }, + })) + } + fn asin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { + Ok(Entry::Number(Number { + value: match angle_mode { + CalculatorAngleMode::Degrees => self.value.asin().to_degrees(), + CalculatorAngleMode::Radians => self.value.asin(), + CalculatorAngleMode::Grads => { + self.value.asin() * std::f64::consts::PI / 200.0 + } + }, + })) + } + fn acos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { + Ok(Entry::Number(Number { + value: match angle_mode { + CalculatorAngleMode::Degrees => self.value.acos().to_degrees(), + CalculatorAngleMode::Radians => self.value.acos(), + CalculatorAngleMode::Grads => { + self.value.acos() * std::f64::consts::PI / 200.0 + } + }, + })) + } + fn atan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { + Ok(Entry::Number(Number { + value: match angle_mode { + CalculatorAngleMode::Degrees => self.value.atan().to_degrees(), + CalculatorAngleMode::Radians => self.value.atan(), + CalculatorAngleMode::Grads => { + self.value.atan() * std::f64::consts::PI / 200.0 + } + }, + })) + } + fn sqrt(&self) -> CalculatorResult { + Ok(Entry::Number(Number { + value: self.value.sqrt(), + })) + } + fn log(&self) -> CalculatorResult { + Ok(Entry::Number(Number { + value: self.value.log10(), + })) + } + fn ln(&self) -> CalculatorResult { + Ok(Entry::Number(Number { + value: self.value.ln(), + })) + } + fn pow(&self, arg: Entry) -> CalculatorResult { + match arg { + Entry::Number(Number { value }) => Ok(Entry::Number(Number { + value: value.powf(self.value), + })), + } } - } } pub trait CalculatorEntry { - // Misc - fn is_valid(&self) -> bool; - fn format_entry(&self, display_mode: &CalculatorDisplayMode) -> String; - // Mathematical operations - fn add(&self, arg: Entry) -> CalculatorResult; - fn sub(&self, arg: Entry) -> CalculatorResult; - fn mul(&self, arg: Entry) -> CalculatorResult; - fn div(&self, arg: Entry) -> CalculatorResult; - fn int_divide(&self, arg: Entry) -> CalculatorResult; - fn negate(&self) -> CalculatorResult; - fn abs(&self) -> CalculatorResult; - fn inverse(&self) -> CalculatorResult; - fn modulo(&self, arg: Entry) -> CalculatorResult; - fn sin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult; - fn cos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult; - fn tan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult; - fn asin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult; - fn acos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult; - fn atan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult; - fn sqrt(&self) -> CalculatorResult; - fn log(&self) -> CalculatorResult; - fn ln(&self) -> CalculatorResult; - fn pow(&self, arg: Entry) -> CalculatorResult; + // Misc + fn is_valid(&self) -> bool; + fn format_entry(&self, display_mode: &CalculatorDisplayMode) -> String; + // Mathematical operations + fn add(&self, arg: Entry) -> CalculatorResult; + fn sub(&self, arg: Entry) -> CalculatorResult; + fn mul(&self, arg: Entry) -> CalculatorResult; + fn div(&self, arg: Entry) -> CalculatorResult; + fn int_divide(&self, arg: Entry) -> CalculatorResult; + fn negate(&self) -> CalculatorResult; + fn abs(&self) -> CalculatorResult; + fn inverse(&self) -> CalculatorResult; + fn modulo(&self, arg: Entry) -> CalculatorResult; + fn sin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult; + fn cos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult; + fn tan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult; + fn asin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult; + fn acos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult; + fn atan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult; + fn sqrt(&self) -> CalculatorResult; + fn log(&self) -> CalculatorResult; + fn ln(&self) -> CalculatorResult; + fn pow(&self, arg: Entry) -> CalculatorResult; } impl fmt::Display for Entry { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::Number(Number { value }) => write!(f, "{}", value), + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::Number(Number { value }) => write!(f, "{}", value), + } } - } } impl fmt::Display for Number { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self) + } } // Based on https://stackoverflow.com/a/65266882 fn scientific(f: f64, precision: usize) -> String { - let mut ret = format!("{:.precision$E}", f, precision = precision); - let exp = ret.split_off(ret.find('E').unwrap_or(0)); - let (exp_sign, exp) = exp - .strip_prefix("E-") - .map_or_else(|| ('+', &exp[1..]), |stripped| ('-', stripped)); + let mut ret = format!("{:.precision$E}", f, precision = precision); + let exp = ret.split_off(ret.find('E').unwrap_or(0)); + let (exp_sign, exp) = exp + .strip_prefix("E-") + .map_or_else(|| ('+', &exp[1..]), |stripped| ('-', stripped)); - let sign = if ret.starts_with('-') { "" } else { " " }; - format!("{}{} E{}{:0>pad$}", sign, ret, exp_sign, exp, pad = 2) + let sign = if ret.starts_with('-') { "" } else { " " }; + format!("{}{} E{}{:0>pad$}", sign, ret, exp_sign, exp, pad = 2) } fn engineering(f: f64, precision: usize) -> String { - // Format the string so the first digit is always in the first column, and remove '.'. Requested precision + 2 to account for using 1, 2, or 3 digits for the whole portion of the string - // 1,000 => 1000E3 - let all = format!(" {:.precision$E}", f, precision = precision) - // Remove . since it can be moved - .replacen(".", "", 1) - // Add 00E before E here so the length is enough for slicing below - .replacen("E", "00E", 1); - // Extract mantissa and the string representation of the exponent. Unwrap should be safe as formatter will insert E - // 1000E3 => (1000, E3) - let (num_str, exp_str) = all.split_at(all.find('E').unwrap()); - // Extract the exponent as an isize. This should always be true because Entry max will be ~400 - // E3 => 3 as isize - let exp = exp_str[1..].parse::().unwrap(); - // Sign of the exponent. If string representation starts with E-, then negative - let display_exp_sign = if exp_str.strip_prefix("E-").is_some() { - '-' - } else { - '+' - }; + // Format the string so the first digit is always in the first column, and remove '.'. Requested precision + 2 to account for using 1, 2, or 3 digits for the whole portion of the string + // 1,000 => 1000E3 + let all = format!(" {:.precision$E}", f, precision = precision) + // Remove . since it can be moved + .replacen(".", "", 1) + // Add 00E before E here so the length is enough for slicing below + .replacen("E", "00E", 1); + // Extract mantissa and the string representation of the exponent. Unwrap should be safe as formatter will insert E + // 1000E3 => (1000, E3) + let (num_str, exp_str) = all.split_at(all.find('E').unwrap()); + // Extract the exponent as an isize. This should always be true because Entry max will be ~400 + // E3 => 3 as isize + let exp = exp_str[1..].parse::().unwrap(); + // Sign of the exponent. If string representation starts with E-, then negative + let display_exp_sign = if exp_str.strip_prefix("E-").is_some() { + '-' + } else { + '+' + }; - // The exponent to display. Always a multiple of 3 in engineering mode. Always positive because sign is added with display_exp_sign above - // 100 => 0, 1000 => 3, .1 => 3 (but will show as -3) - let display_exp = (exp.div_euclid(3) * 3).abs(); - // Number of whole digits. Always 1, 2, or 3 depending on exponent divisibility - let num_whole_digits = exp.rem_euclid(3) as usize + 1; + // The exponent to display. Always a multiple of 3 in engineering mode. Always positive because sign is added with display_exp_sign above + // 100 => 0, 1000 => 3, .1 => 3 (but will show as -3) + let display_exp = (exp.div_euclid(3) * 3).abs(); + // Number of whole digits. Always 1, 2, or 3 depending on exponent divisibility + let num_whole_digits = exp.rem_euclid(3) as usize + 1; - // If this is a negative number, strip off the added space, otherwise keep the space (and next digit) - let num_str = if num_str.strip_prefix(" -").is_some() { - &num_str[1..] - } else { - num_str - }; + // If this is a negative number, strip off the added space, otherwise keep the space (and next digit) + let num_str = if num_str.strip_prefix(" -").is_some() { + &num_str[1..] + } else { + num_str + }; - // Whole portion of number. Slice is safe because the num_whole_digits is always 3 and the num_str will always have length >= 3 since precision in all=2 (+original whole digit) - // Original number is 1,000 => whole will be 1, if original is 0.01, whole will be 10 - let whole = &num_str[0..=num_whole_digits]; - // Decimal portion of the number. Sliced from the number of whole digits to the *requested* precision. Precision generated in all will be requested precision + 2 - let decimal = &num_str[(num_whole_digits + 1)..=(precision + num_whole_digits)]; - // Right align whole portion, always have decimal point - format!( - "{: >4}.{} E{}{:0>pad$}", - // display_sign, - whole, - decimal, - display_exp_sign, - display_exp, - pad = 2 - ) + // Whole portion of number. Slice is safe because the num_whole_digits is always 3 and the num_str will always have length >= 3 since precision in all=2 (+original whole digit) + // Original number is 1,000 => whole will be 1, if original is 0.01, whole will be 10 + let whole = &num_str[0..=num_whole_digits]; + // Decimal portion of the number. Sliced from the number of whole digits to the *requested* precision. Precision generated in all will be requested precision + 2 + let decimal = &num_str[(num_whole_digits + 1)..=(precision + num_whole_digits)]; + // Right align whole portion, always have decimal point + format!( + "{: >4}.{} E{}{:0>pad$}", + // display_sign, + whole, + decimal, + display_exp_sign, + display_exp, + pad = 2 + ) } fn separated(f: f64, sep: char) -> String { - let mut ret = f.to_string(); - let start = if ret.starts_with('-') { 1 } else { 0 }; - let end = ret.find('.').unwrap_or_else(|| ret.len()); - for i in 0..((end - start - 1).div_euclid(3)) { - ret.insert(end - (i + 1) * 3, sep); - } - ret + let mut ret = f.to_string(); + let start = if ret.starts_with('-') { 1 } else { 0 }; + let end = ret.find('.').unwrap_or_else(|| ret.len()); + for i in 0..((end - start - 1).div_euclid(3)) { + ret.insert(end - (i + 1) * 3, sep); + } + ret }