Use fastnum::d128 (#41)

This commit is contained in:
Kasper 2025-05-30 03:35:04 +02:00 committed by GitHub
parent 3bbc5c53a1
commit 2d89355566
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 1407 additions and 1531 deletions

View File

@ -1,5 +1,9 @@
# Changelog # Changelog
## Next
- Switch to the fastnum crate's d128
- Fix exp function
## 2.0.0 - 2025 May 30 ## 2.0.0 - 2025 May 30
- Remove the `degrees` keyword which referred to `celcius` by default - Remove the `degrees` keyword which referred to `celcius` by default
- Remove the `default_degrees` argument from `eval()` and `lex()`. Not necessary now that the `degrees` keyword is removed - Remove the `default_degrees` argument from `eval()` and `lex()`. Not necessary now that the `degrees` keyword is removed

103
Cargo.lock generated
View File

@ -12,78 +12,42 @@ dependencies = [
] ]
[[package]] [[package]]
name = "bitflags" name = "autocfg"
version = "1.3.2" version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]] [[package]]
name = "cc" name = "bnum"
version = "1.2.24" version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16595d3be041c03b09d08d0858631facccee9221e579704070e6e9e4915d3bc7" checksum = "f781dba93de3a5ef6dc5b17c9958b208f6f3f021623b360fb605ea51ce443f10"
dependencies = [
"shlex",
]
[[package]] [[package]]
name = "cpc" name = "cpc"
version = "2.0.0" version = "2.0.0"
dependencies = [ dependencies = [
"decimal", "fastnum",
"regex", "regex",
"unicode-segmentation", "unicode-segmentation",
] ]
[[package]] [[package]]
name = "decimal" name = "fastnum"
version = "2.1.0" version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a8ab77e91baeb15034c3be91e87bff4665c9036216148e4996d9a9f5792114d" checksum = "86cde0c9334bfed5ced962bd7acc266e02e254d71494787e4255d8ec4f7296d4"
dependencies = [ dependencies = [
"bitflags", "autocfg",
"cc", "bnum",
"libc",
"ord_subset",
"serde",
] ]
[[package]]
name = "libc"
version = "0.2.172"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.7.4" version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "ord_subset"
version = "3.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7ce14664caf5b27f5656ff727defd68ae1eb75ef3c4d95259361df1eb376bef"
[[package]]
name = "proc-macro2"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.11.1" version = "1.11.1"
@ -113,49 +77,6 @@ version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "serde"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "syn"
version = "2.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]] [[package]]
name = "unicode-segmentation" name = "unicode-segmentation"
version = "1.12.0" version = "1.12.0"

View File

@ -19,11 +19,8 @@ categories = [
] ]
[dependencies] [dependencies]
decimal = { version = "2.1", default-features = false, features = [ fastnum = "0.2"
"serde", unicode-segmentation = "1.12"
"ord_subset",
] }
unicode-segmentation = "1.10"
[dev-dependencies] [dev-dependencies]
regex = "1.11" regex = "1.11"

View File

@ -11,9 +11,6 @@ It also lets you mix units, so for example `1 km - 1m` results in `999 Meter`.
[List of all supported units](https://docs.rs/cpc/latest/cpc/units/enum.Unit.html) [List of all supported units](https://docs.rs/cpc/latest/cpc/units/enum.Unit.html)
> [!TIP]
> [fend](https://github.com/printfn/fend) is a great alternative to cpc
## CLI Installation ## CLI Installation
Install using `cargo`: Install using `cargo`:
``` ```
@ -27,26 +24,6 @@ To install it manually, grab the appropriate binary from the [GitHub Releases pa
cpc '2h/3 to min' cpc '2h/3 to min'
``` ```
## API Installation
Add `cpc` as a dependency in `Cargo.toml`.
## API Usage
```rust
use cpc::eval;
use cpc::units::Unit;
match eval("3m + 1cm", true, Unit::Celsius, false) {
Ok(answer) => {
// answer: Number { value: 301, unit: Unit::Centimeter }
println!("Evaluated value: {} {:?}", answer.value, answer.unit)
},
Err(e) => {
println!("{e}")
}
}
```
## Examples ## Examples
``` ```
3 + 4 * 2 3 + 4 * 2
@ -84,14 +61,29 @@ round(sqrt(2)^4)! liters
- Speed - Speed
- Temperature - Temperature
## API Installation
Add `cpc` as a dependency in `Cargo.toml`.
## API Usage
```rust
use cpc::eval;
use cpc::units::Unit;
match eval("3m + 1cm", true, Unit::Celsius, false) {
Ok(answer) => {
// answer: Number { value: 301, unit: Unit::Centimeter }
println!("Evaluated value: {} {:?}", answer.value, answer.unit)
},
Err(e) => {
println!("{e}")
}
}
```
## Accuracy ## Accuracy
cpc uses 128-bit Decimal Floating Point (d128) numbers instead of Binary Coded Decimals for better accuracy. The result cpc gives will still not always be 100% accurate. I would recommend rounding the result to 20 decimals or less. cpc uses 128-bit Decimal Floating Point (d128) numbers instead of Binary Coded Decimals for better accuracy. The result cpc gives will still not always be 100% accurate. I would recommend rounding the result to 20 decimals or less.
## Performance
It's pretty fast and scales well. In my case, it usually runs in under 0.1ms. The biggest performance hit is functions like `log()`. `log(12345)` evaluates in 0.12ms, and `log(e)` in 0.25ms.
To see how fast it is, you can pass the `--verbose` flag in CLI, or the `verbose` argument to `eval()`.
## Dev Instructions ## Dev Instructions
### Get started ### Get started
@ -165,9 +157,6 @@ match string {
### Potential Improvements ### Potential Improvements
- Support for conversion between Power, Current, Resistance and Voltage. Multiplication and division is currently supported, but not conversions using sqrt or pow. - Support for conversion between Power, Current, Resistance and Voltage. Multiplication and division is currently supported, but not conversions using sqrt or pow.
- Move to pure-rust decimal implementation
- `rust_decimal`: Only supports numbers up to ~1E+29
- `bigdecimal`: Lacking math functions
- E notation, like 2E+10 - E notation, like 2E+10
- Unit types - Unit types
- Currency: How to go about dynamically updating the weights? - Currency: How to go about dynamically updating the weights?

View File

@ -7,7 +7,7 @@ use crate::Operator::{Caret, Divide, Minus, Modulo, Multiply, Plus};
use crate::TextOperator::{Of, To}; use crate::TextOperator::{Of, To};
use crate::UnaryOperator::{Factorial, Percent}; use crate::UnaryOperator::{Factorial, Percent};
use crate::{Number, Token}; use crate::{Number, Token};
use decimal::d128; use fastnum::{dec128 as d, D128};
/// Evaluate an [`AstNode`] into a [`Number`] /// Evaluate an [`AstNode`] into a [`Number`]
pub fn evaluate(ast: &AstNode) -> Result<Number, String> { pub fn evaluate(ast: &AstNode) -> Result<Number, String> {
@ -20,14 +20,14 @@ pub fn evaluate(ast: &AstNode) -> Result<Number, String> {
/// Factorials do not work with decimal numbers. /// Factorials do not work with decimal numbers.
/// ///
/// All return values of this function are hard-coded. /// All return values of this function are hard-coded.
pub fn factorial(input: d128) -> d128 { pub fn factorial(input: D128) -> D128 {
lookup_factorial(input.into()) lookup_factorial(input.try_into().unwrap())
} }
/// Returns the square root of a [`struct@d128`] /// Returns the square root of a [`struct@d128`]
pub fn sqrt(input: d128) -> d128 { pub fn sqrt(input: D128) -> D128 {
let mut n = d128!(1); let mut n = d!(1);
let half = d128!(0.5); let half = d!(0.5);
for _ in 0..10 { for _ in 0..10 {
n = (n + input / n) * half; n = (n + input / n) * half;
} }
@ -35,10 +35,10 @@ pub fn sqrt(input: d128) -> d128 {
} }
/// Returns the cube root of a [`struct@d128`] /// Returns the cube root of a [`struct@d128`]
pub fn cbrt(input: d128) -> d128 { pub fn cbrt(input: D128) -> D128 {
let mut n: d128 = input; let mut n: D128 = input;
// hope that 20 iterations makes it accurate enough // hope that 20 iterations makes it accurate enough
let three = d128!(3); let three = d!(3);
for _ in 0..20 { for _ in 0..20 {
let z2 = n * n; let z2 = n * n;
n = n - ((n * z2 - input) / (three * z2)); n = n - ((n * z2 - input) / (three * z2));
@ -46,60 +46,23 @@ pub fn cbrt(input: d128) -> d128 {
n n
} }
fn pi() -> d128 {
d128!(3.14159265358979323846264338327950288)
}
fn pi2() -> d128 {
d128!(6.283185307179586476925286766559005768)
}
fn pi_half() -> d128 {
d128!(1.570796326794896619231321691639751)
}
fn eulers_number() -> d128 {
d128!(2.718281828459045235360287471352662)
}
fn rounding_base() -> d128 {
// DO NOT add trailing zeroes
d128!(0.0000000000000000000000000000001)
}
/// Returns the sine of a [`struct@d128`] /// Returns the sine of a [`struct@d128`]
pub fn sin(mut input: d128) -> d128 { pub fn sin(input: D128) -> D128 {
input %= pi2(); let result =input.sin();
match result.is_zero() {
let negative_correction = if input.is_negative() { true => D128::ZERO,
input -= pi(); false => result,
d128!(-1)
} else {
d128!(1)
};
let one = d128!(1);
let two = d128!(2);
let neg_one = -one;
let precision = 37;
let mut result = d128!(0);
for i_int in 0..precision {
let i = d128::from(i_int);
let calc_result = two * i + one;
result += neg_one.pow(i) * (input.pow(calc_result) / factorial(calc_result));
} }
let unrounded_result = negative_correction * result;
// This uses bankers rounding
unrounded_result.quantize(rounding_base())
} }
/// Returns the cosine of a [`struct@d128`] /// Returns the cosine of a [`struct@d128`]
pub fn cos(input: d128) -> d128 { pub fn cos(input: D128) -> D128 {
sin(pi_half() - input) input.cos()
} }
/// Returns the tangent of a [`struct@d128`] /// Returns the tangent of a [`struct@d128`]
pub fn tan(input: d128) -> d128 { pub fn tan(input: D128) -> D128 {
sin(input) / cos(input) input.tan()
} }
/// Evaluate an [`AstNode`] into a [`Number`] /// Evaluate an [`AstNode`] into a [`Number`]
@ -110,11 +73,11 @@ fn evaluate_node(ast_node: &AstNode) -> Result<Number, String> {
Token::Number(number) => Ok(Number::new(*number, Unit::NoUnit)), Token::Number(number) => Ok(Number::new(*number, Unit::NoUnit)),
Token::Constant(constant) => match constant { Token::Constant(constant) => match constant {
Pi => Ok(Number::new( Pi => Ok(Number::new(
pi(), D128::PI,
Unit::NoUnit, Unit::NoUnit,
)), )),
E => Ok(Number::new( E => Ok(Number::new(
eulers_number(), D128::E,
Unit::NoUnit, Unit::NoUnit,
)), )),
}, },
@ -148,8 +111,7 @@ fn evaluate_node(ast_node: &AstNode) -> Result<Number, String> {
} }
Ln => { Ln => {
if child_answer.unit.category() == UnitType::NoType { if child_answer.unit.category() == UnitType::NoType {
let unrounded_result = child_answer.value.ln(); let result = child_answer.value.ln();
let result = unrounded_result.quantize(unrounded_result * d128!(10));
Ok(Number::new(result, child_answer.unit)) Ok(Number::new(result, child_answer.unit))
} else { } else {
Err("ln() only accepts UnitType::NoType".to_string()) Err("ln() only accepts UnitType::NoType".to_string())
@ -157,7 +119,7 @@ fn evaluate_node(ast_node: &AstNode) -> Result<Number, String> {
} }
Exp => { Exp => {
if child_answer.unit.category() == UnitType::NoType { if child_answer.unit.category() == UnitType::NoType {
let result = child_answer.value.exp(child_answer.value); let result = child_answer.value.exp();
Ok(Number::new(result, child_answer.unit)) Ok(Number::new(result, child_answer.unit))
} else { } else {
Err("exp() only accepts UnitType::NoType".to_string()) Err("exp() only accepts UnitType::NoType".to_string())
@ -165,35 +127,35 @@ fn evaluate_node(ast_node: &AstNode) -> Result<Number, String> {
} }
Round => { Round => {
// .quantize() rounds .5 to nearest even integer, so we correct that // .quantize() rounds .5 to nearest even integer, so we correct that
let mut result = child_answer.value.quantize(d128!(1)); let mut result = child_answer.value.quantize(d!(1));
let rounding_change = result - child_answer.value; let rounding_change = result - child_answer.value;
// If the result was rounded down by 0.5, correct by +1 // If the result was rounded down by 0.5, correct by +1
if rounding_change == d128!(-0.5) { if rounding_change == d!(-0.5) {
result += d128!(1); result += d!(1);
} }
Ok(Number::new(result, child_answer.unit)) Ok(Number::new(result, child_answer.unit))
} }
Ceil => { Ceil => {
let mut result = child_answer.value.quantize(d128!(1)); let mut result = child_answer.value.quantize(d!(1));
let rounding_change = result - child_answer.value; let rounding_change = result - child_answer.value;
if rounding_change.is_negative() { if rounding_change.is_negative() {
result += d128!(1); result += d!(1);
} }
Ok(Number::new(result, child_answer.unit)) Ok(Number::new(result, child_answer.unit))
} }
Floor => { Floor => {
let mut result = child_answer.value.quantize(d128!(1)); let mut result = child_answer.value.quantize(d!(1));
let rounding_change = result - child_answer.value; let rounding_change = result - child_answer.value;
if !rounding_change.is_negative() { if !rounding_change.is_negative() {
result -= d128!(1); result -= d!(1);
} }
Ok(Number::new(result, child_answer.unit)) Ok(Number::new(result, child_answer.unit))
} }
Abs => { Abs => {
let mut result = child_answer.value.abs(); let mut result = child_answer.value.abs();
let rounding_change = result - child_answer.value; let rounding_change = result - child_answer.value;
if rounding_change == d128!(-0.5) { if rounding_change == d!(-0.5) {
result += d128!(1); result += d!(1);
} }
Ok(Number::new(result, child_answer.unit)) Ok(Number::new(result, child_answer.unit))
} }
@ -232,7 +194,7 @@ fn evaluate_node(ast_node: &AstNode) -> Result<Number, String> {
let child_answer = evaluate_node(child_node)?; let child_answer = evaluate_node(child_node)?;
match operator { match operator {
Percent => Ok(Number::new( Percent => Ok(Number::new(
child_answer.value / d128!(100), child_answer.value / d!(100),
child_answer.unit, child_answer.unit,
)), )),
Factorial => { Factorial => {
@ -326,7 +288,7 @@ mod tests {
let result = eval(input, true, false).unwrap(); let result = eval(input, true, false).unwrap();
assert_eq!(result.unit, Unit::NoUnit); assert_eq!(result.unit, Unit::NoUnit);
result.get_simplified_value().to_string() result.to_string()
} }
#[test] #[test]
@ -334,7 +296,7 @@ mod tests {
assert_eq!(eval_num("-2(-3)"), "6"); assert_eq!(eval_num("-2(-3)"), "6");
assert_eq!(eval_num("-2(3)"), "-6"); assert_eq!(eval_num("-2(3)"), "-6");
assert_eq!(eval_num("(3)-2"), "1"); assert_eq!(eval_num("(3)-2"), "1");
assert_eq!(eval_default("-1km to m"), Number::new(d128!(-1000), Unit::Meter)); assert_eq!(eval_default("-1km to m"), Number::new(d!(-1000), Unit::Meter));
assert_eq!(eval_num("2*-3*0.5"), "-3"); assert_eq!(eval_num("2*-3*0.5"), "-3");
assert_eq!(eval_num("-3^2"), "-9"); assert_eq!(eval_num("-3^2"), "-9");
assert_eq!(eval_num("-1+2"), "1"); assert_eq!(eval_num("-1+2"), "1");
@ -347,13 +309,15 @@ mod tests {
assert_eq!(eval_num("sqrt(25)"), "5"); assert_eq!(eval_num("sqrt(25)"), "5");
assert_eq!(eval_num("log(100)"), "2"); assert_eq!(eval_num("log(100)"), "2");
assert_eq!(eval_num("log(2)"), "0.301029995663981195213738894724493"); assert_eq!(eval_num("log(2)"), "0.301029995663981195213738894724493026768");
assert_eq!(eval_num("ln(1)"), "0"); assert_eq!(eval_num("ln(1)"), "0");
assert_eq!(eval_num("ln(2)"), "0.693147180559945309417232121458177"); assert_eq!(eval_num("ln(2)"), "0.69314718055994530941723212145817656808");
assert_eq!(eval_num("ln(e)"), "1"); assert_eq!(eval_num("ln(e)"), "1");
assert_eq!(eval_num("ln(e^2)"), "2"); assert_eq!(eval_num("ln(e^2)"), "2");
assert_eq!(eval_num("exp(1)"), "2.71828182845904523536028747135266249776");
assert_eq!(eval_num("round(1.4)"), "1"); assert_eq!(eval_num("round(1.4)"), "1");
assert_eq!(eval_num("round(1.6)"), "2"); assert_eq!(eval_num("round(1.6)"), "2");
assert_eq!(eval_num("round(1.5)"), "2"); assert_eq!(eval_num("round(1.5)"), "2");
@ -367,7 +331,7 @@ mod tests {
assert_eq!(eval_num("abs(-3)"), "3"); assert_eq!(eval_num("abs(-3)"), "3");
assert_eq!(eval_num("sin(2)"), "0.9092974268256816953960198659117"); assert_eq!(eval_num("sin(2)"), "0.9092974268256816953960198659117448427");
assert_eq!(eval_num("sin(-2)"), "-0.9092974268256816953960198659117"); assert_eq!(eval_num("sin(-2)"), "-0.9092974268256816953960198659117448427");
} }
} }

View File

@ -1,6 +1,6 @@
use std::iter::Peekable; use std::iter::Peekable;
use std::str::FromStr; use fastnum::decimal::Context;
use decimal::d128; use fastnum::D128;
use crate::Token; use crate::Token;
use crate::Operator::{Caret, Divide, LeftParen, Minus, Modulo, Multiply, Plus, RightParen}; use crate::Operator::{Caret, Divide, LeftParen, Minus, Modulo, Multiply, Plus, RightParen};
use crate::UnaryOperator::{Percent, Factorial}; use crate::UnaryOperator::{Percent, Factorial};
@ -98,14 +98,9 @@ fn parse_token(c: &str, lexer: &mut Lexer) -> Result<(), String> {
break; break;
} }
} }
d128::set_status(decimal::Status::empty()); match D128::from_str(&number_string, Context::default()) {
match d128::from_str(&number_string) {
Ok(number) => { Ok(number) => {
if d128::get_status().is_empty() { tokens.push(Token::Number(number));
tokens.push(Token::Number(number));
} else {
return Err(format!("Error lexing d128 number: {}", number_string));
}
}, },
Err(_e) => { Err(_e) => {
return Err(format!("Error lexing d128 number: {}", number_string)); return Err(format!("Error lexing d128 number: {}", number_string));

View File

@ -23,7 +23,7 @@
//! ``` //! ```
use crate::units::Unit; use crate::units::Unit;
use decimal::d128; use fastnum::{dec128 as d, D128};
use std::fmt::{self, Display}; use std::fmt::{self, Display};
use std::time::Instant; use std::time::Instant;
@ -47,37 +47,32 @@ pub mod units;
/// ```rust /// ```rust
/// use cpc::{eval,Number}; /// use cpc::{eval,Number};
/// use cpc::units::Unit; /// use cpc::units::Unit;
/// use decimal::d128; /// use fastnum::dec128;
/// ///
/// let x = Number { /// let x = Number {
/// value: d128!(100), /// value: dec128!(100),
/// unit: Unit::Meter, /// unit: Unit::Meter,
/// }; /// };
/// ``` /// ```
pub struct Number { pub struct Number {
/// The number part of a [`Number`] struct /// The number part of a [`Number`] struct
pub value: d128, pub value: D128,
/// The unit of a [`Number`] struct. This can be [`NoType`](units::UnitType::NoType) /// The unit of a [`Number`] struct. This can be [`NoType`](units::UnitType::NoType)
pub unit: Unit, pub unit: Unit,
} }
impl Number { impl Number {
pub const fn new(value: d128, unit: Unit) -> Number { pub const fn new(value: D128, unit: Unit) -> Number {
Number { value, unit } Number { value, unit }
} }
pub fn get_simplified_value(&self) -> d128 { pub fn get_simplified_value(&self) -> D128 {
// The order here matters, reduce must be first self.value.reduce()
// sin(pi) results in 0E-32, but .reduce() changes is to 0
let mut value = self.value.reduce();
// 0.2/0.01 results in 2E+1, but adding 0 changes it to 20
value = value + d128!(0);
value
} }
} }
impl Display for Number { impl Display for Number {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let value = self.get_simplified_value(); let value = self.get_simplified_value();
let word = match self.value == d128!(1) { let word = match self.value == d!(1) {
true => self.unit.singular(), true => self.unit.singular(),
false => self.unit.plural(), false => self.unit.plural(),
}; };
@ -199,7 +194,7 @@ pub enum LexerKeyword {
pub enum Token { pub enum Token {
Operator(Operator), Operator(Operator),
UnaryOperator(UnaryOperator), UnaryOperator(UnaryOperator),
Number(d128), Number(D128),
FunctionIdentifier(FunctionIdentifier), FunctionIdentifier(FunctionIdentifier),
Constant(Constant), Constant(Constant),
/// Used by the parser only /// Used by the parser only
@ -218,7 +213,7 @@ pub enum Token {
#[macro_export] #[macro_export]
macro_rules! numtok { macro_rules! numtok {
( $num:literal ) => { ( $num:literal ) => {
Token::Number(d128!($num)) Token::Number(fastnum::dec128!($num))
}; };
} }
@ -305,12 +300,12 @@ mod tests {
#[test] #[test]
fn test_evaluations() { fn test_evaluations() {
assert_eq!(default_eval("-2(-3)"), Number::new(d128!(6), Unit::NoUnit)); assert_eq!(default_eval("-2(-3)"), Number::new(d!(6), Unit::NoUnit));
assert_eq!(default_eval("-2(3)"), Number::new(d128!(-6), Unit::NoUnit)); assert_eq!(default_eval("-2(3)"), Number::new(d!(-6), Unit::NoUnit));
assert_eq!(default_eval("(3)-2"), Number::new(d128!(1), Unit::NoUnit)); assert_eq!(default_eval("(3)-2"), Number::new(d!(1), Unit::NoUnit));
assert_eq!(default_eval("-1km to m"), Number::new(d128!(-1000), Unit::Meter)); assert_eq!(default_eval("-1km to m"), Number::new(d!(-1000), Unit::Meter));
assert_eq!(default_eval("2*-3*0.5"), Number::new(d128!(-3), Unit::NoUnit)); assert_eq!(default_eval("2*-3*0.5"), Number::new(d!(-3), Unit::NoUnit));
assert_eq!(default_eval("-3^2"), Number::new(d128!(-9), Unit::NoUnit)); assert_eq!(default_eval("-3^2"), Number::new(d!(-9), Unit::NoUnit));
assert_eq!(default_eval("-1+2"), Number::new(d128!(1), Unit::NoUnit)); assert_eq!(default_eval("-1+2"), Number::new(d!(1), Unit::NoUnit));
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
use decimal::d128; use fastnum::{dec128 as d, D128, D256};
use crate::Number; use crate::Number;
#[derive(Clone, Copy, PartialEq, Debug)] #[derive(Clone, Copy, PartialEq, Debug)]
@ -62,7 +62,7 @@ macro_rules! create_units {
),* ),*
} }
} }
pub fn weight(&self) -> d128 { pub fn weight(&self) -> D128 {
match self { match self {
$( $(
Unit::$variant => $properties.1 Unit::$variant => $properties.1
@ -88,229 +88,229 @@ macro_rules! create_units {
} }
create_units!( create_units!(
NoUnit: (NoType, d128!(1), "", ""), NoUnit: (NoType, d!(1), "", ""),
Nanosecond: (Time, d128!(1), "nanosecond", "nanoseconds"), Nanosecond: (Time, d!(1), "nanosecond", "nanoseconds"),
Microsecond: (Time, d128!(1000), "microsecond", "microseconds"), Microsecond: (Time, d!(1000), "microsecond", "microseconds"),
Millisecond: (Time, d128!(1000000), "millisecond", "milliseconds"), Millisecond: (Time, d!(1000000), "millisecond", "milliseconds"),
Second: (Time, d128!(1000000000), "second", "seconds"), Second: (Time, d!(1000000000), "second", "seconds"),
Minute: (Time, d128!(60000000000), "minute", "minutes"), Minute: (Time, d!(60000000000), "minute", "minutes"),
Hour: (Time, d128!(3600000000000), "hour", "hours"), Hour: (Time, d!(3600000000000), "hour", "hours"),
Day: (Time, d128!(86400000000000), "day", "days"), Day: (Time, d!(86400000000000), "day", "days"),
Week: (Time, d128!(604800000000000), "week", "weeks"), Week: (Time, d!(604800000000000), "week", "weeks"),
Month: (Time, d128!(2629746000000000), "month", "months"), Month: (Time, d!(2629746000000000), "month", "months"),
Quarter: (Time, d128!(7889238000000000), "quarter", "quarters"), Quarter: (Time, d!(7889238000000000), "quarter", "quarters"),
Year: (Time, d128!(31556952000000000), "year", "years"), Year: (Time, d!(31556952000000000), "year", "years"),
Decade: (Time, d128!(315569520000000000), "decade", "decades"), Decade: (Time, d!(315569520000000000), "decade", "decades"),
Century: (Time, d128!(3155695200000000000), "century", "centuries"), Century: (Time, d!(3155695200000000000), "century", "centuries"),
Millenium: (Time, d128!(31556952000000000000), "millenium", "millenia"), Millenium: (Time, d!(31556952000000000000), "millenium", "millenia"),
Millimeter: (Length, d128!(1), "millimeter", "millimeters"), Millimeter: (Length, d!(1), "millimeter", "millimeters"),
Centimeter: (Length, d128!(10), "centimeter", "centimeters"), Centimeter: (Length, d!(10), "centimeter", "centimeters"),
Decimeter: (Length, d128!(100), "decimeter", "decimeters"), Decimeter: (Length, d!(100), "decimeter", "decimeters"),
Meter: (Length, d128!(1000), "meter", "meters"), Meter: (Length, d!(1000), "meter", "meters"),
Kilometer: (Length, d128!(1000000), "kilometer", "kilometers"), Kilometer: (Length, d!(1000000), "kilometer", "kilometers"),
Inch: (Length, d128!(25.4), "inch", "inches"), Inch: (Length, d!(25.4), "inch", "inches"),
Foot: (Length, d128!(304.8), "foot", "feet"), Foot: (Length, d!(304.8), "foot", "feet"),
Yard: (Length, d128!(914.4), "yard", "yards"), Yard: (Length, d!(914.4), "yard", "yards"),
Mile: (Length, d128!(1609344), "mile", "miles"), Mile: (Length, d!(1609344), "mile", "miles"),
// 1-dimensional only: // 1-dimensional only:
Marathon: (Length, d128!(42195000), "marathon", "marathons"), Marathon: (Length, d!(42195000), "marathon", "marathons"),
NauticalMile: (Length, d128!(1852000), "nautical mile", "nautical miles"), NauticalMile: (Length, d!(1852000), "nautical mile", "nautical miles"),
LightYear: (Length, d128!(9460730472580800000), "light year", "light years"), LightYear: (Length, d!(9460730472580800000), "light year", "light years"),
LightSecond: (Length, d128!(299792458000), "light second", "light seconds"), LightSecond: (Length, d!(299792458000), "light second", "light seconds"),
SquareMillimeter: (Area, d128!(1), "square millimeter", "square millimeters"), SquareMillimeter: (Area, d!(1), "square millimeter", "square millimeters"),
SquareCentimeter: (Area, d128!(100), "square centimeter", "square centimeters"), SquareCentimeter: (Area, d!(100), "square centimeter", "square centimeters"),
SquareDecimeter: (Area, d128!(10000), "square decimeter", "square decimeters"), SquareDecimeter: (Area, d!(10000), "square decimeter", "square decimeters"),
SquareMeter: (Area, d128!(1000000), "square meter", "square meters"), SquareMeter: (Area, d!(1000000), "square meter", "square meters"),
SquareKilometer: (Area, d128!(1000000000000), "square kilometer", "square kilometers"), SquareKilometer: (Area, d!(1000000000000), "square kilometer", "square kilometers"),
SquareInch: (Area, d128!(645.16), "square inch", "square inches"), SquareInch: (Area, d!(645.16), "square inch", "square inches"),
SquareFoot: (Area, d128!(92903.04), "square foot", "square feet"), SquareFoot: (Area, d!(92903.04), "square foot", "square feet"),
SquareYard: (Area, d128!(836127.36), "square yard", "square yards"), SquareYard: (Area, d!(836127.36), "square yard", "square yards"),
SquareMile: (Area, d128!(2589988110336.00), "square mile", "square miles"), SquareMile: (Area, d!(2589988110336.00), "square mile", "square miles"),
// 2-dimensional only // 2-dimensional only
Are: (Area, d128!(100000000), "are", "ares"), Are: (Area, d!(100000000), "are", "ares"),
Decare: (Area, d128!(1000000000), "decare", "decare"), Decare: (Area, d!(1000000000), "decare", "decare"),
Hectare: (Area, d128!(10000000000), "hectare", "hectares"), Hectare: (Area, d!(10000000000), "hectare", "hectares"),
Acre: (Area, d128!(4046856422.40), "acre", "acres"), Acre: (Area, d!(4046856422.40), "acre", "acres"),
CubicMillimeter: (Volume, d128!(1), "cubic millimeter", "cubic millimeters"), CubicMillimeter: (Volume, d!(1), "cubic millimeter", "cubic millimeters"),
CubicCentimeter: (Volume, d128!(1000), "cubic centimeter", "cubic centimeters"), CubicCentimeter: (Volume, d!(1000), "cubic centimeter", "cubic centimeters"),
CubicDecimeter: (Volume, d128!(1000000), "cubic decimeter", "cubic decimeters"), CubicDecimeter: (Volume, d!(1000000), "cubic decimeter", "cubic decimeters"),
CubicMeter: (Volume, d128!(1000000000), "cubic meter", "cubic meters"), CubicMeter: (Volume, d!(1000000000), "cubic meter", "cubic meters"),
CubicKilometer: (Volume, d128!(1000000000000000000), "cubic kilometer", "cubic kilometers"), CubicKilometer: (Volume, d!(1000000000000000000), "cubic kilometer", "cubic kilometers"),
CubicInch: (Volume, d128!(16387.064), "cubic inch", "cubic inches"), CubicInch: (Volume, d!(16387.064), "cubic inch", "cubic inches"),
CubicFoot: (Volume, d128!(28316846.592), "cubic foot", "cubic feet"), CubicFoot: (Volume, d!(28316846.592), "cubic foot", "cubic feet"),
CubicYard: (Volume, d128!(764554857.984), "cubic yard", "cubic yards"), CubicYard: (Volume, d!(764554857.984), "cubic yard", "cubic yards"),
CubicMile: (Volume, d128!(4168181825440579584), "cubic mile", "cubic miles"), CubicMile: (Volume, d!(4168181825440579584), "cubic mile", "cubic miles"),
// 3-dimensional only // 3-dimensional only
Milliliter: (Volume, d128!(1000), "milliliter", "milliliters"), Milliliter: (Volume, d!(1000), "milliliter", "milliliters"),
Centiliter: (Volume, d128!(10000), "centiliter", "centiliters"), Centiliter: (Volume, d!(10000), "centiliter", "centiliters"),
Deciliter: (Volume, d128!(100000), "deciliter", "deciliters"), Deciliter: (Volume, d!(100000), "deciliter", "deciliters"),
Liter: (Volume, d128!(1000000), "liter", "liters"), Liter: (Volume, d!(1000000), "liter", "liters"),
Teaspoon: (Volume, d128!(4928.92159375), "teaspoon", "teaspoons"), Teaspoon: (Volume, d!(4928.92159375), "teaspoon", "teaspoons"),
Tablespoon: (Volume, d128!(14786.76478125), "tablespoon", "tablespoons"), Tablespoon: (Volume, d!(14786.76478125), "tablespoon", "tablespoons"),
FluidOunce: (Volume, d128!(29573.5295625), "fluid ounce", "fluid ounces"), FluidOunce: (Volume, d!(29573.5295625), "fluid ounce", "fluid ounces"),
Cup: (Volume, d128!(236588.2365), "cup", "cups"), Cup: (Volume, d!(236588.2365), "cup", "cups"),
Pint: (Volume, d128!(473176.473), "pint", "pints"), Pint: (Volume, d!(473176.473), "pint", "pints"),
Quart: (Volume, d128!(946352.946), "quart", "quarts"), Quart: (Volume, d!(946352.946), "quart", "quarts"),
Gallon: (Volume, d128!(3785411.784), "gallon", "gallons"), Gallon: (Volume, d!(3785411.784), "gallon", "gallons"),
OilBarrel: (Volume, d128!(158987294.928), "oil barrel", "oil barrels"), OilBarrel: (Volume, d!(158987294.928), "oil barrel", "oil barrels"),
Milligram: (Mass, d128!(0.001), "milligram", "milligrams"), Milligram: (Mass, d!(0.001), "milligram", "milligrams"),
Gram: (Mass, d128!(1), "gram", "grams"), Gram: (Mass, d!(1), "gram", "grams"),
Hectogram: (Mass, d128!(100), "hectogram", "hectograms"), Hectogram: (Mass, d!(100), "hectogram", "hectograms"),
Kilogram: (Mass, d128!(1000), "kilogram", "kilograms"), Kilogram: (Mass, d!(1000), "kilogram", "kilograms"),
MetricTon: (Mass, d128!(1000000), "metric ton", "metric tons"), MetricTon: (Mass, d!(1000000), "metric ton", "metric tons"),
Ounce: (Mass, d128!(28.349523125), "ounce", "ounces"), Ounce: (Mass, d!(28.349523125), "ounce", "ounces"),
Pound: (Mass, d128!(453.59237), "pound", "pounds"), Pound: (Mass, d!(453.59237), "pound", "pounds"),
Stone: (Mass, d128!(6350.29318), "stone", "stones"), Stone: (Mass, d!(6350.29318), "stone", "stones"),
ShortTon: (Mass, d128!(907184.74), "short ton", "short tons"), ShortTon: (Mass, d!(907184.74), "short ton", "short tons"),
LongTon: (Mass, d128!(1016046.9088), "long ton", "long tons"), LongTon: (Mass, d!(1016046.9088), "long ton", "long tons"),
Bit: (DigitalStorage, d128!(1), "bit", "bits"), Bit: (DigitalStorage, d!(1), "bit", "bits"),
Kilobit: (DigitalStorage, d128!(1000), "kilobit", "kilobits"), Kilobit: (DigitalStorage, d!(1000), "kilobit", "kilobits"),
Megabit: (DigitalStorage, d128!(1000000), "megabit", "megabits"), Megabit: (DigitalStorage, d!(1000000), "megabit", "megabits"),
Gigabit: (DigitalStorage, d128!(1000000000), "gigabit", "gigabits"), Gigabit: (DigitalStorage, d!(1000000000), "gigabit", "gigabits"),
Terabit: (DigitalStorage, d128!(1000000000000), "terabit", "terabits"), Terabit: (DigitalStorage, d!(1000000000000), "terabit", "terabits"),
Petabit: (DigitalStorage, d128!(1000000000000000), "petabit", "petabits"), Petabit: (DigitalStorage, d!(1000000000000000), "petabit", "petabits"),
Exabit: (DigitalStorage, d128!(1000000000000000000), "exabit", "exabits"), Exabit: (DigitalStorage, d!(1000000000000000000), "exabit", "exabits"),
Zettabit: (DigitalStorage, d128!(1000000000000000000000), "zettabit", "zettabits"), Zettabit: (DigitalStorage, d!(1000000000000000000000), "zettabit", "zettabits"),
Yottabit: (DigitalStorage, d128!(1000000000000000000000000), "yottabit", "yottabits"), Yottabit: (DigitalStorage, d!(1000000000000000000000000), "yottabit", "yottabits"),
Kibibit: (DigitalStorage, d128!(1024), "kibibit", "kibibits"), Kibibit: (DigitalStorage, d!(1024), "kibibit", "kibibits"),
Mebibit: (DigitalStorage, d128!(1048576), "mebibit", "mebibits"), Mebibit: (DigitalStorage, d!(1048576), "mebibit", "mebibits"),
Gibibit: (DigitalStorage, d128!(1073741824), "gibibit", "gibibits"), Gibibit: (DigitalStorage, d!(1073741824), "gibibit", "gibibits"),
Tebibit: (DigitalStorage, d128!(1099511627776), "tebibit", "tebibits"), Tebibit: (DigitalStorage, d!(1099511627776), "tebibit", "tebibits"),
Pebibit: (DigitalStorage, d128!(1125899906842624), "pebibit", "pebibits"), Pebibit: (DigitalStorage, d!(1125899906842624), "pebibit", "pebibits"),
Exbibit: (DigitalStorage, d128!(1152921504606846976), "exbibit", "exbibits"), Exbibit: (DigitalStorage, d!(1152921504606846976), "exbibit", "exbibits"),
Zebibit: (DigitalStorage, d128!(1180591620717411303424), "zebibit", "zebibits"), Zebibit: (DigitalStorage, d!(1180591620717411303424), "zebibit", "zebibits"),
Yobibit: (DigitalStorage, d128!(1208925819614629174706176), "yobibit", "yobibits"), Yobibit: (DigitalStorage, d!(1208925819614629174706176), "yobibit", "yobibits"),
Byte: (DigitalStorage, d128!(8), "byte", "bytes"), Byte: (DigitalStorage, d!(8), "byte", "bytes"),
Kilobyte: (DigitalStorage, d128!(8000), "kilobyte", "kilobytes"), Kilobyte: (DigitalStorage, d!(8000), "kilobyte", "kilobytes"),
Megabyte: (DigitalStorage, d128!(8000000), "megabyte", "megabytes"), Megabyte: (DigitalStorage, d!(8000000), "megabyte", "megabytes"),
Gigabyte: (DigitalStorage, d128!(8000000000), "gigabyte", "gigabytes"), Gigabyte: (DigitalStorage, d!(8000000000), "gigabyte", "gigabytes"),
Terabyte: (DigitalStorage, d128!(8000000000000), "terabyte", "terabytes"), Terabyte: (DigitalStorage, d!(8000000000000), "terabyte", "terabytes"),
Petabyte: (DigitalStorage, d128!(8000000000000000), "petabyte", "petabytes"), Petabyte: (DigitalStorage, d!(8000000000000000), "petabyte", "petabytes"),
Exabyte: (DigitalStorage, d128!(8000000000000000000), "exabyte", "exabytes"), Exabyte: (DigitalStorage, d!(8000000000000000000), "exabyte", "exabytes"),
Zettabyte: (DigitalStorage, d128!(8000000000000000000000), "zettabyte", "zettabytes"), Zettabyte: (DigitalStorage, d!(8000000000000000000000), "zettabyte", "zettabytes"),
Yottabyte: (DigitalStorage, d128!(8000000000000000000000000), "yottabyte", "yottabytes"), Yottabyte: (DigitalStorage, d!(8000000000000000000000000), "yottabyte", "yottabytes"),
Kibibyte: (DigitalStorage, d128!(8192), "kibibyte", "kibibytes"), Kibibyte: (DigitalStorage, d!(8192), "kibibyte", "kibibytes"),
Mebibyte: (DigitalStorage, d128!(8388608), "mebibyte", "mebibytes"), Mebibyte: (DigitalStorage, d!(8388608), "mebibyte", "mebibytes"),
Gibibyte: (DigitalStorage, d128!(8589934592), "gibibyte", "gibibytes"), Gibibyte: (DigitalStorage, d!(8589934592), "gibibyte", "gibibytes"),
Tebibyte: (DigitalStorage, d128!(8796093022208), "tebibyte", "tebibytes"), Tebibyte: (DigitalStorage, d!(8796093022208), "tebibyte", "tebibytes"),
Pebibyte: (DigitalStorage, d128!(9007199254740992), "pebibyte", "pebibytes"), Pebibyte: (DigitalStorage, d!(9007199254740992), "pebibyte", "pebibytes"),
Exbibyte: (DigitalStorage, d128!(9223372036854775808), "exbibyte", "exbibytes"), Exbibyte: (DigitalStorage, d!(9223372036854775808), "exbibyte", "exbibytes"),
Zebibyte: (DigitalStorage, d128!(9444732965739290427392), "zebibyte", "zebibytes"), Zebibyte: (DigitalStorage, d!(9444732965739290427392), "zebibyte", "zebibytes"),
Yobibyte: (DigitalStorage, d128!(9671406556917033397649408), "yobibyte", "yobibytes"), Yobibyte: (DigitalStorage, d!(9671406556917033397649408), "yobibyte", "yobibytes"),
BitsPerSecond: (DataTransferRate, d128!(1), "bit per second", "bits per second"), BitsPerSecond: (DataTransferRate, d!(1), "bit per second", "bits per second"),
KilobitsPerSecond: (DataTransferRate, d128!(1000), "kilobit per second", "kilobits per second"), KilobitsPerSecond: (DataTransferRate, d!(1000), "kilobit per second", "kilobits per second"),
MegabitsPerSecond: (DataTransferRate, d128!(1000000), "megabit per second", "megabits per second"), MegabitsPerSecond: (DataTransferRate, d!(1000000), "megabit per second", "megabits per second"),
GigabitsPerSecond: (DataTransferRate, d128!(1000000000), "gigabit per second", "gigabits per second"), GigabitsPerSecond: (DataTransferRate, d!(1000000000), "gigabit per second", "gigabits per second"),
TerabitsPerSecond: (DataTransferRate, d128!(1000000000000), "terabit per second", "terabits per second"), TerabitsPerSecond: (DataTransferRate, d!(1000000000000), "terabit per second", "terabits per second"),
PetabitsPerSecond: (DataTransferRate, d128!(1000000000000000), "petabit per second", "petabits per second"), PetabitsPerSecond: (DataTransferRate, d!(1000000000000000), "petabit per second", "petabits per second"),
ExabitsPerSecond: (DataTransferRate, d128!(1000000000000000000), "exabit per second", "exabits per second"), ExabitsPerSecond: (DataTransferRate, d!(1000000000000000000), "exabit per second", "exabits per second"),
ZettabitsPerSecond: (DataTransferRate, d128!(1000000000000000000000), "zettabit per second", "zettabits per second"), ZettabitsPerSecond: (DataTransferRate, d!(1000000000000000000000), "zettabit per second", "zettabits per second"),
YottabitsPerSecond: (DataTransferRate, d128!(1000000000000000000000000), "yottabit per second", "yottabits per second"), YottabitsPerSecond: (DataTransferRate, d!(1000000000000000000000000), "yottabit per second", "yottabits per second"),
KibibitsPerSecond: (DataTransferRate, d128!(1024), "kibibit per second", "kibibits per second"), KibibitsPerSecond: (DataTransferRate, d!(1024), "kibibit per second", "kibibits per second"),
MebibitsPerSecond: (DataTransferRate, d128!(1048576), "mebibit per second", "mebibits per second"), MebibitsPerSecond: (DataTransferRate, d!(1048576), "mebibit per second", "mebibits per second"),
GibibitsPerSecond: (DataTransferRate, d128!(1073741824), "gibibit per second", "gibibits per second"), GibibitsPerSecond: (DataTransferRate, d!(1073741824), "gibibit per second", "gibibits per second"),
TebibitsPerSecond: (DataTransferRate, d128!(1099511627776), "tebibit per second", "tebibits per second"), TebibitsPerSecond: (DataTransferRate, d!(1099511627776), "tebibit per second", "tebibits per second"),
PebibitsPerSecond: (DataTransferRate, d128!(1125899906842624), "pebibit per second", "pebibits per second"), PebibitsPerSecond: (DataTransferRate, d!(1125899906842624), "pebibit per second", "pebibits per second"),
ExbibitsPerSecond: (DataTransferRate, d128!(1152921504606846976), "exbibit per second", "exbibits per second"), ExbibitsPerSecond: (DataTransferRate, d!(1152921504606846976), "exbibit per second", "exbibits per second"),
ZebibitsPerSecond: (DataTransferRate, d128!(1180591620717411303424), "zebibit per second", "zebibits per second"), ZebibitsPerSecond: (DataTransferRate, d!(1180591620717411303424), "zebibit per second", "zebibits per second"),
YobibitsPerSecond: (DataTransferRate, d128!(1208925819614629174706176), "yobibit per second", "yobibits per second"), YobibitsPerSecond: (DataTransferRate, d!(1208925819614629174706176), "yobibit per second", "yobibits per second"),
BytesPerSecond: (DataTransferRate, d128!(8), "byte per second", "bytes per second"), BytesPerSecond: (DataTransferRate, d!(8), "byte per second", "bytes per second"),
KilobytesPerSecond: (DataTransferRate, d128!(8000), "kilobyte per second", "kilobytes per second"), KilobytesPerSecond: (DataTransferRate, d!(8000), "kilobyte per second", "kilobytes per second"),
MegabytesPerSecond: (DataTransferRate, d128!(8000000), "megabyte per second", "megabytes per second"), MegabytesPerSecond: (DataTransferRate, d!(8000000), "megabyte per second", "megabytes per second"),
GigabytesPerSecond: (DataTransferRate, d128!(8000000000), "gigabyte per second", "gigabytes per second"), GigabytesPerSecond: (DataTransferRate, d!(8000000000), "gigabyte per second", "gigabytes per second"),
TerabytesPerSecond: (DataTransferRate, d128!(8000000000000), "terabyte per second", "terabytes per second"), TerabytesPerSecond: (DataTransferRate, d!(8000000000000), "terabyte per second", "terabytes per second"),
PetabytesPerSecond: (DataTransferRate, d128!(8000000000000000), "petabyte per second", "petabytes per second"), PetabytesPerSecond: (DataTransferRate, d!(8000000000000000), "petabyte per second", "petabytes per second"),
ExabytesPerSecond: (DataTransferRate, d128!(8000000000000000000), "exabyte per second", "exabytes per second"), ExabytesPerSecond: (DataTransferRate, d!(8000000000000000000), "exabyte per second", "exabytes per second"),
ZettabytesPerSecond: (DataTransferRate, d128!(8000000000000000000000), "zettabyte per second", "zettabytes per second"), ZettabytesPerSecond: (DataTransferRate, d!(8000000000000000000000), "zettabyte per second", "zettabytes per second"),
YottabytesPerSecond: (DataTransferRate, d128!(8000000000000000000000000), "yottabyte per second", "yottabytes per second"), YottabytesPerSecond: (DataTransferRate, d!(8000000000000000000000000), "yottabyte per second", "yottabytes per second"),
KibibytesPerSecond: (DataTransferRate, d128!(8192), "kibibyte per second", "kibibytes per second"), KibibytesPerSecond: (DataTransferRate, d!(8192), "kibibyte per second", "kibibytes per second"),
MebibytesPerSecond: (DataTransferRate, d128!(8388608), "mebibyte per second", "mebibytes per second"), MebibytesPerSecond: (DataTransferRate, d!(8388608), "mebibyte per second", "mebibytes per second"),
GibibytesPerSecond: (DataTransferRate, d128!(8589934592), "gibibyte per second", "gibibytes per second"), GibibytesPerSecond: (DataTransferRate, d!(8589934592), "gibibyte per second", "gibibytes per second"),
TebibytesPerSecond: (DataTransferRate, d128!(8796093022208), "tebibyte per second", "tebibytes per second"), TebibytesPerSecond: (DataTransferRate, d!(8796093022208), "tebibyte per second", "tebibytes per second"),
PebibytesPerSecond: (DataTransferRate, d128!(9007199254740992), "pebibyte per second", "pebibytes per second"), PebibytesPerSecond: (DataTransferRate, d!(9007199254740992), "pebibyte per second", "pebibytes per second"),
ExbibytesPerSecond: (DataTransferRate, d128!(9223372036854775808), "exbibyte per second", "exbibytes per second"), ExbibytesPerSecond: (DataTransferRate, d!(9223372036854775808), "exbibyte per second", "exbibytes per second"),
ZebibytesPerSecond: (DataTransferRate, d128!(9444732965739290427392), "zebibyte per second", "zebibytes per second"), ZebibytesPerSecond: (DataTransferRate, d!(9444732965739290427392), "zebibyte per second", "zebibytes per second"),
YobibytesPerSecond: (DataTransferRate, d128!(9671406556917033397649408), "yobibyte per second", "yobibytes per second"), YobibytesPerSecond: (DataTransferRate, d!(9671406556917033397649408), "yobibyte per second", "yobibytes per second"),
Millijoule: (Energy, d128!(0.001), "millijoule", "millijoules"), Millijoule: (Energy, d!(0.001), "millijoule", "millijoules"),
Joule: (Energy, d128!(1), "joule", "joules"), Joule: (Energy, d!(1), "joule", "joules"),
NewtonMeter: (Energy, d128!(1), "newton meter", "newton meters"), NewtonMeter: (Energy, d!(1), "newton meter", "newton meters"),
Kilojoule: (Energy, d128!(1000), "kilojoule", "kilojoules"), Kilojoule: (Energy, d!(1000), "kilojoule", "kilojoules"),
Megajoule: (Energy, d128!(1000000), "megajoule", "megajoules"), Megajoule: (Energy, d!(1000000), "megajoule", "megajoules"),
Gigajoule: (Energy, d128!(1000000000), "gigajoule", "gigajoules"), Gigajoule: (Energy, d!(1000000000), "gigajoule", "gigajoules"),
Terajoule: (Energy, d128!(1000000000000), "terajoule", "terajoules"), Terajoule: (Energy, d!(1000000000000), "terajoule", "terajoules"),
Calorie: (Energy, d128!(4.1868), "calorie", "calories"), Calorie: (Energy, d!(4.1868), "calorie", "calories"),
KiloCalorie: (Energy, d128!(4186.8), "kilocalorie", "kilocalories"), KiloCalorie: (Energy, d!(4186.8), "kilocalorie", "kilocalories"),
BritishThermalUnit: (Energy, d128!(1055.05585262), "British thermal unit", "British thermal units"), BritishThermalUnit: (Energy, d!(1055.05585262), "British thermal unit", "British thermal units"),
WattHour: (Energy, d128!(3600), "watt-hour", "watt-hours"), WattHour: (Energy, d!(3600), "watt-hour", "watt-hours"),
KilowattHour: (Energy, d128!(3600000), "kilowatt-hour", "kilowatt-hours"), KilowattHour: (Energy, d!(3600000), "kilowatt-hour", "kilowatt-hours"),
MegawattHour: (Energy, d128!(3600000000), "megawatt-hour", "megawatt-hours"), MegawattHour: (Energy, d!(3600000000), "megawatt-hour", "megawatt-hours"),
GigawattHour: (Energy, d128!(3600000000000), "gigawatt-hour", "gigawatt-hours"), GigawattHour: (Energy, d!(3600000000000), "gigawatt-hour", "gigawatt-hours"),
TerawattHour: (Energy, d128!(3600000000000000), "terawatt-hour", "terawatt-hours"), TerawattHour: (Energy, d!(3600000000000000), "terawatt-hour", "terawatt-hours"),
PetawattHour: (Energy, d128!(3600000000000000000), "petawatt-hour", "petawatt-hours"), PetawattHour: (Energy, d!(3600000000000000000), "petawatt-hour", "petawatt-hours"),
Milliwatt: (Power, d128!(0.001), "milliwatt", "milliwatts"), Milliwatt: (Power, d!(0.001), "milliwatt", "milliwatts"),
Watt: (Power, d128!(1), "watt", "watts"), Watt: (Power, d!(1), "watt", "watts"),
Kilowatt: (Power, d128!(1000), "kilowatt", "kilowatts"), Kilowatt: (Power, d!(1000), "kilowatt", "kilowatts"),
Megawatt: (Power, d128!(1000000), "megawatt", "megawatts"), Megawatt: (Power, d!(1000000), "megawatt", "megawatts"),
Gigawatt: (Power, d128!(1000000000), "gigawatt", "gigawatts"), Gigawatt: (Power, d!(1000000000), "gigawatt", "gigawatts"),
Terawatt: (Power, d128!(1000000000000), "terawatt", "terawatts"), Terawatt: (Power, d!(1000000000000), "terawatt", "terawatts"),
Petawatt: (Power, d128!(1000000000000000), "petawatt", "petawatts"), Petawatt: (Power, d!(1000000000000000), "petawatt", "petawatts"),
// probably inexact: // probably inexact:
BritishThermalUnitsPerMinute: (Power, d128!(0.0568690272188), "british thermal unit per minute", "british thermal units per minute"), BritishThermalUnitsPerMinute: (Power, d!(0.0568690272188), "british thermal unit per minute", "british thermal units per minute"),
// probably inexact: // probably inexact:
BritishThermalUnitsPerHour: (Power, d128!(3.412141633128), "british thermal unit per hour", "british thermal units per hour"), BritishThermalUnitsPerHour: (Power, d!(3.412141633128), "british thermal unit per hour", "british thermal units per hour"),
// exact according to wikipedia: // exact according to wikipedia:
Horsepower: (Power, d128!(745.69987158227022), "horsepower", "horsepower"), Horsepower: (Power, d!(745.69987158227022), "horsepower", "horsepower"),
MetricHorsepower: (Power, d128!(735.49875), "metric horsepower", "metric horsepower"), MetricHorsepower: (Power, d!(735.49875), "metric horsepower", "metric horsepower"),
Milliampere: (ElectricCurrent, d128!(0.001), "milliampere", "milliamperes"), Milliampere: (ElectricCurrent, d!(0.001), "milliampere", "milliamperes"),
Ampere: (ElectricCurrent, d128!(1), "ampere", "amperes"), Ampere: (ElectricCurrent, d!(1), "ampere", "amperes"),
Kiloampere: (ElectricCurrent, d128!(1000), "kiloampere", "kiloamperes"), Kiloampere: (ElectricCurrent, d!(1000), "kiloampere", "kiloamperes"),
Abampere: (ElectricCurrent, d128!(10), "abampere", "abamperes"), Abampere: (ElectricCurrent, d!(10), "abampere", "abamperes"),
Milliohm: (Resistance, d128!(0.001), "milliohm", "milliohms"), Milliohm: (Resistance, d!(0.001), "milliohm", "milliohms"),
Ohm: (Resistance, d128!(1), "ohm", "ohms"), Ohm: (Resistance, d!(1), "ohm", "ohms"),
Kiloohm: (Resistance, d128!(1000), "kiloohm", "kiloohms"), Kiloohm: (Resistance, d!(1000), "kiloohm", "kiloohms"),
Millivolt: (Voltage, d128!(0.001), "millivolt", "millivolts"), Millivolt: (Voltage, d!(0.001), "millivolt", "millivolts"),
Volt: (Voltage, d128!(1), "volt", "volts"), Volt: (Voltage, d!(1), "volt", "volts"),
Kilovolt: (Voltage, d128!(1000), "kilovolt", "kilovolts"), Kilovolt: (Voltage, d!(1000), "kilovolt", "kilovolts"),
Pascal: (Pressure, d128!(1), "pascal", "pascals"), Pascal: (Pressure, d!(1), "pascal", "pascals"),
Kilopascal: (Pressure, d128!(1000), "kilopascal", "kilopascals"), Kilopascal: (Pressure, d!(1000), "kilopascal", "kilopascals"),
Atmosphere: (Pressure, d128!(101325), "atmosphere", "atmospheres"), Atmosphere: (Pressure, d!(101325), "atmosphere", "atmospheres"),
Millibar: (Pressure, d128!(100), "millibar", "millibars"), Millibar: (Pressure, d!(100), "millibar", "millibars"),
Bar: (Pressure, d128!(100000), "bar", "bars"), Bar: (Pressure, d!(100000), "bar", "bars"),
InchOfMercury: (Pressure, d128!(3386.389), "inch of mercury", "inches of mercury"), InchOfMercury: (Pressure, d!(3386.389), "inch of mercury", "inches of mercury"),
// inexact: // inexact:
PoundsPerSquareInch: (Pressure, d128!(6894.757293168361), "pound per square inch", "pounds per square inch"), PoundsPerSquareInch: (Pressure, d!(6894.757293168361), "pound per square inch", "pounds per square inch"),
Torr: (Pressure, d128!(162.12), "torr", "torr"), Torr: (Pressure, d!(162.12), "torr", "torr"),
Hertz: (Frequency, d128!(1), "hertz", "hertz"), Hertz: (Frequency, d!(1), "hertz", "hertz"),
Kilohertz: (Frequency, d128!(1000), "kilohertz", "kilohertz"), Kilohertz: (Frequency, d!(1000), "kilohertz", "kilohertz"),
Megahertz: (Frequency, d128!(1000000), "megahertz", "megahertz"), Megahertz: (Frequency, d!(1000000), "megahertz", "megahertz"),
Gigahertz: (Frequency, d128!(1000000000), "gigahertz", "gigahertz"), Gigahertz: (Frequency, d!(1000000000), "gigahertz", "gigahertz"),
Terahertz: (Frequency, d128!(1000000000000), "terahertz", "terahertz"), Terahertz: (Frequency, d!(1000000000000), "terahertz", "terahertz"),
Petahertz: (Frequency, d128!(1000000000000000), "petahertz", "petahertz"), Petahertz: (Frequency, d!(1000000000000000), "petahertz", "petahertz"),
RevolutionsPerMinute: (Frequency, d128!(60), "revolution per minute", "revolutions per minute"), RevolutionsPerMinute: (Frequency, d!(60), "revolution per minute", "revolutions per minute"),
KilometersPerHour: (Speed, d128!(1), "kilometer per hour", "kilometers per hour"), KilometersPerHour: (Speed, d!(1), "kilometer per hour", "kilometers per hour"),
MetersPerSecond: (Speed, d128!(3.6), "meter per second", "meters per second"), MetersPerSecond: (Speed, d!(3.6), "meter per second", "meters per second"),
MilesPerHour: (Speed, d128!(1.609344), "mile per hour", "miles per hour"), MilesPerHour: (Speed, d!(1.609344), "mile per hour", "miles per hour"),
FeetPerSecond: (Speed, d128!(1.09728), "foot per second", "feet per second"), FeetPerSecond: (Speed, d!(1.09728), "foot per second", "feet per second"),
Knot: (Speed, d128!(1.852), "knot", "knots"), Knot: (Speed, d!(1.852), "knot", "knots"),
Kelvin: (Temperature, d128!(0), "kelvin", "kelvin"), Kelvin: (Temperature, d!(0), "kelvin", "kelvin"),
Celsius: (Temperature, d128!(0), "celsius", "celsius"), Celsius: (Temperature, d!(0), "celsius", "celsius"),
Fahrenheit: (Temperature, d128!(0), "fahrenheit", "fahrenheit"), Fahrenheit: (Temperature, d!(0), "fahrenheit", "fahrenheit"),
); );
/// Returns the conversion factor between two units. /// Returns the conversion factor between two units.
@ -320,7 +320,7 @@ create_units!(
/// is 60. /// is 60.
/// ///
/// This is not sufficient for [`Temperature`] units. /// This is not sufficient for [`Temperature`] units.
pub fn get_conversion_factor(unit: Unit, to_unit: Unit) -> d128 { pub fn get_conversion_factor(unit: Unit, to_unit: Unit) -> D128 {
unit.weight() / to_unit.weight() unit.weight() / to_unit.weight()
} }
@ -336,14 +336,14 @@ pub fn convert(number: Number, to_unit: Unit) -> Result<Number, String> {
if number.unit.category() == UnitType::Temperature { if number.unit.category() == UnitType::Temperature {
match (number.unit, to_unit) { match (number.unit, to_unit) {
(Kelvin, Kelvin) => ok(value), (Kelvin, Kelvin) => ok(value),
(Kelvin, Celsius) => ok(value-d128!(273.15)), (Kelvin, Celsius) => ok(value-d!(273.15)),
(Kelvin, Fahrenheit) => ok(value*d128!(1.8)-d128!(459.67)), (Kelvin, Fahrenheit) => ok(value*d!(1.8)-d!(459.67)),
(Celsius, Celsius) => ok(value), (Celsius, Celsius) => ok(value),
(Celsius, Kelvin) => ok(value+d128!(273.15)), (Celsius, Kelvin) => ok(value+d!(273.15)),
(Celsius, Fahrenheit) => ok(value*d128!(1.8)+d128!(32)), (Celsius, Fahrenheit) => ok(value*d!(1.8)+d!(32)),
(Fahrenheit, Fahrenheit) => ok(value), (Fahrenheit, Fahrenheit) => ok(value),
(Fahrenheit, Kelvin) => ok((value+d128!(459.67))*d128!(5)/d128!(9)), (Fahrenheit, Kelvin) => ok((value+d!(459.67))*d!(5)/d!(9)),
(Fahrenheit, Celsius) => ok((value-d128!(32))/d128!(1.8)), (Fahrenheit, Celsius) => ok((value-d!(32))/d!(1.8)),
_ => Err(format!("Error converting temperature {:?} to {:?}", number.unit, to_unit)), _ => Err(format!("Error converting temperature {:?} to {:?}", number.unit, to_unit)),
} }
} else { } else {
@ -400,113 +400,113 @@ pub fn subtract(left: Number, right: Number) -> Result<Number, String> {
pub fn to_ideal_unit(number: Number) -> Number { pub fn to_ideal_unit(number: Number) -> Number {
let value = number.value * number.unit.weight(); let value = number.value * number.unit.weight();
if number.unit.category() == Length { if number.unit.category() == Length {
if value >= d128!(1000000000000000000) { // ≈ 0.1 light years if value >= d!(1000000000000000000) { // ≈ 0.1 light years
return Number::new(value/LightYear.weight(), LightYear) return Number::new(value/LightYear.weight(), LightYear)
} else if value >= d128!(1000000) { // 1 km } else if value >= d!(1000000) { // 1 km
return Number::new(value/Kilometer.weight(), Kilometer) return Number::new(value/Kilometer.weight(), Kilometer)
} else if value >= d128!(1000) { // 1 m } else if value >= d!(1000) { // 1 m
return Number::new(value/Meter.weight(), Meter) return Number::new(value/Meter.weight(), Meter)
} else if value >= d128!(10) { // 1 cm } else if value >= d!(10) { // 1 cm
return Number::new(value/Centimeter.weight(), Centimeter) return Number::new(value/Centimeter.weight(), Centimeter)
} else { } else {
return Number::new(value, Millimeter) return Number::new(value, Millimeter)
} }
} else if number.unit.category() == Time { } else if number.unit.category() == Time {
if value >= d128!(31556952000000000) { if value >= d!(31556952000000000) {
return Number::new(value/Year.weight(), Year); return Number::new(value/Year.weight(), Year);
} else if value >= d128!(86400000000000) { } else if value >= d!(86400000000000) {
return Number::new(value/Day.weight(), Day); return Number::new(value/Day.weight(), Day);
} else if value >= d128!(3600000000000) { } else if value >= d!(3600000000000) {
return Number::new(value/Hour.weight(), Hour); return Number::new(value/Hour.weight(), Hour);
} else if value >= d128!(60000000000) { } else if value >= d!(60000000000) {
return Number::new(value/Minute.weight(), Minute); return Number::new(value/Minute.weight(), Minute);
} else if value >= d128!(1000000000) { } else if value >= d!(1000000000) {
return Number::new(value/Second.weight(), Second); return Number::new(value/Second.weight(), Second);
} else if value >= d128!(1000000) { } else if value >= d!(1000000) {
return Number::new(value/Millisecond.weight(), Millisecond); return Number::new(value/Millisecond.weight(), Millisecond);
} else if value >= d128!(1000) { } else if value >= d!(1000) {
return Number::new(value/Microsecond.weight(), Microsecond); return Number::new(value/Microsecond.weight(), Microsecond);
} else { } else {
return Number::new(value, Nanosecond); return Number::new(value, Nanosecond);
} }
} else if number.unit.category() == Area { } else if number.unit.category() == Area {
if value >= d128!(1000000000000) { // 1 km2 if value >= d!(1000000000000) { // 1 km2
return Number::new(value/SquareKilometer.weight(), SquareKilometer) return Number::new(value/SquareKilometer.weight(), SquareKilometer)
} else if value >= d128!(10000000000) { // 1 hectare } else if value >= d!(10000000000) { // 1 hectare
return Number::new(value/Hectare.weight(), Hectare) return Number::new(value/Hectare.weight(), Hectare)
} else if value >= d128!(1000000) { // 1 m2 } else if value >= d!(1000000) { // 1 m2
return Number::new(value/SquareMeter.weight(), SquareMeter) return Number::new(value/SquareMeter.weight(), SquareMeter)
} else if value >= d128!(100) { // 1 cm2 } else if value >= d!(100) { // 1 cm2
return Number::new(value/SquareCentimeter.weight(), SquareCentimeter) return Number::new(value/SquareCentimeter.weight(), SquareCentimeter)
} else { } else {
return Number::new(value, SquareMillimeter) return Number::new(value, SquareMillimeter)
} }
} else if number.unit.category() == Volume { } else if number.unit.category() == Volume {
if value >= d128!(1000000000000000000) { // 1 km3 if value >= d!(1000000000000000000) { // 1 km3
return Number::new(value/CubicKilometer.weight(), CubicKilometer) return Number::new(value/CubicKilometer.weight(), CubicKilometer)
} else if value >= d128!(1000000000) { // 1 m3 } else if value >= d!(1000000000) { // 1 m3
return Number::new(value/CubicMeter.weight(), CubicMeter) return Number::new(value/CubicMeter.weight(), CubicMeter)
} else if value >= d128!(1000000) { // 1 l } else if value >= d!(1000000) { // 1 l
return Number::new(value/Liter.weight(), Liter) return Number::new(value/Liter.weight(), Liter)
} else if value >= d128!(1000) { // 1 ml } else if value >= d!(1000) { // 1 ml
return Number::new(value/Milliliter.weight(), Milliliter) return Number::new(value/Milliliter.weight(), Milliliter)
} else { } else {
return Number::new(value, CubicMillimeter) return Number::new(value, CubicMillimeter)
} }
} else if number.unit.category() == Energy { } else if number.unit.category() == Energy {
if value >= d128!(3600000000000000000) { // 1 petawatthour if value >= d!(3600000000000000000) { // 1 petawatthour
return Number::new(value/PetawattHour.weight(), PetawattHour) return Number::new(value/PetawattHour.weight(), PetawattHour)
} else if value >= d128!(3600000000000000) { // 1 terawatthour } else if value >= d!(3600000000000000) { // 1 terawatthour
return Number::new(value/TerawattHour.weight(), TerawattHour) return Number::new(value/TerawattHour.weight(), TerawattHour)
} else if value >= d128!(3600000000000) { // 1 gigawatthour } else if value >= d!(3600000000000) { // 1 gigawatthour
return Number::new(value/GigawattHour.weight(), GigawattHour) return Number::new(value/GigawattHour.weight(), GigawattHour)
} else if value >= d128!(3600000000) { // 1 megawatthour } else if value >= d!(3600000000) { // 1 megawatthour
return Number::new(value/MegawattHour.weight(), MegawattHour) return Number::new(value/MegawattHour.weight(), MegawattHour)
} else if value >= d128!(3600000) { // 1 kilowatthour } else if value >= d!(3600000) { // 1 kilowatthour
return Number::new(value/KilowattHour.weight(), KilowattHour) return Number::new(value/KilowattHour.weight(), KilowattHour)
} else if value >= d128!(3600) { // 1 watthour } else if value >= d!(3600) { // 1 watthour
return Number::new(value/WattHour.weight(), WattHour) return Number::new(value/WattHour.weight(), WattHour)
} else if value >= d128!(1) { // 1 joule } else if value >= d!(1) { // 1 joule
return Number::new(value, Joule) return Number::new(value, Joule)
} else { } else {
return Number::new(value/Millijoule.weight(), Millijoule) return Number::new(value/Millijoule.weight(), Millijoule)
} }
} else if number.unit.category() == Power { } else if number.unit.category() == Power {
if value >= d128!(1000000000000000) { // 1 petawatt if value >= d!(1000000000000000) { // 1 petawatt
return Number::new(value/Petawatt.weight(), Petawatt) return Number::new(value/Petawatt.weight(), Petawatt)
} else if value >= d128!(1000000000000) { // 1 terawatt } else if value >= d!(1000000000000) { // 1 terawatt
return Number::new(value/Terawatt.weight(), Terawatt) return Number::new(value/Terawatt.weight(), Terawatt)
} else if value >= d128!(1000000000) { // 1 gigawatt } else if value >= d!(1000000000) { // 1 gigawatt
return Number::new(value/Gigawatt.weight(), Gigawatt) return Number::new(value/Gigawatt.weight(), Gigawatt)
} else if value >= d128!(1000000) { // megawatt } else if value >= d!(1000000) { // megawatt
return Number::new(value/Megawatt.weight(), Megawatt) return Number::new(value/Megawatt.weight(), Megawatt)
} else if value >= d128!(1000) { // 1 kilowatt } else if value >= d!(1000) { // 1 kilowatt
return Number::new(value/Kilowatt.weight(), Kilowatt) return Number::new(value/Kilowatt.weight(), Kilowatt)
} else if value >= d128!(1) { // 1 watt } else if value >= d!(1) { // 1 watt
return Number::new(value, Watt) return Number::new(value, Watt)
} else { } else {
return Number::new(value/Milliwatt.weight(), Milliwatt) return Number::new(value/Milliwatt.weight(), Milliwatt)
} }
} else if number.unit.category() == ElectricCurrent { } else if number.unit.category() == ElectricCurrent {
if value >= d128!(1000) { // 1 kiloampere if value >= d!(1000) { // 1 kiloampere
return Number::new(value/Kiloampere.weight(), Kiloampere) return Number::new(value/Kiloampere.weight(), Kiloampere)
} else if value >= d128!(1) { // 1 ampere } else if value >= d!(1) { // 1 ampere
return Number::new(value, Ampere) return Number::new(value, Ampere)
} else { } else {
return Number::new(value/Milliampere.weight(), Milliampere) return Number::new(value/Milliampere.weight(), Milliampere)
} }
} else if number.unit.category() == Resistance { } else if number.unit.category() == Resistance {
if value >= d128!(1000) { // 1 kiloohm if value >= d!(1000) { // 1 kiloohm
return Number::new(value/Kiloohm.weight(), Kiloohm) return Number::new(value/Kiloohm.weight(), Kiloohm)
} else if value >= d128!(1) { // 1 ohm } else if value >= d!(1) { // 1 ohm
return Number::new(value, Ohm) return Number::new(value, Ohm)
} else { } else {
return Number::new(value/Milliohm.weight(), Milliohm) return Number::new(value/Milliohm.weight(), Milliohm)
} }
} else if number.unit.category() == Voltage { } else if number.unit.category() == Voltage {
if value >= d128!(1000) { // 1 kilovolt if value >= d!(1000) { // 1 kilovolt
return Number::new(value/Kilovolt.weight(), Kilovolt) return Number::new(value/Kilovolt.weight(), Kilovolt)
} else if value >= d128!(1) { // 1 volt } else if value >= d!(1) { // 1 volt
return Number::new(value, Volt) return Number::new(value, Volt)
} else { } else {
return Number::new(value/Millivolt.weight(), Millivolt) return Number::new(value/Millivolt.weight(), Millivolt)
@ -519,15 +519,15 @@ pub fn to_ideal_unit(number: Number) -> Number {
pub fn to_ideal_joule_unit(number: Number) -> Number { pub fn to_ideal_joule_unit(number: Number) -> Number {
let value = number.value * number.unit.weight(); let value = number.value * number.unit.weight();
if number.unit.category() == Energy { if number.unit.category() == Energy {
if value >= d128!(1000000000000) { // 1 terajoule if value >= d!(1000000000000) { // 1 terajoule
return Number::new(value/Terajoule.weight(), Terajoule) return Number::new(value/Terajoule.weight(), Terajoule)
} else if value >= d128!(1000000000) { // 1 gigajoule } else if value >= d!(1000000000) { // 1 gigajoule
return Number::new(value/Gigajoule.weight(), Gigajoule) return Number::new(value/Gigajoule.weight(), Gigajoule)
} else if value >= d128!(1000000) { // 1 megajoule } else if value >= d!(1000000) { // 1 megajoule
return Number::new(value/Megajoule.weight(), Megajoule) return Number::new(value/Megajoule.weight(), Megajoule)
} else if value >= d128!(1000) { // 1 kilojoule } else if value >= d!(1000) { // 1 kilojoule
return Number::new(value/Kilojoule.weight(), Kilojoule) return Number::new(value/Kilojoule.weight(), Kilojoule)
} else if value >= d128!(1) { // 1 joule } else if value >= d!(1) { // 1 joule
return Number::new(value/Joule.weight(), Joule) return Number::new(value/Joule.weight(), Joule)
} else { } else {
return Number::new(value/Millijoule.weight(), Millijoule) return Number::new(value/Millijoule.weight(), Millijoule)
@ -754,6 +754,15 @@ pub fn modulo(left: Number, right: Number) -> Result<Number, String> {
} }
} }
fn do_pow(left: D128, right: D128) -> D128 {
// Do pow with d256 for higher accuracy
let left = D256::try_from(left.transmute()).unwrap();
let right = D256::try_from(right.transmute()).unwrap();
let result = left.pow(right);
let result_d128: D128 = result.transmute();
result_d128
}
/// Returns a [`Number`] to the power of another [`Number`] /// Returns a [`Number`] to the power of another [`Number`]
/// ///
/// - If you take [`Length`] to the power of [`NoType`], the result has a unit of [`Area`]. /// - If you take [`Length`] to the power of [`NoType`], the result has a unit of [`Area`].
@ -765,28 +774,28 @@ pub fn pow(left: Number, right: Number) -> Result<Number, String> {
let rcat = left.unit.category(); let rcat = left.unit.category();
if left.unit == NoUnit && right.unit == NoUnit { if left.unit == NoUnit && right.unit == NoUnit {
// 3 ^ 2 // 3 ^ 2
Ok(Number::new(left.value.pow(right.value), left.unit)) Ok(Number::new(do_pow(left.value, right.value), left.unit))
} else if right.value == d128!(1) && right.unit == NoUnit { } else if right.value == d!(1) && right.unit == NoUnit {
Ok(left) Ok(left)
} else if lcat == Length && right.unit == NoUnit && right.value == d128!(2) { } else if lcat == Length && right.unit == NoUnit && right.value == d!(2) {
// x km ^ 2 // x km ^ 2
let result = (left.value * left.unit.weight()).pow(right.value); let result = do_pow(left.value * left.unit.weight(), right.value);
Ok(to_ideal_unit(Number::new(result, SquareMillimeter))) Ok(to_ideal_unit(Number::new(result, SquareMillimeter)))
} else if lcat == Length && right.unit == NoUnit && right.value == d128!(3) { } else if lcat == Length && right.unit == NoUnit && right.value == d!(3) {
// x km ^ 3 // x km ^ 3
let result = (left.value * left.unit.weight()).pow(right.value); let result = do_pow(left.value * left.unit.weight(), right.value);
Ok(to_ideal_unit(Number::new(result, CubicMillimeter))) Ok(to_ideal_unit(Number::new(result, CubicMillimeter)))
} else if lcat == Length && rcat == Length && right.value == d128!(1) { } else if lcat == Length && rcat == Length && right.value == d!(1) {
// x km ^ 1 km // x km ^ 1 km
Ok(multiply(left, right)?) Ok(multiply(left, right)?)
} else if lcat == Length && rcat == Length && right.value == d128!(2) { } else if lcat == Length && rcat == Length && right.value == d!(2) {
// x km ^ 2 km // x km ^ 2 km
let pow2 = multiply(left, Number::new(d128!(1), right.unit))?; let pow2 = multiply(left, Number::new(d!(1), right.unit))?;
let pow3 = multiply(pow2, Number::new(d128!(1), right.unit))?; let pow3 = multiply(pow2, Number::new(d!(1), right.unit))?;
Ok(pow3) Ok(pow3)
} else if lcat == Length && rcat == Area && right.value == d128!(1) { } else if lcat == Length && rcat == Area && right.value == d!(1) {
// x km ^ km2 // x km ^ km2
Ok(multiply(left, Number::new(d128!(1), right.unit))?) Ok(multiply(left, Number::new(d!(1), right.unit))?)
} else { } else {
Err(format!("Cannot multiply {:?} and {:?}", left.unit, right.unit)) Err(format!("Cannot multiply {:?} and {:?}", left.unit, right.unit))
} }
@ -794,7 +803,9 @@ pub fn pow(left: Number, right: Number) -> Result<Number, String> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use fastnum::decimal::Context;
use super::*;
macro_rules! assert_float_eq { macro_rules! assert_float_eq {
( $actual:expr, $expected:literal ) => { ( $actual:expr, $expected:literal ) => {
@ -808,7 +819,7 @@ mod tests {
use std::str::FromStr; use std::str::FromStr;
let value_string = &value.to_string(); let value_string = &value.to_string();
let value_d128 = d128::from_str(value_string).unwrap(); let value_d128 = D128::from_str(value_string, Context::default()).unwrap();
let number = Number::new(value_d128, unit); let number = Number::new(value_d128, unit);
let result = convert(number, to_unit); let result = convert(number, to_unit);