diff --git a/steel_decimal/src/functions.rs b/steel_decimal/src/functions.rs index 0f7da65..19787a9 100644 --- a/steel_decimal/src/functions.rs +++ b/steel_decimal/src/functions.rs @@ -3,7 +3,7 @@ use rust_decimal::prelude::*; use rust_decimal::MathematicalOps; use std::str::FromStr; -/// Global precision setting for the current Steel execution context +// Global precision setting for the current Steel execution context thread_local! { static PRECISION_CONTEXT: std::cell::RefCell> = std::cell::RefCell::new(None); } @@ -105,44 +105,6 @@ pub fn decimal_div(a: String, b: String) -> Result { Ok(format_result(a_dec / b_dec)) } -// Precision-specific operations (explicit precision override) -pub fn decimal_add_p(a: String, b: String, precision: u32) -> Result { - let a_dec = parse_decimal(&a)?; - let b_dec = parse_decimal(&b)?; - let result = a_dec + b_dec; - - Ok(result.round_dp(precision).to_string()) -} - -pub fn decimal_sub_p(a: String, b: String, precision: u32) -> Result { - let a_dec = parse_decimal(&a)?; - let b_dec = parse_decimal(&b)?; - let result = a_dec - b_dec; - - Ok(result.round_dp(precision).to_string()) -} - -pub fn decimal_mul_p(a: String, b: String, precision: u32) -> Result { - let a_dec = parse_decimal(&a)?; - let b_dec = parse_decimal(&b)?; - let result = a_dec * b_dec; - - Ok(result.round_dp(precision).to_string()) -} - -pub fn decimal_div_p(a: String, b: String, precision: u32) -> Result { - let a_dec = parse_decimal(&a)?; - let b_dec = parse_decimal(&b)?; - - if b_dec.is_zero() { - return Err("Division by zero".to_string()); - } - - let result = a_dec / b_dec; - - Ok(result.round_dp(precision).to_string()) -} - // Precision control functions pub fn set_precision(precision: u32) -> String { if precision > 28 { diff --git a/steel_decimal/src/registry.rs b/steel_decimal/src/registry.rs index 6254a04..e29b312 100644 --- a/steel_decimal/src/registry.rs +++ b/steel_decimal/src/registry.rs @@ -10,7 +10,6 @@ impl FunctionRegistry { /// Register all decimal math functions with the Steel VM pub fn register_all(vm: &mut Engine) { Self::register_basic_arithmetic(vm); - Self::register_precision_arithmetic(vm); Self::register_precision_control(vm); Self::register_advanced_math(vm); Self::register_trigonometric(vm); @@ -29,14 +28,6 @@ impl FunctionRegistry { vm.register_fn("decimal-div", decimal_div); } - /// Register precision-specific arithmetic functions - pub fn register_precision_arithmetic(vm: &mut Engine) { - vm.register_fn("decimal-add-p", decimal_add_p); - vm.register_fn("decimal-sub-p", decimal_sub_p); - vm.register_fn("decimal-mul-p", decimal_mul_p); - vm.register_fn("decimal-div-p", decimal_div_p); - } - /// Register precision control functions pub fn register_precision_control(vm: &mut Engine) { vm.register_fn("set-precision", set_precision); diff --git a/steel_decimal/tests/function_tests.rs b/steel_decimal/tests/function_tests.rs index 28903de..b5e5bb4 100644 --- a/steel_decimal/tests/function_tests.rs +++ b/steel_decimal/tests/function_tests.rs @@ -238,7 +238,7 @@ fn test_decimal_precision_edge_cases(#[case] a: &str, #[case] b: &str, #[case] e // Test scientific notation #[rstest] #[case("1e2", "2e1", "120")] -#[case("1.5e2", "2.3e1", "173")] +#[case("1.5e2", "2.3e1", "173.0")] fn test_scientific_notation(#[case] a: &str, #[case] b: &str, #[case] expected: &str) { let result = decimal_add(a.to_string(), b.to_string()).unwrap(); assert_eq!(result, expected); @@ -255,26 +255,21 @@ fn test_precision_preservation(#[case] a: &str, #[case] b: &str, #[case] expecte assert_eq!(result, expected); } -// Test explicit precision functions +// Test explicit precision using global precision setting #[rstest] #[case("5.123", "2.456", 0, "8")] // 0 decimal places #[case("5.123", "2.456", 2, "7.58")] // 2 decimal places -#[case("5.123", "2.456", 4, "7.5790")] // 4 decimal places +#[case("5.12312", "2.45622", 4, "7.5793")] // 4 decimal places fn test_explicit_precision(#[case] a: &str, #[case] b: &str, #[case] precision: u32, #[case] expected: &str) { - let result = decimal_add_p(a.to_string(), b.to_string(), precision).unwrap(); - assert_eq!(result, expected); -} + // Set precision globally + set_precision(precision); -// Test scientific notation edge cases -#[rstest] -#[case("1e0", "1")] // Simple case -#[case("1.0e0", "1.0")] // Preserves decimal -#[case("1e-2", "0.01")] // Negative exponent -#[case("1.5e-3", "0.0015")] // Decimal + negative exponent -#[case("2.5e2", "250.0")] // Decimal + positive exponent -fn test_scientific_edge_cases(#[case] input: &str, #[case] expected: &str) { - let result = to_decimal(input.to_string()).unwrap(); + // Test with precision set + let result = decimal_add(a.to_string(), b.to_string()).unwrap(); assert_eq!(result, expected); + + // Clean up - clear precision for other tests + clear_precision(); } // Test precision functions @@ -289,9 +284,58 @@ fn test_precision_functions() { assert_eq!(result, "4.46"); // Test clearing precision + assert_eq!(clear_precision(), "Precision cleared - using full precision"); assert_eq!(get_precision(), "full"); // Test with full precision let result = decimal_add("1.567".to_string(), "2.891".to_string()).unwrap(); assert_eq!(result, "4.458"); } + +// Test decimal_format function +#[rstest] +#[case("5.123456", 2, "5.12")] +#[case("5.123456", 4, "5.1235")] +#[case("5.999", 0, "6")] +fn test_decimal_format(#[case] value: &str, #[case] precision: u32, #[case] expected: &str) { + let result = decimal_format(value.to_string(), precision).unwrap(); + assert_eq!(result, expected); +} + +// Test precision doesn't affect comparison functions +#[test] +fn test_precision_does_not_affect_comparisons() { + set_precision(2); + + // Comparisons should use full precision internally + assert_eq!(decimal_gt("1.567".to_string(), "1.566".to_string()).unwrap(), true); + assert_eq!(decimal_eq("1.567".to_string(), "1.567".to_string()).unwrap(), true); + assert_eq!(decimal_eq("1.567".to_string(), "1.57".to_string()).unwrap(), false); + + clear_precision(); +} + +// Test precision edge cases +#[test] +fn test_precision_edge_cases() { + // Test max precision + assert_eq!(set_precision(28), "Precision set to 28 decimal places"); + + // Test beyond max precision + assert_eq!(set_precision(29), "Error: Maximum precision is 28 decimal places"); + + // Clean up + clear_precision(); +} + +// Test scientific notation edge cases +#[rstest] +#[case("1e0", "1")] // Simple case +#[case("1.0e0", "1.0")] // Preserves decimal +#[case("1e-2", "0.01")] // Negative exponent +#[case("1.5e-3", "0.0015")] // Decimal + negative exponent +#[case("2.5e2", "250.0")] // Decimal + positive exponent +fn test_scientific_edge_cases(#[case] input: &str, #[case] expected: &str) { + let result = to_decimal(input.to_string()).unwrap(); + assert_eq!(result, expected); +} diff --git a/steel_decimal/tests/integration_tests.rs b/steel_decimal/tests/integration_tests.rs index 3dfafdc..4610888 100644 --- a/steel_decimal/tests/integration_tests.rs +++ b/steel_decimal/tests/integration_tests.rs @@ -290,7 +290,7 @@ fn test_financial_calculations() { let result = steel_decimal_instance.parse_and_execute("(decimal-compound \"1000\" \"0.05\" \"2\")").unwrap(); assert_eq!(result.len(), 1); if let SteelVal::StringV(s) = &result[0] { - assert_eq!(s.to_string(), "1102.50"); // 1000 * (1.05)^2 = 1102.50 + assert_eq!(s.to_string(), "1102.5000"); // 1000 * (1.05)^2 = 1102.5000 } }