Decimals are exact
Fractions are kept as exact rationals, not floats. The printed number is
rounded and ends in three zeros it does not have. The value behind it is not, so
multiplying by three gives 2^64 back to the digit.
[00]Overview
A Rust library and CLI. Fractions stay exact, integers have no ceiling, and the big ones run tens of times faster than GNU bc.
cargo add yarer
2^64/3*3
18446744073709551616
52!/(5!*47!)
2598960
(1+1/1000000)^1000000
Eval error: the result would need about 40000000 bits, over the size limit of 1048576 bits
(1+1/1000000)^1000000
^
[01]Properties
Fractions are kept as exact rationals, not floats. The printed number is
rounded and ends in three zeros it does not have. The value behind it is not, so
multiplying by three gives 2^64 back to the digit.
Whole numbers are BigInt, so 100! prints all 158
digits. And 52!/(5!*47!) gives exactly 2598960, the
number of poker hands, through 68-digit numbers on the way.
A comparison gives 1 or 0, as in GNU bc. Multiply by
it and you get a branch without having one. Both halves are computed, one is
zeroed, and the answer is 20 either way.
[02]Usage
Library
use yarer::{Expression, Session};
fn main() -> Result<(), yarer::Error> {
let session = Session::init();
let expr = Expression::compile("1/cos(x^2)")?;
session.set("x", 1)?;
println!("{}", expr.eval(&session)?);
Ok(())
}
Compile once, evaluate as often as you like, against any
value of x.
Command line
yarer -e '16*atan(1/5)-4*atan(1/239)'
3.1415926535897936
printf '2^64/3*3\n52!/(5!*47!)\n' | yarer
18446744073709551616
2598960
Machin's formula for pi. Piped input runs a line at a time,
as in GNU bc, and repeated -e flags share one session. Values go to
stdout, errors to stderr.
cargo install yarer builds the command line.
cargo add yarer --no-default-features gives a program the evaluator alone:
41 crates instead of 74.
[03]Worked example
Variables live in the session, so a formula can be built a line at a time and every step stays available. This is a European call priced with Black-Scholes.
The refusal at the top of the page is the same idea from the other side.
(1+1/1000000)^1000000 needs forty million bits, and yarer knows it
before computing any of them. You get an error, not a hang.
Version 0.4.1 is production ready: 196 tests, a fuzz corpus replayed on every push, and no way past the size guards.
S=100;K=100;T=1;r=0.05;sigma=0.2;
0.2
d1=(ln(S/K)+(r+sigma^2/2)*T)/(sigma*sqrt(T))
0.35
d2=d1-sigma*sqrt(T)
0.15
S*cdf(d1)-K*exp(-r*T)*cdf(d2)
10.45058357218556
[04]Reference
Precedence, weakest to strongest
=rightorxorleftandleftnotright, prefix<><=>===<>left+-left*/modleft^right- unaryright! factorialleft, postfix!=. Since ! is the postfix factorial,
5!=3 would be ambiguous, so the spelling is <>.and, or, xor, not and
mod are reserved words in every casing, and cannot name a variable.0 and 1/0 is a division error, not
0, because a stack machine holds both operands before it sees the
operator.sqrt,
ln, sin and the rest go through f64, so
sqrt(2)^2 is 2.0000000000000004. Add, subtract,
multiply, divide and raise to an integer power, and nothing is rounded.Functions
Matched case-insensitively. Arguments are always parenthesised.
Constants
Held as exact rationals, not as the decimals shown here.