diff --git a/rust/src/instructions/math/logarithm/ln_func.rs b/rust/src/instructions/math/logarithm/ln_func.rs index 4ed165f8..4442d552 100644 --- a/rust/src/instructions/math/logarithm/ln_func.rs +++ b/rust/src/instructions/math/logarithm/ln_func.rs @@ -19,11 +19,14 @@ use crate::types::value::Value; use crate::utils::{expected_type::expected_type, get_type_name::get_type_name}; #[inline(always)] pub fn ln_values(a: Value, num_type: PrimitiveTypes, ip: usize) -> Result { - if !a.is_number() { + if !matches!( + &a, + Value::Float16(_) | Value::Float32(_) | Value::Float64(_) + ) { return Err(VMError::TypeMismatch { ip, expected: expected_type(num_type, ExpectedCategory::Float), - found: get_type_name(a), + found: get_type_name(a.clone()), }); } Ok(match num_type { @@ -34,7 +37,7 @@ pub fn ln_values(a: Value, num_type: PrimitiveTypes, ip: usize) -> Result Result mod tests { use super::*; #[test] - fn invalid_operand_reports_type_mismatch_and_preserves_stack() { - let invalid = Value::String("invalid".into()); + fn rejects_non_float_operands_without_mutating_stack() { + for (value, found) in [ + (Value::Int32(1), "Integer"), + (Value::String("invalid".into()), "String"), + ] { + assert!(matches!( + ln_values(value.clone(), PrimitiveTypes::Flt, 17), + Err(VMError::TypeMismatch { ip: 17, expected: "Float", found: actual }) + if actual == found + )); + let mut stack = Stack::from_vec(vec![value]); + let original = stack.clone(); + assert!(matches!( + ln_func(&mut stack, PrimitiveTypes::Flt, 18), + Err(VMError::TypeMismatch { ip: 18, expected: "Float", found: actual }) + if actual == found + )); + assert_eq!(stack, original); + } + } + #[test] + fn accepts_cross_width_float_operands_and_returns_directive_type() { assert!(matches!( - ln_values(invalid.clone(), PrimitiveTypes::Flt, 17), - Err(VMError::TypeMismatch { - ip: 17, - expected: "Float", - found: "String" - }) + ln_values(Value::Float16(half::f16::ONE), PrimitiveTypes::Dbl, 19), + Ok(Value::Float64(0.0)) )); - let mut stack = Stack::from_vec(vec![invalid]); - let original = stack.clone(); assert!(matches!( - ln_func(&mut stack, PrimitiveTypes::Flt, 18), - Err(VMError::TypeMismatch { - ip: 18, - expected: "Float", - found: "String" - }) + ln_values(Value::Float64(1.0), PrimitiveTypes::Hlf, 20), + Ok(Value::Float16(value)) if value == half::f16::ZERO )); - assert_eq!(stack, original); } #[test] - fn unsupported_directive_reports_numeric_operand_type() { + fn unsupported_directive_reports_unknown_without_mutating_stack() { assert!(matches!( ln_values(Value::Float32(1.0), PrimitiveTypes::Int, 19), Err(VMError::TypeMismatch { ip: 19, expected: "Float", - found: "float32" + found: "unknown" }) )); + let mut stack = Stack::from_vec(vec![Value::Float32(1.0)]); + let original = stack.clone(); + assert!(ln_func(&mut stack, PrimitiveTypes::Int, 20).is_err()); + assert_eq!(stack, original); } } diff --git a/rust/src/instructions/math/logarithm/log10_func.rs b/rust/src/instructions/math/logarithm/log10_func.rs index e797a60a..89f0e1a1 100644 --- a/rust/src/instructions/math/logarithm/log10_func.rs +++ b/rust/src/instructions/math/logarithm/log10_func.rs @@ -19,11 +19,14 @@ use crate::types::value::Value; use crate::utils::{expected_type::expected_type, get_type_name::get_type_name}; #[inline(always)] pub fn log10_values(a: Value, num_type: PrimitiveTypes, ip: usize) -> Result { - if !a.is_number() { + if !matches!( + &a, + Value::Float16(_) | Value::Float32(_) | Value::Float64(_) + ) { return Err(VMError::TypeMismatch { ip, expected: expected_type(num_type, ExpectedCategory::Float), - found: get_type_name(a), + found: get_type_name(a.clone()), }); } Ok(match num_type { @@ -34,7 +37,7 @@ pub fn log10_values(a: Value, num_type: PrimitiveTypes, ip: usize) -> Result Res mod tests { use super::*; #[test] - fn invalid_operand_reports_type_mismatch_and_preserves_stack() { - let invalid = Value::String("invalid".into()); + fn rejects_non_float_operands_without_mutating_stack() { + for (value, found) in [ + (Value::Int32(1), "Integer"), + (Value::String("invalid".into()), "String"), + ] { + assert!(matches!( + log10_values(value.clone(), PrimitiveTypes::Flt, 17), + Err(VMError::TypeMismatch { ip: 17, expected: "Float", found: actual }) + if actual == found + )); + let mut stack = Stack::from_vec(vec![value]); + let original = stack.clone(); + assert!(matches!( + log10_func(&mut stack, PrimitiveTypes::Flt, 18), + Err(VMError::TypeMismatch { ip: 18, expected: "Float", found: actual }) + if actual == found + )); + assert_eq!(stack, original); + } + } + #[test] + fn accepts_cross_width_float_operands_and_returns_directive_type() { assert!(matches!( - log10_values(invalid.clone(), PrimitiveTypes::Flt, 17), - Err(VMError::TypeMismatch { - ip: 17, - expected: "Float", - found: "String" - }) + log10_values(Value::Float16(half::f16::ONE), PrimitiveTypes::Dbl, 19), + Ok(Value::Float64(0.0)) )); - let mut stack = Stack::from_vec(vec![invalid]); - let original = stack.clone(); assert!(matches!( - log10_func(&mut stack, PrimitiveTypes::Flt, 18), - Err(VMError::TypeMismatch { - ip: 18, - expected: "Float", - found: "String" - }) + log10_values(Value::Float64(1.0), PrimitiveTypes::Hlf, 20), + Ok(Value::Float16(value)) if value == half::f16::ZERO )); + } + #[test] + fn unsupported_directive_reports_unknown_without_mutating_stack() { + assert!(matches!( + log10_values(Value::Float32(1.0), PrimitiveTypes::Int, 21), + Err(VMError::TypeMismatch { ip: 21, expected: "Float", found: "unknown" }) + )); + let mut stack = Stack::from_vec(vec![Value::Float32(1.0)]); + let original = stack.clone(); + assert!(log10_func(&mut stack, PrimitiveTypes::Int, 22).is_err()); assert_eq!(stack, original); } } diff --git a/rust/src/instructions/math/logarithm/log2_func.rs b/rust/src/instructions/math/logarithm/log2_func.rs index 7eb7e110..287eb7b5 100644 --- a/rust/src/instructions/math/logarithm/log2_func.rs +++ b/rust/src/instructions/math/logarithm/log2_func.rs @@ -19,11 +19,14 @@ use crate::types::value::Value; use crate::utils::{expected_type::expected_type, get_type_name::get_type_name}; #[inline(always)] pub fn log2_values(a: Value, num_type: PrimitiveTypes, ip: usize) -> Result { - if !a.is_number() { + if !matches!( + &a, + Value::Float16(_) | Value::Float32(_) | Value::Float64(_) + ) { return Err(VMError::TypeMismatch { ip, expected: expected_type(num_type, ExpectedCategory::Float), - found: get_type_name(a), + found: get_type_name(a.clone()), }); } Ok(match num_type { @@ -34,7 +37,7 @@ pub fn log2_values(a: Value, num_type: PrimitiveTypes, ip: usize) -> Result Resu mod tests { use super::*; #[test] - fn invalid_operand_reports_type_mismatch_and_preserves_stack() { - let invalid = Value::String("invalid".into()); + fn rejects_non_float_operands_without_mutating_stack() { + for (value, found) in [ + (Value::Int32(1), "Integer"), + (Value::String("invalid".into()), "String"), + ] { + assert!(matches!( + log2_values(value.clone(), PrimitiveTypes::Flt, 17), + Err(VMError::TypeMismatch { ip: 17, expected: "Float", found: actual }) + if actual == found + )); + let mut stack = Stack::from_vec(vec![value]); + let original = stack.clone(); + assert!(matches!( + log2_func(&mut stack, PrimitiveTypes::Flt, 18), + Err(VMError::TypeMismatch { ip: 18, expected: "Float", found: actual }) + if actual == found + )); + assert_eq!(stack, original); + } + } + #[test] + fn accepts_cross_width_float_operands_and_returns_directive_type() { assert!(matches!( - log2_values(invalid.clone(), PrimitiveTypes::Flt, 17), - Err(VMError::TypeMismatch { - ip: 17, - expected: "Float", - found: "String" - }) + log2_values(Value::Float16(half::f16::ONE), PrimitiveTypes::Dbl, 19), + Ok(Value::Float64(0.0)) )); - let mut stack = Stack::from_vec(vec![invalid]); - let original = stack.clone(); assert!(matches!( - log2_func(&mut stack, PrimitiveTypes::Flt, 18), - Err(VMError::TypeMismatch { - ip: 18, - expected: "Float", - found: "String" - }) + log2_values(Value::Float64(1.0), PrimitiveTypes::Hlf, 20), + Ok(Value::Float16(value)) if value == half::f16::ZERO )); + } + #[test] + fn unsupported_directive_reports_unknown_without_mutating_stack() { + assert!(matches!( + log2_values(Value::Float32(1.0), PrimitiveTypes::Int, 21), + Err(VMError::TypeMismatch { ip: 21, expected: "Float", found: "unknown" }) + )); + let mut stack = Stack::from_vec(vec![Value::Float32(1.0)]); + let original = stack.clone(); + assert!(log2_func(&mut stack, PrimitiveTypes::Int, 22).is_err()); assert_eq!(stack, original); } } diff --git a/rust/src/instructions/math/vector/logarithm/expv_func.rs b/rust/src/instructions/math/vector/logarithm/expv_func.rs index d5e9c89e..a6b7c539 100644 --- a/rust/src/instructions/math/vector/logarithm/expv_func.rs +++ b/rust/src/instructions/math/vector/logarithm/expv_func.rs @@ -25,7 +25,10 @@ pub fn expv_values(value: Value, num_type: PrimitiveTypes, ip: usize) -> Result< found: get_type_name(value.clone()), })?; for value in values.iter() { - if !value.is_number() { + if !matches!( + value, + Value::Float16(_) | Value::Float32(_) | Value::Float64(_) + ) { return Err(VMError::TypeMismatch { ip, expected: expected_type(num_type, ExpectedCategory::Float), @@ -41,7 +44,7 @@ pub fn expv_values(value: Value, num_type: PrimitiveTypes, ip: usize) -> Result< return Err(VMError::TypeMismatch { ip, expected: expected_type(num_type, ExpectedCategory::Float), - found: expected_type(num_type, ExpectedCategory::All), + found: "unknown", }); } }) @@ -65,23 +68,41 @@ mod tests { } #[test] fn supports_float_directives() { - for num_type in [ - PrimitiveTypes::Hlf, - PrimitiveTypes::Flt, - PrimitiveTypes::Dbl, - ] { - assert!(expv_values(array(vec![Value::Int32(1)]), num_type, 0).is_ok()); - } + assert!(matches!( + expv_values(array(vec![Value::Float64(0.0)]), PrimitiveTypes::Hlf, 0), + Ok(Value::Array(values)) if matches!(&values[0], Value::Float16(_)) + )); + assert!(matches!( + expv_values(array(vec![Value::Float16(half::f16::ZERO)]), PrimitiveTypes::Dbl, 1), + Ok(Value::Array(values)) if matches!(&values[0], Value::Float64(_)) + )); } #[test] fn validates_operands_and_preserves_stack() { - for value in [Value::Bool(false), array(vec![Value::Bool(false)])] { + for (value, found) in [ + (Value::Bool(false), "Boolean"), + (array(vec![Value::Int32(1)]), "Integer"), + (array(vec![Value::Bool(false)]), "Boolean"), + ] { + assert!(matches!( + expv_values(value.clone(), PrimitiveTypes::Flt, 16), + Err(VMError::TypeMismatch { ip: 16, expected: "Float", found: actual }) + if actual == found + )); let mut stack = Stack::from_vec(vec![value]); let original = stack.clone(); assert!(expv_func(&mut stack, PrimitiveTypes::Flt, 17).is_err()); assert_eq!(stack, original); } - assert!(expv_values(array(vec![Value::Float32(1.0)]), PrimitiveTypes::Int, 18).is_err()); + let value = array(vec![Value::Float32(1.0)]); + assert!(matches!( + expv_values(value.clone(), PrimitiveTypes::Int, 18), + Err(VMError::TypeMismatch { ip: 18, expected: "Float", found: "unknown" }) + )); + let mut stack = Stack::from_vec(vec![value]); + let original = stack.clone(); + assert!(expv_func(&mut stack, PrimitiveTypes::Int, 18).is_err()); + assert_eq!(stack, original); } #[test] fn preserves_nan_behavior() { diff --git a/rust/src/instructions/math/vector/logarithm/lnv_func.rs b/rust/src/instructions/math/vector/logarithm/lnv_func.rs index 15733f8e..a51f5a1a 100644 --- a/rust/src/instructions/math/vector/logarithm/lnv_func.rs +++ b/rust/src/instructions/math/vector/logarithm/lnv_func.rs @@ -25,7 +25,10 @@ pub fn lnv_values(value: Value, num_type: PrimitiveTypes, ip: usize) -> Result Result Resul found: get_type_name(value.clone()), })?; for value in values.iter() { - if !value.is_number() { + if !matches!( + value, + Value::Float16(_) | Value::Float32(_) | Value::Float64(_) + ) { return Err(VMError::TypeMismatch { ip, expected: expected_type(num_type, ExpectedCategory::Float), @@ -41,7 +44,7 @@ pub fn log10v_values(value: Value, num_type: PrimitiveTypes, ip: usize) -> Resul return Err(VMError::TypeMismatch { ip, expected: expected_type(num_type, ExpectedCategory::Float), - found: expected_type(num_type, ExpectedCategory::All), + found: "unknown", }); } }) @@ -65,23 +68,41 @@ mod tests { } #[test] fn supports_float_directives() { - for num_type in [ - PrimitiveTypes::Hlf, - PrimitiveTypes::Flt, - PrimitiveTypes::Dbl, - ] { - assert!(log10v_values(array(vec![Value::Int32(1)]), num_type, 0).is_ok()); - } + assert!(matches!( + log10v_values(array(vec![Value::Float64(1.0)]), PrimitiveTypes::Hlf, 0), + Ok(Value::Array(values)) if matches!(&values[0], Value::Float16(_)) + )); + assert!(matches!( + log10v_values(array(vec![Value::Float16(half::f16::ONE)]), PrimitiveTypes::Dbl, 1), + Ok(Value::Array(values)) if matches!(&values[0], Value::Float64(_)) + )); } #[test] fn validates_operands_and_preserves_stack() { - for value in [Value::Bool(false), array(vec![Value::Bool(false)])] { + for (value, found) in [ + (Value::Bool(false), "Boolean"), + (array(vec![Value::Int32(1)]), "Integer"), + (array(vec![Value::Bool(false)]), "Boolean"), + ] { + assert!(matches!( + log10v_values(value.clone(), PrimitiveTypes::Flt, 16), + Err(VMError::TypeMismatch { ip: 16, expected: "Float", found: actual }) + if actual == found + )); let mut stack = Stack::from_vec(vec![value]); let original = stack.clone(); assert!(log10v_func(&mut stack, PrimitiveTypes::Flt, 17).is_err()); assert_eq!(stack, original); } - assert!(log10v_values(array(vec![Value::Float32(1.0)]), PrimitiveTypes::Int, 18).is_err()); + let value = array(vec![Value::Float32(1.0)]); + assert!(matches!( + log10v_values(value.clone(), PrimitiveTypes::Int, 18), + Err(VMError::TypeMismatch { ip: 18, expected: "Float", found: "unknown" }) + )); + let mut stack = Stack::from_vec(vec![value]); + let original = stack.clone(); + assert!(log10v_func(&mut stack, PrimitiveTypes::Int, 18).is_err()); + assert_eq!(stack, original); } #[test] fn preserves_nan_behavior() { diff --git a/rust/src/instructions/math/vector/logarithm/log2v_func.rs b/rust/src/instructions/math/vector/logarithm/log2v_func.rs index cc570736..9577be9a 100644 --- a/rust/src/instructions/math/vector/logarithm/log2v_func.rs +++ b/rust/src/instructions/math/vector/logarithm/log2v_func.rs @@ -25,7 +25,10 @@ pub fn log2v_values(value: Value, num_type: PrimitiveTypes, ip: usize) -> Result found: get_type_name(value.clone()), })?; for value in values.iter() { - if !value.is_number() { + if !matches!( + value, + Value::Float16(_) | Value::Float32(_) | Value::Float64(_) + ) { return Err(VMError::TypeMismatch { ip, expected: expected_type(num_type, ExpectedCategory::Float), @@ -41,7 +44,7 @@ pub fn log2v_values(value: Value, num_type: PrimitiveTypes, ip: usize) -> Result return Err(VMError::TypeMismatch { ip, expected: expected_type(num_type, ExpectedCategory::Float), - found: expected_type(num_type, ExpectedCategory::All), + found: "unknown", }); } }) @@ -65,23 +68,41 @@ mod tests { } #[test] fn supports_float_directives() { - for num_type in [ - PrimitiveTypes::Hlf, - PrimitiveTypes::Flt, - PrimitiveTypes::Dbl, - ] { - assert!(log2v_values(array(vec![Value::Int32(1)]), num_type, 0).is_ok()); - } + assert!(matches!( + log2v_values(array(vec![Value::Float64(1.0)]), PrimitiveTypes::Hlf, 0), + Ok(Value::Array(values)) if matches!(&values[0], Value::Float16(_)) + )); + assert!(matches!( + log2v_values(array(vec![Value::Float16(half::f16::ONE)]), PrimitiveTypes::Dbl, 1), + Ok(Value::Array(values)) if matches!(&values[0], Value::Float64(_)) + )); } #[test] fn validates_operands_and_preserves_stack() { - for value in [Value::Bool(false), array(vec![Value::Bool(false)])] { + for (value, found) in [ + (Value::Bool(false), "Boolean"), + (array(vec![Value::Int32(1)]), "Integer"), + (array(vec![Value::Bool(false)]), "Boolean"), + ] { + assert!(matches!( + log2v_values(value.clone(), PrimitiveTypes::Flt, 16), + Err(VMError::TypeMismatch { ip: 16, expected: "Float", found: actual }) + if actual == found + )); let mut stack = Stack::from_vec(vec![value]); let original = stack.clone(); assert!(log2v_func(&mut stack, PrimitiveTypes::Flt, 17).is_err()); assert_eq!(stack, original); } - assert!(log2v_values(array(vec![Value::Float32(1.0)]), PrimitiveTypes::Int, 18).is_err()); + let value = array(vec![Value::Float32(1.0)]); + assert!(matches!( + log2v_values(value.clone(), PrimitiveTypes::Int, 18), + Err(VMError::TypeMismatch { ip: 18, expected: "Float", found: "unknown" }) + )); + let mut stack = Stack::from_vec(vec![value]); + let original = stack.clone(); + assert!(log2v_func(&mut stack, PrimitiveTypes::Int, 18).is_err()); + assert_eq!(stack, original); } #[test] fn preserves_nan_behavior() {