From ea42cf63559057e14211a08461fc07c6c463cb23 Mon Sep 17 00:00:00 2001 From: Ophir LOJKINE Date: Wed, 23 Mar 2022 13:04:09 +0100 Subject: [PATCH] Add support for is_nan, is_finite, and other floating-point testing functions --- src/function/builtin.rs | 15 +++++++++++++++ src/lib.rs | 5 +++++ tests/integration.rs | 12 ++++++++++++ 3 files changed, 32 insertions(+) diff --git a/src/function/builtin.rs b/src/function/builtin.rs index 7194da1..3b6d286 100644 --- a/src/function/builtin.rs +++ b/src/function/builtin.rs @@ -23,6 +23,16 @@ macro_rules! simple_math { }; } +fn float_is(func: fn(f64) -> bool) -> Option { + Some(Function::new(move |argument| { + if let Ok(num) = argument.as_float() { + Ok(func(num).into()) + } else { + Ok(false.into()) + } + })) +} + macro_rules! int_function { ($func:ident) => { Some(Function::new(|argument| { @@ -76,6 +86,11 @@ pub fn builtin_function(identifier: &str) -> Option { "floor" => simple_math!(floor), "round" => simple_math!(round), "ceil" => simple_math!(ceil), + "is_nan" => float_is(f64::is_nan), + "is_finite" => float_is(f64::is_finite), + "is_infinite" => float_is(f64::is_infinite), + "is_normal" => float_is(f64::is_normal), + "is_subnormal" => float_is(f64::is_subnormal), // Other "min" => Some(Function::new(|argument| { let arguments = argument.as_tuple()?; diff --git a/src/lib.rs b/src/lib.rs index f136143..237483e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -328,6 +328,11 @@ //! | `round` | 1 | Numeric | Returns the nearest integer to a number. Rounds half-way cases away from 0.0 | //! | `ceil` | 1 | Numeric | Returns the smallest integer greater than or equal to a number | //! | `if` | 3 | Boolean, Any, Any | If the first argument is true, returns the second argument, otherwise, returns the third | +//! | `is_nan` | 1 | Numeric | Returns true if the argument is the floating-point value NaN, false otherwise | +//! | `is_finite` | 1 | Numeric | Returns true if the argument is a finite floating-point number, false otherwise | +//! | `is_infinite` | 1 | Numeric | Returns true if the argument is an infinite floating-point number, false otherwise | +//! | `is_normal` | 1 | Numeric | Returns true if the argument is a floating-point number that is neither zero, infinite, subnormal, or NaN, false otherwise | +//! | `is_subnormal` | 1 | Numeric | Returns true if the argument is a [subnormal](https://en.wikipedia.org/wiki/Subnormal_number) number, false otherwise | //! | `math::ln` | 1 | Numeric | Returns the natural logarithm of the number | //! | `math::log` | 2 | Numeric, Numeric | Returns the logarithm of the number with respect to an arbitrary base | //! | `math::log2` | 1 | Numeric | Returns the base 2 logarithm of the number | diff --git a/tests/integration.rs b/tests/integration.rs index 945f000..77fc9fe 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -362,6 +362,18 @@ fn test_builtin_functions() { assert_eq!(eval("round(1.9)"), Ok(Value::Float(2.0))); assert_eq!(eval("ceil(1.1)"), Ok(Value::Float(2.0))); assert_eq!(eval("ceil(1.9)"), Ok(Value::Float(2.0))); + assert_eq!(eval("is_nan(\"xxx\")"), Ok(Value::Boolean(false))); + assert_eq!(eval("is_nan(1.0/0.0)"), Ok(Value::Boolean(false))); + assert_eq!(eval("is_nan(0.0/0.0)"), Ok(Value::Boolean(true))); + assert_eq!(eval("is_finite(1.0/0.0)"), Ok(Value::Boolean(false))); + assert_eq!(eval("is_finite(0.0/0.0)"), Ok(Value::Boolean(false))); + assert_eq!(eval("is_finite(0.0)"), Ok(Value::Boolean(true))); + assert_eq!(eval("is_infinite(0.0/0.0)"), Ok(Value::Boolean(false))); + assert_eq!(eval("is_infinite(1.0/0.0)"), Ok(Value::Boolean(true))); + assert_eq!(eval("is_normal(1.0/0.0)"), Ok(Value::Boolean(false))); + assert_eq!(eval("is_normal(0)"), Ok(Value::Boolean(false))); + assert_eq!(eval("is_subnormal(0)"), Ok(Value::Boolean(false))); + assert_eq!(eval("is_subnormal(1.0e-308)"), Ok(Value::Boolean(true))); // Other assert_eq!(eval("min(4.0, 3)"), Ok(Value::Int(3))); assert_eq!(eval("max(4.0, 3)"), Ok(Value::Float(4.0)));