Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ Arguments:
Options:
-e, --expression <EXPRESSION> Expression to evaluate
-l, --load <LOAD> Load files before entering the REPL
-p, --precision <PRECISION> Decimal precision to use for calculations [default: 1024]
-p, --precision <PRECISION> Binary precision (in bits) to use for calculations [default: 1024]
-d, --digits <DIGITS> Decimal digits to use for calculations and output
-r, --rounding-mode <ROUNDING_MODE> Rounding mode to use for calculations [default: to-even]
--stack-size <STACK_SIZE> Stack size in MB for evaluations [default: 128]
-h, --help Print help
Expand Down Expand Up @@ -185,8 +186,12 @@ the hood):
```

You can specify the rounding mode, and what sort of precision you'd like to see
in the output by using the `--rounding-mode` and `--precision` options
respectively.
in the output by using the `--rounding-mode`, `--precision`, and `--digits`
options respectively. `--precision` controls the underlying binary precision
used during calculations, while `--digits` can be used when you care about the
number of decimal places in the result. The rounding behavior when trimming
to a fixed number of decimal places follows the selected `--rounding-mode`
(default: `to-even`).

#### Boolean

Expand Down
6 changes: 5 additions & 1 deletion crates/val-wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,14 @@ pub fn evaluate(input: &str) -> Result<JsValue, JsValue> {
let mut evaluator = Evaluator::from(Environment::new(val::Config {
precision: 53,
rounding_mode: RoundingMode::FromZero.into(),
digits: None,
}));

match evaluator.eval(&ast) {
Ok(value) => Ok(to_value(&value.to_string()).unwrap()),
Ok(value) => Ok(
to_value(&value.format_with_config(&evaluator.environment.config))
.unwrap(),
),
Err(error) => Err(
to_value(&[ValError {
kind: ErrorKind::Evaluator,
Expand Down
94 changes: 79 additions & 15 deletions src/arguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,18 @@ pub struct Arguments {
short,
long,
default_value = "1024",
help = "Decimal precision to use for calculations"
help = "Binary precision (in bits) to use for calculations"
)]
precision: usize,

#[clap(
long,
short = 'd',
conflicts_with = "precision",
help = "Decimal digits to use for calculations and output"
)]
digits: Option<usize>,

#[clap(
short,
long,
Expand Down Expand Up @@ -83,10 +91,7 @@ impl Arguments {

let filename = filename.to_string_lossy().to_string();

let mut evaluator = Evaluator::from(Environment::new(Config {
precision: self.precision,
rounding_mode: self.rounding_mode.into(),
}));
let mut evaluator = Evaluator::from(Environment::new(self.to_config()));

match parse(&content) {
Ok(ast) => match evaluator.eval(&ast) {
Expand All @@ -112,10 +117,7 @@ impl Arguments {
}

fn eval_expression(&self, value: String) -> Result {
let mut evaluator = Evaluator::from(Environment::new(Config {
precision: self.precision,
rounding_mode: self.rounding_mode.into(),
}));
let mut evaluator = Evaluator::from(Environment::new(self.to_config()));

match parse(&value) {
Ok(ast) => match evaluator.eval(&ast) {
Expand All @@ -124,7 +126,10 @@ impl Arguments {
return Ok(());
}

println!("{}", value);
println!(
"{}",
value.format_with_config(&evaluator.environment.config)
);

Ok(())
}
Expand Down Expand Up @@ -166,10 +171,7 @@ impl Arguments {
editor.set_helper(Some(Highlighter::new()));
editor.load_history(&history).ok();

let mut evaluator = Evaluator::from(Environment::new(Config {
precision: self.precision,
rounding_mode: self.rounding_mode.into(),
}));
let mut evaluator = Evaluator::from(Environment::new(self.to_config()));

if let Some(filenames) = &self.load {
for filename in filenames {
Expand Down Expand Up @@ -212,7 +214,12 @@ impl Arguments {

match parse(line) {
Ok(ast) => match evaluator.eval(&ast) {
Ok(value) if !matches!(value, Value::Null) => println!("{value}"),
Ok(value) if !matches!(value, Value::Null) => {
println!(
"{}",
value.format_with_config(&evaluator.environment.config)
);
}
Ok(_) => {}
Err(error) => error
.report("<input>")
Expand All @@ -228,6 +235,29 @@ impl Arguments {
}
}
}

fn to_config(&self) -> Config {
Config {
precision: self.precision_bits(),
rounding_mode: self.rounding_mode.into(),
digits: self.digits,
}
}

fn precision_bits(&self) -> usize {
self
.digits
.map(Self::digits_to_binary_precision)
.unwrap_or(self.precision)
}

fn digits_to_binary_precision(digits: usize) -> usize {
if digits == 0 {
return 0;
}

((digits as f64) * f64::consts::LOG2_10).ceil() as usize
}
}

#[cfg(test)]
Expand Down Expand Up @@ -325,4 +355,38 @@ mod tests {
error
);
}

#[test]
fn digits_option_sets_decimal_precision() {
let arguments = Arguments::parse_from(vec![
"program",
"--digits",
"25",
"--rounding-mode",
"to-zero",
]);

assert_eq!(arguments.digits, Some(25));

assert_eq!(
arguments.precision_bits(),
Arguments::digits_to_binary_precision(25)
);
}

#[test]
fn digits_conflicts_with_precision() {
let result = Arguments::try_parse_from(vec![
"program",
"--digits",
"10",
"--precision",
"512",
]);

assert!(
result.is_err(),
"Parser should reject simultaneous --digits and --precision"
);
}
}
2 changes: 2 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
pub struct Config {
pub precision: usize,
pub rounding_mode: astro_float::RoundingMode,
pub digits: Option<usize>,
}

impl Default for Config {
fn default() -> Self {
Self {
precision: 1024,
rounding_mode: astro_float::RoundingMode::ToEven,
digits: None,
}
}
}
4 changes: 2 additions & 2 deletions src/environment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -751,7 +751,7 @@ impl<'src> Environment<'src> {
let mut output_strings = Vec::with_capacity(payload.arguments.len());

for argument in &payload.arguments {
output_strings.push(format!("{}", argument));
output_strings.push(argument.format_with_config(&payload.config));
}

print!("{}", output_strings.join(" "));
Expand All @@ -766,7 +766,7 @@ impl<'src> Environment<'src> {
let mut output_strings = Vec::with_capacity(payload.arguments.len());

for argument in &payload.arguments {
output_strings.push(format!("{}", argument));
output_strings.push(argument.format_with_config(&payload.config));
}

println!("{}", output_strings.join(" "));
Expand Down
Loading
Loading