perf: compile date comparisons into numeric VM
This commit is contained in:
@@ -314,15 +314,67 @@ fn eval_binary(operator: BinaryOp, lhs: Value, rhs: Value) -> Result<Value, Eval
|
||||
BinaryOp::Multiply => Ok(Value::Number(number(lhs)? * number(rhs)?)),
|
||||
BinaryOp::Divide => Ok(Value::Number(number(lhs)? / number(rhs)?)),
|
||||
BinaryOp::Remainder => Ok(Value::Number(number(lhs)? % number(rhs)?)),
|
||||
BinaryOp::Equal => Ok(Value::Boolean(lhs == rhs)),
|
||||
BinaryOp::NotEqual => Ok(Value::Boolean(lhs != rhs)),
|
||||
BinaryOp::Less => Ok(Value::Boolean(number(lhs)? < number(rhs)?)),
|
||||
BinaryOp::LessEqual => Ok(Value::Boolean(number(lhs)? <= number(rhs)?)),
|
||||
BinaryOp::Greater => Ok(Value::Boolean(number(lhs)? > number(rhs)?)),
|
||||
BinaryOp::GreaterEqual => Ok(Value::Boolean(number(lhs)? >= number(rhs)?)),
|
||||
BinaryOp::Equal => Ok(Value::Boolean(match (lhs, rhs) {
|
||||
(Value::Number(lhs), Value::Number(rhs)) => float_equal(lhs, rhs),
|
||||
(Value::Boolean(lhs), Value::Boolean(rhs)) => lhs == rhs,
|
||||
_ => {
|
||||
return Err(EvalError::new(
|
||||
"comparison operands must have the same type",
|
||||
));
|
||||
}
|
||||
})),
|
||||
BinaryOp::NotEqual => Ok(Value::Boolean(match (lhs, rhs) {
|
||||
(Value::Number(lhs), Value::Number(rhs)) => float_not_equal(lhs, rhs),
|
||||
(Value::Boolean(lhs), Value::Boolean(rhs)) => lhs != rhs,
|
||||
_ => {
|
||||
return Err(EvalError::new(
|
||||
"comparison operands must have the same type",
|
||||
));
|
||||
}
|
||||
})),
|
||||
BinaryOp::Less => {
|
||||
let (lhs, rhs) = (number(lhs)?, number(rhs)?);
|
||||
Ok(Value::Boolean(
|
||||
(rhs - lhs) / float_comparison_scale(lhs, rhs) > f64::EPSILON,
|
||||
))
|
||||
}
|
||||
BinaryOp::LessEqual => {
|
||||
let (lhs, rhs) = (number(lhs)?, number(rhs)?);
|
||||
Ok(Value::Boolean(
|
||||
(rhs - lhs) / float_comparison_scale(lhs, rhs) > -f64::EPSILON,
|
||||
))
|
||||
}
|
||||
BinaryOp::Greater => {
|
||||
let (lhs, rhs) = (number(lhs)?, number(rhs)?);
|
||||
Ok(Value::Boolean(
|
||||
(lhs - rhs) / float_comparison_scale(lhs, rhs) > f64::EPSILON,
|
||||
))
|
||||
}
|
||||
BinaryOp::GreaterEqual => {
|
||||
let (lhs, rhs) = (number(lhs)?, number(rhs)?);
|
||||
Ok(Value::Boolean(
|
||||
(lhs - rhs) / float_comparison_scale(lhs, rhs) > -f64::EPSILON,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn float_comparison_scale(lhs: f64, rhs: f64) -> f64 {
|
||||
if lhs * rhs == 0.0 {
|
||||
1.0
|
||||
} else {
|
||||
lhs.abs().max(rhs.abs())
|
||||
}
|
||||
}
|
||||
|
||||
fn float_equal(lhs: f64, rhs: f64) -> bool {
|
||||
(lhs - rhs).abs() / float_comparison_scale(lhs, rhs) <= f64::EPSILON
|
||||
}
|
||||
|
||||
fn float_not_equal(lhs: f64, rhs: f64) -> bool {
|
||||
(lhs - rhs).abs() / float_comparison_scale(lhs, rhs) > f64::EPSILON
|
||||
}
|
||||
|
||||
fn eval_builtin(builtin: Builtin, args: &[Value]) -> Result<Value, EvalError> {
|
||||
let numeric = |index: usize| -> Result<f64, EvalError> {
|
||||
args.get(index)
|
||||
@@ -1313,6 +1365,35 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn floating_comparisons_match_rhai_epsilon_semantics() {
|
||||
let adjacent = 11.699999999999998_f64;
|
||||
assert_eq!(
|
||||
evaluate("value == 11.7", &[("value", Value::Number(adjacent))]),
|
||||
Value::Boolean(true)
|
||||
);
|
||||
assert_eq!(
|
||||
evaluate("value != 11.7", &[("value", Value::Number(adjacent))]),
|
||||
Value::Boolean(false)
|
||||
);
|
||||
assert_eq!(
|
||||
evaluate("value <= 11.7", &[("value", Value::Number(adjacent))]),
|
||||
Value::Boolean(true)
|
||||
);
|
||||
assert_eq!(
|
||||
evaluate("value >= 11.7", &[("value", Value::Number(adjacent))]),
|
||||
Value::Boolean(true)
|
||||
);
|
||||
assert_eq!(
|
||||
evaluate("value < 11.7", &[("value", Value::Number(adjacent))]),
|
||||
Value::Boolean(false)
|
||||
);
|
||||
assert_eq!(
|
||||
evaluate("value > 11.7", &[("value", Value::Number(adjacent))]),
|
||||
Value::Boolean(false)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn short_circuit_does_not_resolve_unused_variable() {
|
||||
let program = compile("false && missing", |name| {
|
||||
|
||||
Reference in New Issue
Block a user