diff --git a/README.md b/README.md index ad50663..09765ea 100644 --- a/README.md +++ b/README.md @@ -21,17 +21,17 @@ DeciMojo provides an arbitrary-precision decimal and integer mathematics library The core types are: - A base-10 arbitrary-precision signed integer type (`BigInt`) and a base-10 arbitrary-precision unsigned integer type (`BigUInt`) supporting unlimited digits[^integer]. It features comprehensive arithmetic operations, comparison functions, and supports extremely large integer calculations efficiently. -- An arbitrary-precision decimal implementation `BigDecimal` allowing for calculations with unlimited digits and decimal places[^arbitrary]. It provides a complete set of arithmetic operations, comparisons, and mathematical functions like logarithms, exponentiation, roots, trigonometric functions, etc. It also supports rounding modes and conversions to/from built-in types. -- A 128-bit fixed-point decimal implementation (`Decimal`) supporting up to 29 significant digits with a maximum of 28 decimal places[^fixed]. It features a complete set of mathematical functions including logarithms, exponentiation, roots, etc. +- An arbitrary-precision decimal implementation (`BigDecimal`) allowing for calculations with unlimited digits and decimal places[^arbitrary]. It provides a complete set of arithmetic operations, comparisons, and mathematical functions like logarithms, exponentiation, roots, trigonometric functions, etc. It also supports rounding modes and conversions to/from built-in types. +- A 128-bit fixed-point decimal implementation (`Decimal128`) supporting up to 29 significant digits with a maximum of 28 decimal places[^fixed]. It features a complete set of mathematical functions including logarithms, exponentiation, roots, etc. This repository includes [TOMLMojo](https://github.com/forfudan/decimojo/tree/main/src/tomlmojo), a lightweight TOML parser in pure Mojo. It parses configuration files and test data, supporting basic types, arrays, and nested tables. While created for DeciMojo's testing framework, it offers general-purpose structured data parsing with a clean, simple API. -| type | alias | information | internal representation | -| ------------ | ------- | ------------------------------------ | ----------------------------------- | -| `BigUInt` | `BUInt` | arbitrary-precision unsigned integer | `List[UInt32]` | -| `BigInt` | `BInt` | arbitrary-precision integer | `BigUInt`, `Bool` | -| `BigDecimal` | `BDec` | arbitrary-precision decimal | `BigUInt`, `Int`, `Bool` | -| `Decimal` | `Dec` | 128-bit fixed-precision decimal | `UInt32`,`UInt32`,`UInt32`,`UInt32` | +| type | alias | information | internal representation | +| ------------ | ----------------- | ------------------------------------ | ----------------------------------- | +| `BigUInt` | `BUInt` | arbitrary-precision unsigned integer | `List[UInt32]` | +| `BigInt` | `BInt` | arbitrary-precision integer | `BigUInt`, `Bool` | +| `BigDecimal` | `BDec`, `Decimal` | arbitrary-precision decimal | `BigUInt`, `Int`, `Bool` | +| `Decimal128` | `Dec128` | 128-bit fixed-precision decimal | `UInt32`,`UInt32`,`UInt32`,`UInt32` | ## Installation @@ -63,28 +63,30 @@ This will import the following types or aliases into your namespace: - `dm`: An alias for the `decimojo` module. - `BigInt` (alias `BInt`): An arbitrary-precision signed integer type. -- `BigDecimal` (alias `BDec`): An arbitrary-precision decimal type. -- `Decimal` (alias `Dec`): A 128-bit fixed-precision decimal type. +- `BigDecimal` (aliases `BDec` and `Decimal`): An arbitrary-precision decimal type. +- `Decimal128` (alias `Dec128`): A 128-bit fixed-precision decimal type. - `RoundingMode` (alias `RM`): An enumeration for rounding modes. - `ROUND_DOWN`, `ROUND_HALF_UP`, `ROUND_HALF_EVEN`, `ROUND_UP`: Constants for common rounding modes. --- -Here are some examples showcasing the arbitrary-precision feature of the `BigDecimal` type. Note that Mojo does not support global variables at the moment, so we need to pass the `precision` parameter explicitly to each function call. In future, we will add a global precision setting with the default value of, *e.g.*, `28`, to avoid passing it around. +Here are some examples showcasing the arbitrary-precision feature of the `BigDecimal` type (`BDec`). For some mathematical operations, the default precision (number of significant digits) is set to `36`. You can change the precision by passing the `precision` argument to the function. This default precision will be configurable globally in future when Mojo supports global variables. ```mojo from decimojo.prelude import * fn main() raises: - var a = BDec("123456789.123456789") - var b = BDec("1234.56789") + var a = BDec("123456789.123456789") # BDec is an alias for BigDecimal + var b = Decimal( + "1234.56789" + ) # Decimal is a Python-like alias for BigDecimal # === Basic Arithmetic === # print(a + b) # 123458023.691346789 print(a - b) # 123455554.555566789 print(a * b) # 152415787654.32099750190521 - print(a.true_divide(b, precision=80)) # 100000.0001 + print(a.true_divide(b + 1)) # 99919.0656560820700835791386582569736 # === Exponential Functions === # print(a.sqrt(precision=80)) @@ -122,7 +124,7 @@ fn main() raises: # === Internal representation of the number === # ( - BDec( + Decimal( "3.141592653589793238462643383279502884197169399375105820974944" ).power(2, precision=60) ).print_internal_representation() @@ -147,11 +149,10 @@ fn main() raises: --- -Here is a comprehensive quick-start guide showcasing each major function of the `BigInt` type. +Here is a comprehensive quick-start guide showcasing each major function of the `BigInt` type (`BInt`). ```mojo -from decimojo import BigInt, BInt -# BInt is an alias for BigInt +from decimojo.prelude import * fn main() raises: # === Construction === @@ -192,17 +193,17 @@ fn main() raises: --- -Here is a comprehensive quick-start guide showcasing each major function of the `Decimal` type. +Here is a comprehensive quick-start guide showcasing each major function of the `Decimal128` type (`Dec128`). ```mojo -from decimojo import Decimal, RoundingMode +from decimojo.prelude import * fn main() raises: # === Construction === - var a = Decimal("123.45") # From string - var b = Decimal(123) # From integer - var c = Decimal(123, 2) # Integer with scale (1.23) - var d = Decimal.from_float(3.14159) # From floating-point + var a = Dec128("123.45") # From string + var b = Dec128(123) # From integer + var c = Dec128(123, 2) # Integer with scale (1.23) + var d = Dec128.from_float(3.14159) # From floating-point # === Basic Arithmetic === print(a + b) # Addition: 246.45 @@ -212,14 +213,14 @@ fn main() raises: # === Rounding & Precision === print(a.round(1)) # Round to 1 decimal place: 123.5 - print(a.quantize(Decimal("0.01"))) # Format to 2 decimal places: 123.45 + print(a.quantize(Dec128("0.01"))) # Format to 2 decimal places: 123.45 print(a.round(0, RoundingMode.ROUND_DOWN)) # Round down to integer: 123 # === Comparison === print(a > b) # Greater than: True - print(a == Decimal("123.45")) # Equality: True + print(a == Dec128("123.45")) # Equality: True print(a.is_zero()) # Check for zero: False - print(Decimal("0").is_zero()) # Check for zero: True + print(Dec128("0").is_zero()) # Check for zero: True # === Type Conversions === print(Float64(a)) # To float: 123.45 @@ -229,34 +230,32 @@ fn main() raises: print(a.scale()) # Get scale: 2 # === Mathematical Functions === - print(Decimal("2").sqrt()) # Square root: 1.4142135623730950488016887242 - print(Decimal("100").root(3)) # Cube root: 4.641588833612778892410076351 - print(Decimal("2.71828").ln()) # Natural log: 0.9999993273472820031578910056 - print(Decimal("10").log10()) # Base-10 log: 1 - print(Decimal("16").log(Decimal("2"))) # Log base 2: 3.9999999999999999999999999999 - print(Decimal("10").exp()) # e^10: 22026.465794806716516957900645 - print(Decimal("2").power(10)) # Power: 1024 + print(Dec128("2").sqrt()) # Square root: 1.4142135623730950488016887242 + print(Dec128("100").root(3)) # Cube root: 4.641588833612778892410076351 + print(Dec128("2.71828").ln()) # Natural log: 0.9999993273472820031578910056 + print(Dec128("10").log10()) # Base-10 log: 1 + print(Dec128("16").log(Dec128("2"))) # Log base 2: 3.9999999999999999999999999999 + print(Dec128("10").exp()) # e^10: 22026.465794806716516957900645 + print(Dec128("2").power(10)) # Power: 1024 # === Sign Handling === print(-a) # Negation: -123.45 - print(abs(Decimal("-123.45"))) # Absolute value: 123.45 - print(Decimal("123.45").is_negative()) # Check if negative: False + print(abs(Dec128("-123.45"))) # Absolute value: 123.45 + print(Dec128("123.45").is_negative()) # Check if negative: False # === Special Values === - print(Decimal.PI()) # π constant: 3.1415926535897932384626433833 - print(Decimal.E()) # e constant: 2.7182818284590452353602874714 - print(Decimal.ONE()) # Value 1: 1 - print(Decimal.ZERO()) # Value 0: 0 - print(Decimal.MAX()) # Maximum value: 79228162514264337593543950335 + print(Dec128.PI()) # π constant: 3.1415926535897932384626433833 + print(Dec128.E()) # e constant: 2.7182818284590452353602874714 + print(Dec128.ONE()) # Value 1: 1 + print(Dec128.ZERO()) # Value 0: 0 + print(Dec128.MAX()) # Maximum value: 79228162514264337593543950335 # === Convenience Methods === - print(Decimal("123.400").is_integer()) # Check if integer: False + print(Dec128("123.400").is_integer()) # Check if integer: False print(a.number_of_significant_digits()) # Count significant digits: 5 - print(Decimal("12.34").to_str_scientific()) # Scientific notation: 1.234E+1 + print(Dec128("12.34").to_str_scientific()) # Scientific notation: 1.234E+1 ``` -[Click here for 8 key examples](https://zhuyuhao.com/decimojo/docs/examples) highlighting the most important features of the `Decimal` type. - ## Objective Financial calculations and data analysis require precise decimal arithmetic that floating-point numbers cannot reliably provide. As someone working in finance and credit risk model validation, I needed a dependable correctly-rounded, fixed-precision numeric type when migrating my personal projects from Python to Mojo. @@ -284,7 +283,7 @@ Regular benchmarks against Python's `decimal` module are available in the `bench After cloning the repo onto your local disk, you can: - Use `pixi run test` to run tests. -- Use `pixi run bench_decimal` to generate logs for benchmarking tests against `python.decimal` module. The log files are saved in `benches/decimal/logs/`. +- Use `pixi run bdec` to generate logs for benchmarking tests against `python.decimal` module. The log files are saved in `benches/bigdecimal/logs/`. ## Citation @@ -305,6 +304,6 @@ If you find DeciMojo useful for your research, consider listing it in your citat This repository and its contributions are licensed under the Apache License v2.0. -[^fixed]: The `Decimal` type can represent values with up to 29 significant digits and a maximum of 28 digits after the decimal point. When a value exceeds the maximum representable value (`2^96 - 1`), DeciMojo either raises an error or rounds the value to fit within these constraints. For example, the significant digits of `8.8888888888888888888888888888` (29 eights total with 28 after the decimal point) exceeds the maximum representable value (`2^96 - 1`) and is automatically rounded to `8.888888888888888888888888889` (28 eights total with 27 after the decimal point). DeciMojo's `Decimal` type is similar to `System.Decimal` (C#/.NET), `rust_decimal` in Rust, `DECIMAL/NUMERIC` in SQL Server, etc. +[^fixed]: The `Decimal128` type can represent values with up to 29 significant digits and a maximum of 28 digits after the decimal point. When a value exceeds the maximum representable value (`2^96 - 1`), DeciMojo either raises an error or rounds the value to fit within these constraints. For example, the significant digits of `8.8888888888888888888888888888` (29 eights total with 28 after the decimal point) exceeds the maximum representable value (`2^96 - 1`) and is automatically rounded to `8.888888888888888888888888889` (28 eights total with 27 after the decimal point). DeciMojo's `Decimal128` type is similar to `System.Decimal` (C#/.NET), `rust_decimal` in Rust, `DECIMAL/NUMERIC` in SQL Server, etc. [^integer]: The BigInt implementation uses a base-10 representation for users (maintaining decimal semantics), while internally using an optimized base-10^9 storage system for efficient calculations. This approach balances human-readable decimal operations with high-performance computing. It provides both floor division (round toward negative infinity) and truncate division (round toward zero) semantics, enabling precise handling of division operations with correct mathematical behavior regardless of operand signs. [^arbitrary]: Built on top of our completed BigInt implementation, BigDecimal will support arbitrary precision for both the integer and fractional parts, similar to `decimal` and `mpmath` in Python, `java.math.BigDecimal` in Java, etc. diff --git a/benches/decimal/bench.mojo b/benches/decimal128/bench.mojo similarity index 100% rename from benches/decimal/bench.mojo rename to benches/decimal128/bench.mojo diff --git a/benches/decimal/bench_add.mojo b/benches/decimal128/bench_add.mojo similarity index 70% rename from benches/decimal/bench_add.mojo rename to benches/decimal128/bench_add.mojo index d584762..d826b99 100644 --- a/benches/decimal/bench_add.mojo +++ b/benches/decimal128/bench_add.mojo @@ -1,9 +1,9 @@ """ -Comprehensive benchmarks for Decimal addition operations. +Comprehensive benchmarks for Decimal128 addition operations. Compares performance against Python's decimal module. """ -from decimojo.prelude import dm, Decimal, RoundingMode +from decimojo.prelude import dm, Decimal128, RoundingMode from python import Python, PythonObject from time import perf_counter_ns import time @@ -48,22 +48,22 @@ fn log_print(msg: String, log_file: PythonObject) raises: fn run_benchmark( name: String, - a_mojo: Decimal, - b_mojo: Decimal, + a_mojo: Decimal128, + b_mojo: Decimal128, a_py: PythonObject, b_py: PythonObject, iterations: Int, log_file: PythonObject, ) raises: """ - Run a benchmark comparing Mojo Decimal addition with Python Decimal addition. + Run a benchmark comparing Mojo Dec128 addition with Python decimal addition. Args: name: Name of the benchmark case. - a_mojo: First Mojo Decimal operand. - b_mojo: Second Mojo Decimal operand. - a_py: First Python Decimal operand. - b_py: Second Python Decimal operand. + a_mojo: First Mojo Dec128 operand. + b_mojo: Second Mojo Dec128 operand. + a_py: First Python decimal operand. + b_py: Second Python decimal operand. iterations: Number of 1000-iteration runs to average. log_file: File object for logging results. """ @@ -92,11 +92,11 @@ fn run_benchmark( # Print results with speedup comparison log_print( - "Mojo Decimal: " + String(mojo_time) + " ns per iterations", + "Mojo decimal: " + String(mojo_time) + " ns per iterations", log_file, ) log_print( - "Python Decimal: " + String(python_time) + " ns per iteration", + "Python decimal: " + String(python_time) + " ns per iteration", log_file, ) log_print("Speedup factor: " + String(python_time / mojo_time), log_file) @@ -137,7 +137,9 @@ fn main() raises: "Python decimal precision: " + String(pydecimal.getcontext().prec), log_file, ) - log_print("Mojo decimal precision: " + String(Decimal.MAX_SCALE), log_file) + log_print( + "Mojo decimal precision: " + String(Decimal128.MAX_SCALE), log_file + ) # Define benchmark cases log_print( @@ -148,10 +150,10 @@ fn main() raises: ) # Case 1: Simple integers - var case1_a_mojo = Decimal("12345") - var case1_b_mojo = Decimal("67890") - var case1_a_py = pydecimal.Decimal("12345") - var case1_b_py = pydecimal.Decimal("67890") + var case1_a_mojo = Decimal128("12345") + var case1_b_mojo = Decimal128("67890") + var case1_a_py = pydecimal.Decimal128("12345") + var case1_b_py = pydecimal.Decimal128("67890") run_benchmark( "Simple integers", case1_a_mojo, @@ -163,10 +165,10 @@ fn main() raises: ) # Case 2: Simple decimals (few decimal places) - var case2_a_mojo = Decimal("123.45") - var case2_b_mojo = Decimal("67.89") - var case2_a_py = pydecimal.Decimal("123.45") - var case2_b_py = pydecimal.Decimal("67.89") + var case2_a_mojo = Decimal128("123.45") + var case2_b_mojo = Decimal128("67.89") + var case2_a_py = pydecimal.Decimal128("123.45") + var case2_b_py = pydecimal.Decimal128("67.89") run_benchmark( "Simple decimals", case2_a_mojo, @@ -178,10 +180,10 @@ fn main() raises: ) # Case 3: High-precision decimals - var case3_a_mojo = Decimal("0.123456789012345678901234567") - var case3_b_mojo = Decimal("0.987654321098765432109876543") - var case3_a_py = pydecimal.Decimal("0.123456789012345678901234567") - var case3_b_py = pydecimal.Decimal("0.987654321098765432109876543") + var case3_a_mojo = Decimal128("0.123456789012345678901234567") + var case3_b_mojo = Decimal128("0.987654321098765432109876543") + var case3_a_py = pydecimal.Decimal128("0.123456789012345678901234567") + var case3_b_py = pydecimal.Decimal128("0.987654321098765432109876543") run_benchmark( "High-precision decimals", case3_a_mojo, @@ -193,10 +195,10 @@ fn main() raises: ) # Case 4: Different scales - var case4_a_mojo = Decimal("123.4") - var case4_b_mojo = Decimal("67.89") - var case4_a_py = pydecimal.Decimal("123.4") - var case4_b_py = pydecimal.Decimal("67.89") + var case4_a_mojo = Decimal128("123.4") + var case4_b_mojo = Decimal128("67.89") + var case4_a_py = pydecimal.Decimal128("123.4") + var case4_b_py = pydecimal.Decimal128("67.89") run_benchmark( "Different scales", case4_a_mojo, @@ -208,10 +210,10 @@ fn main() raises: ) # Case 5: Numbers requiring carrying - var case5_a_mojo = Decimal("999.99") - var case5_b_mojo = Decimal("0.01") - var case5_a_py = pydecimal.Decimal("999.99") - var case5_b_py = pydecimal.Decimal("0.01") + var case5_a_mojo = Decimal128("999.99") + var case5_b_mojo = Decimal128("0.01") + var case5_a_py = pydecimal.Decimal128("999.99") + var case5_b_py = pydecimal.Decimal128("0.01") run_benchmark( "Numbers requiring carrying", case5_a_mojo, @@ -223,10 +225,10 @@ fn main() raises: ) # Case 6: Very large numbers - var case6_a_mojo = Decimal("79228162514264337593543950334") # MAX - 1 - var case6_b_mojo = Decimal("1") - var case6_a_py = pydecimal.Decimal("79228162514264337593543950334") - var case6_b_py = pydecimal.Decimal("1") + var case6_a_mojo = Decimal128("79228162514264337593543950334") # MAX - 1 + var case6_b_mojo = Decimal128("1") + var case6_a_py = pydecimal.Decimal128("79228162514264337593543950334") + var case6_b_py = pydecimal.Decimal128("1") run_benchmark( "Very large numbers", case6_a_mojo, @@ -238,12 +240,12 @@ fn main() raises: ) # Case 7: Very small numbers - var case7_a_mojo = Decimal( + var case7_a_mojo = Decimal128( "0." + "0" * 27 + "1" ) # Smallest positive decimal - var case7_b_mojo = Decimal("0." + "0" * 27 + "2") - var case7_a_py = pydecimal.Decimal("0." + "0" * 27 + "1") - var case7_b_py = pydecimal.Decimal("0." + "0" * 27 + "2") + var case7_b_mojo = Decimal128("0." + "0" * 27 + "2") + var case7_a_py = pydecimal.Decimal128("0." + "0" * 27 + "1") + var case7_b_py = pydecimal.Decimal128("0." + "0" * 27 + "2") run_benchmark( "Very small numbers", case7_a_mojo, @@ -255,10 +257,10 @@ fn main() raises: ) # Case 8: Addition with zero - var case8_a_mojo = Decimal("123.456") - var case8_b_mojo = Decimal("0") - var case8_a_py = pydecimal.Decimal("123.456") - var case8_b_py = pydecimal.Decimal("0") + var case8_a_mojo = Decimal128("123.456") + var case8_b_mojo = Decimal128("0") + var case8_a_py = pydecimal.Decimal128("123.456") + var case8_b_py = pydecimal.Decimal128("0") run_benchmark( "Addition with zero", case8_a_mojo, @@ -270,10 +272,10 @@ fn main() raises: ) # Case 9: Negative numbers - var case9_a_mojo = Decimal("123.45") - var case9_b_mojo = Decimal("-67.89") - var case9_a_py = pydecimal.Decimal("123.45") - var case9_b_py = pydecimal.Decimal("-67.89") + var case9_a_mojo = Decimal128("123.45") + var case9_b_mojo = Decimal128("-67.89") + var case9_a_py = pydecimal.Decimal128("123.45") + var case9_b_py = pydecimal.Decimal128("-67.89") run_benchmark( "Negative numbers", case9_a_mojo, @@ -285,10 +287,10 @@ fn main() raises: ) # Case 10: Addition resulting in zero - var case10_a_mojo = Decimal("123.45") - var case10_b_mojo = Decimal("-123.45") - var case10_a_py = pydecimal.Decimal("123.45") - var case10_b_py = pydecimal.Decimal("-123.45") + var case10_a_mojo = Decimal128("123.45") + var case10_b_mojo = Decimal128("-123.45") + var case10_a_py = pydecimal.Decimal128("123.45") + var case10_b_py = pydecimal.Decimal128("-123.45") run_benchmark( "Addition resulting in zero", case10_a_mojo, diff --git a/benches/decimal/bench_comparison.mojo b/benches/decimal128/bench_comparison.mojo similarity index 69% rename from benches/decimal/bench_comparison.mojo rename to benches/decimal128/bench_comparison.mojo index 427c5a9..5d1cf9a 100644 --- a/benches/decimal/bench_comparison.mojo +++ b/benches/decimal128/bench_comparison.mojo @@ -1,10 +1,10 @@ """ -Comprehensive benchmarks for Decimal logical comparison operations. +Comprehensive benchmarks for Decimal128 logical comparison operations. Compares performance against Python's decimal module across diverse test cases. Tests all comparison operators: >, >=, ==, <=, <, != """ -from decimojo.prelude import dm, Decimal, RoundingMode +from decimojo.prelude import dm, Decimal128, RoundingMode from python import Python, PythonObject from time import perf_counter_ns import time @@ -50,8 +50,8 @@ fn log_print(msg: String, log_file: PythonObject) raises: fn run_comparison_benchmark( name: String, - a_mojo: Decimal, - b_mojo: Decimal, + a_mojo: Decimal128, + b_mojo: Decimal128, a_py: PythonObject, b_py: PythonObject, op: String, @@ -60,14 +60,14 @@ fn run_comparison_benchmark( mut speedup_factors: List[Float64], ) raises: """ - Run a benchmark comparing Mojo Decimal comparison with Python Decimal comparison. + Run a benchmark comparing Mojo Dec128 comparison with Python decimal comparison. Args: name: Name of the benchmark case. - a_mojo: First Mojo Decimal operand. - b_mojo: Second Mojo Decimal operand. - a_py: First Python Decimal operand. - b_py: Second Python Decimal operand. + a_mojo: First Mojo Dec128 operand. + b_mojo: Second Mojo Dec128 operand. + a_py: First Python decimal operand. + b_py: Second Python decimal operand. op: Comparison operator as string (">", ">=", "==", "<=", "<", "!="). iterations: Number of iterations for the benchmark. log_file: File object for logging results. @@ -152,11 +152,11 @@ fn run_comparison_benchmark( # Print results with speedup comparison log_print( - "Mojo Decimal: " + String(mojo_time) + " ns per operation", + "Mojo decimal: " + String(mojo_time) + " ns per operation", log_file, ) log_print( - "Python Decimal: " + String(python_time) + " ns per operation", + "Python decimal: " + String(python_time) + " ns per operation", log_file, ) log_print("Speedup factor: " + String(speedup), log_file) @@ -200,7 +200,9 @@ fn main() raises: "Python decimal precision: " + String(pydecimal.getcontext().prec), log_file, ) - log_print("Mojo decimal precision: " + String(Decimal.MAX_SCALE), log_file) + log_print( + "Mojo decimal precision: " + String(Decimal128.MAX_SCALE), log_file + ) # Define benchmark cases log_print( @@ -211,10 +213,10 @@ fn main() raises: ) # Test Case 1: Equal integers - var case1_a_mojo = Decimal("100") - var case1_b_mojo = Decimal("100") - var case1_a_py = pydecimal.Decimal("100") - var case1_b_py = pydecimal.Decimal("100") + var case1_a_mojo = Decimal128("100") + var case1_b_mojo = Decimal128("100") + var case1_a_py = pydecimal.Decimal128("100") + var case1_b_py = pydecimal.Decimal128("100") run_comparison_benchmark( "Equal integers", case1_a_mojo, @@ -228,10 +230,10 @@ fn main() raises: ) # Test Case 2: Different integers - var case2_a_mojo = Decimal("100") - var case2_b_mojo = Decimal("200") - var case2_a_py = pydecimal.Decimal("100") - var case2_b_py = pydecimal.Decimal("200") + var case2_a_mojo = Decimal128("100") + var case2_b_mojo = Decimal128("200") + var case2_a_py = pydecimal.Decimal128("100") + var case2_b_py = pydecimal.Decimal128("200") run_comparison_benchmark( "Different integers (<)", case2_a_mojo, @@ -258,10 +260,10 @@ fn main() raises: ) # Test Case 4: Equal decimals with different representations - var case4_a_mojo = Decimal("100.00") - var case4_b_mojo = Decimal("100") - var case4_a_py = pydecimal.Decimal("100.00") - var case4_b_py = pydecimal.Decimal("100") + var case4_a_mojo = Decimal128("100.00") + var case4_b_mojo = Decimal128("100") + var case4_a_py = pydecimal.Decimal128("100.00") + var case4_b_py = pydecimal.Decimal128("100") run_comparison_benchmark( "Equal decimal with different scales", case4_a_mojo, @@ -275,10 +277,10 @@ fn main() raises: ) # Test Case 5: Compare with zero - var case5_a_mojo = Decimal("0") - var case5_b_mojo = Decimal("-0.00") - var case5_a_py = pydecimal.Decimal("0") - var case5_b_py = pydecimal.Decimal("-0.00") + var case5_a_mojo = Decimal128("0") + var case5_b_mojo = Decimal128("-0.00") + var case5_a_py = pydecimal.Decimal128("0") + var case5_b_py = pydecimal.Decimal128("-0.00") run_comparison_benchmark( "Zero comparison (==)", case5_a_mojo, @@ -292,10 +294,10 @@ fn main() raises: ) # Test Case 6: Very small difference - var case6_a_mojo = Decimal("0.0000000000000000000000000001") - var case6_b_mojo = Decimal("0.0000000000000000000000000002") - var case6_a_py = pydecimal.Decimal("0.0000000000000000000000000001") - var case6_b_py = pydecimal.Decimal("0.0000000000000000000000000002") + var case6_a_mojo = Decimal128("0.0000000000000000000000000001") + var case6_b_mojo = Decimal128("0.0000000000000000000000000002") + var case6_a_py = pydecimal.Decimal128("0.0000000000000000000000000001") + var case6_b_py = pydecimal.Decimal128("0.0000000000000000000000000002") run_comparison_benchmark( "Very small difference (<)", case6_a_mojo, @@ -309,10 +311,10 @@ fn main() raises: ) # Test Case 7: Very large numbers - var case7_a_mojo = Decimal("9999999999999999999999999999") - var case7_b_mojo = Decimal("9999999999999999999999999998") - var case7_a_py = pydecimal.Decimal("9999999999999999999999999999") - var case7_b_py = pydecimal.Decimal("9999999999999999999999999998") + var case7_a_mojo = Decimal128("9999999999999999999999999999") + var case7_b_mojo = Decimal128("9999999999999999999999999998") + var case7_a_py = pydecimal.Decimal128("9999999999999999999999999999") + var case7_b_py = pydecimal.Decimal128("9999999999999999999999999998") run_comparison_benchmark( "Very large numbers (>)", case7_a_mojo, @@ -326,10 +328,10 @@ fn main() raises: ) # Test Case 8: Negative numbers - var case8_a_mojo = Decimal("-10") - var case8_b_mojo = Decimal("-20") - var case8_a_py = pydecimal.Decimal("-10") - var case8_b_py = pydecimal.Decimal("-20") + var case8_a_mojo = Decimal128("-10") + var case8_b_mojo = Decimal128("-20") + var case8_a_py = pydecimal.Decimal128("-10") + var case8_b_py = pydecimal.Decimal128("-20") run_comparison_benchmark( "Negative numbers (>)", case8_a_mojo, @@ -343,10 +345,10 @@ fn main() raises: ) # Test Case 9: Mixed sign comparison - var case9_a_mojo = Decimal("-10") - var case9_b_mojo = Decimal("10") - var case9_a_py = pydecimal.Decimal("-10") - var case9_b_py = pydecimal.Decimal("10") + var case9_a_mojo = Decimal128("-10") + var case9_b_mojo = Decimal128("10") + var case9_a_py = pydecimal.Decimal128("-10") + var case9_b_py = pydecimal.Decimal128("10") run_comparison_benchmark( "Mixed signs (<)", case9_a_mojo, @@ -360,10 +362,10 @@ fn main() raises: ) # Test Case 10: Not equal comparison - var case10_a_mojo = Decimal("99.99") - var case10_b_mojo = Decimal("100") - var case10_a_py = pydecimal.Decimal("99.99") - var case10_b_py = pydecimal.Decimal("100") + var case10_a_mojo = Decimal128("99.99") + var case10_b_mojo = Decimal128("100") + var case10_a_py = pydecimal.Decimal128("99.99") + var case10_b_py = pydecimal.Decimal128("100") run_comparison_benchmark( "Not equal (!=)", case10_a_mojo, @@ -377,10 +379,10 @@ fn main() raises: ) # Test Case 11: Less than or equal (true because less) - var case11_a_mojo = Decimal("50") - var case11_b_mojo = Decimal("100") - var case11_a_py = pydecimal.Decimal("50") - var case11_b_py = pydecimal.Decimal("100") + var case11_a_mojo = Decimal128("50") + var case11_b_mojo = Decimal128("100") + var case11_a_py = pydecimal.Decimal128("50") + var case11_b_py = pydecimal.Decimal128("100") run_comparison_benchmark( "Less than or equal (<=, true because less)", case11_a_mojo, @@ -394,10 +396,10 @@ fn main() raises: ) # Test Case 12: Less than or equal (true because equal) - var case12_a_mojo = Decimal("100") - var case12_b_mojo = Decimal("100") - var case12_a_py = pydecimal.Decimal("100") - var case12_b_py = pydecimal.Decimal("100") + var case12_a_mojo = Decimal128("100") + var case12_b_mojo = Decimal128("100") + var case12_a_py = pydecimal.Decimal128("100") + var case12_b_py = pydecimal.Decimal128("100") run_comparison_benchmark( "Less than or equal (<=, true because equal)", case12_a_mojo, @@ -411,10 +413,10 @@ fn main() raises: ) # Test Case 13: Greater than or equal (true because greater) - var case13_a_mojo = Decimal("200") - var case13_b_mojo = Decimal("100") - var case13_a_py = pydecimal.Decimal("200") - var case13_b_py = pydecimal.Decimal("100") + var case13_a_mojo = Decimal128("200") + var case13_b_mojo = Decimal128("100") + var case13_a_py = pydecimal.Decimal128("200") + var case13_b_py = pydecimal.Decimal128("100") run_comparison_benchmark( "Greater than or equal (>=, true because greater)", case13_a_mojo, @@ -428,10 +430,10 @@ fn main() raises: ) # Test Case 14: Greater than or equal (true because equal) - var case14_a_mojo = Decimal("100") - var case14_b_mojo = Decimal("100") - var case14_a_py = pydecimal.Decimal("100") - var case14_b_py = pydecimal.Decimal("100") + var case14_a_mojo = Decimal128("100") + var case14_b_mojo = Decimal128("100") + var case14_a_py = pydecimal.Decimal128("100") + var case14_b_py = pydecimal.Decimal128("100") run_comparison_benchmark( "Greater than or equal (>=, true because equal)", case14_a_mojo, @@ -445,10 +447,10 @@ fn main() raises: ) # Test Case 15: Equal with high precision after decimal - var case15_a_mojo = Decimal("0.12345678901234567890123456789") - var case15_b_mojo = Decimal("0.12345678901234567890123456789") - var case15_a_py = pydecimal.Decimal("0.12345678901234567890123456789") - var case15_b_py = pydecimal.Decimal("0.12345678901234567890123456789") + var case15_a_mojo = Decimal128("0.12345678901234567890123456789") + var case15_b_mojo = Decimal128("0.12345678901234567890123456789") + var case15_a_py = pydecimal.Decimal128("0.12345678901234567890123456789") + var case15_b_py = pydecimal.Decimal128("0.12345678901234567890123456789") run_comparison_benchmark( "Equal high precision numbers", case15_a_mojo, @@ -462,10 +464,10 @@ fn main() raises: ) # Test Case 16: Almost equal high precision - var case16_a_mojo = Decimal("0.12345678901234567890123456780") - var case16_b_mojo = Decimal("0.12345678901234567890123456789") - var case16_a_py = pydecimal.Decimal("0.12345678901234567890123456780") - var case16_b_py = pydecimal.Decimal("0.12345678901234567890123456789") + var case16_a_mojo = Decimal128("0.12345678901234567890123456780") + var case16_b_mojo = Decimal128("0.12345678901234567890123456789") + var case16_a_py = pydecimal.Decimal128("0.12345678901234567890123456780") + var case16_b_py = pydecimal.Decimal128("0.12345678901234567890123456789") run_comparison_benchmark( "Almost equal high precision (<)", case16_a_mojo, @@ -479,10 +481,10 @@ fn main() raises: ) # Test Case 17: Equal but different trailing zeros - var case17_a_mojo = Decimal("1.10000") - var case17_b_mojo = Decimal("1.1") - var case17_a_py = pydecimal.Decimal("1.10000") - var case17_b_py = pydecimal.Decimal("1.1") + var case17_a_mojo = Decimal128("1.10000") + var case17_b_mojo = Decimal128("1.1") + var case17_a_py = pydecimal.Decimal128("1.10000") + var case17_b_py = pydecimal.Decimal128("1.1") run_comparison_benchmark( "Equal with different trailing zeros", case17_a_mojo, @@ -496,10 +498,10 @@ fn main() raises: ) # Test Case 18: Not equal with trailing zeros that matter - var case18_a_mojo = Decimal("1.10001") - var case18_b_mojo = Decimal("1.1") - var case18_a_py = pydecimal.Decimal("1.10001") - var case18_b_py = pydecimal.Decimal("1.1") + var case18_a_mojo = Decimal128("1.10001") + var case18_b_mojo = Decimal128("1.1") + var case18_a_py = pydecimal.Decimal128("1.10001") + var case18_b_py = pydecimal.Decimal128("1.1") run_comparison_benchmark( "Not equal with significant trailing digits", case18_a_mojo, @@ -513,10 +515,10 @@ fn main() raises: ) # Test Case 19: Large positive vs small negative - var case19_a_mojo = Decimal("9999999") - var case19_b_mojo = Decimal("-0.000001") - var case19_a_py = pydecimal.Decimal("9999999") - var case19_b_py = pydecimal.Decimal("-0.000001") + var case19_a_mojo = Decimal128("9999999") + var case19_b_mojo = Decimal128("-0.000001") + var case19_a_py = pydecimal.Decimal128("9999999") + var case19_b_py = pydecimal.Decimal128("-0.000001") run_comparison_benchmark( "Large positive vs small negative (>)", case19_a_mojo, @@ -530,10 +532,10 @@ fn main() raises: ) # Test Case 20: Equal near zero - var case20_a_mojo = Decimal("0.000000000000000000000000001") - var case20_b_mojo = Decimal("0.000000000000000000000000001") - var case20_a_py = pydecimal.Decimal("0.000000000000000000000000001") - var case20_b_py = pydecimal.Decimal("0.000000000000000000000000001") + var case20_a_mojo = Decimal128("0.000000000000000000000000001") + var case20_b_mojo = Decimal128("0.000000000000000000000000001") + var case20_a_py = pydecimal.Decimal128("0.000000000000000000000000001") + var case20_b_py = pydecimal.Decimal128("0.000000000000000000000000001") run_comparison_benchmark( "Equal near zero (==)", case20_a_mojo, @@ -547,10 +549,10 @@ fn main() raises: ) # Test Case 21: Common financial values - var case21_a_mojo = Decimal("19.99") - var case21_b_mojo = Decimal("20.00") - var case21_a_py = pydecimal.Decimal("19.99") - var case21_b_py = pydecimal.Decimal("20.00") + var case21_a_mojo = Decimal128("19.99") + var case21_b_mojo = Decimal128("20.00") + var case21_a_py = pydecimal.Decimal128("19.99") + var case21_b_py = pydecimal.Decimal128("20.00") run_comparison_benchmark( "Common financial values (<)", case21_a_mojo, @@ -564,10 +566,10 @@ fn main() raises: ) # Test Case 22: Different sign zeros - var case22_a_mojo = Decimal("0") - var case22_b_mojo = Decimal("-0") - var case22_a_py = pydecimal.Decimal("0") - var case22_b_py = pydecimal.Decimal("-0") + var case22_a_mojo = Decimal128("0") + var case22_b_mojo = Decimal128("-0") + var case22_a_py = pydecimal.Decimal128("0") + var case22_b_py = pydecimal.Decimal128("-0") run_comparison_benchmark( "Different sign zeros (==)", case22_a_mojo, @@ -581,10 +583,10 @@ fn main() raises: ) # Test Case 23: Repeated digits comparison - var case23_a_mojo = Decimal("9.999999999") - var case23_b_mojo = Decimal("10") - var case23_a_py = pydecimal.Decimal("9.999999999") - var case23_b_py = pydecimal.Decimal("10") + var case23_a_mojo = Decimal128("9.999999999") + var case23_b_mojo = Decimal128("10") + var case23_a_py = pydecimal.Decimal128("9.999999999") + var case23_b_py = pydecimal.Decimal128("10") run_comparison_benchmark( "Repeated digits comparison (<)", case23_a_mojo, @@ -598,10 +600,10 @@ fn main() raises: ) # Test Case 24: Scientific notation equivalent - var case24_a_mojo = Decimal("1.23e2") - var case24_b_mojo = Decimal("123") - var case24_a_py = pydecimal.Decimal("1.23e2") - var case24_b_py = pydecimal.Decimal("123") + var case24_a_mojo = Decimal128("1.23e2") + var case24_b_mojo = Decimal128("123") + var case24_a_py = pydecimal.Decimal128("1.23e2") + var case24_b_py = pydecimal.Decimal128("123") run_comparison_benchmark( "Scientific notation equivalent (==)", case24_a_mojo, @@ -615,10 +617,10 @@ fn main() raises: ) # Test Case 25: Same value different format - var case25_a_mojo = Decimal("100.00") - var case25_b_mojo = Decimal("1.0e2") - var case25_a_py = pydecimal.Decimal("100.00") - var case25_b_py = pydecimal.Decimal("1.0e2") + var case25_a_mojo = Decimal128("100.00") + var case25_b_mojo = Decimal128("1.0e2") + var case25_a_py = pydecimal.Decimal128("100.00") + var case25_b_py = pydecimal.Decimal128("1.0e2") run_comparison_benchmark( "Same value different format (==)", case25_a_mojo, @@ -632,10 +634,10 @@ fn main() raises: ) # Test Case 26: Almost but not quite equal - var case26_a_mojo = Decimal("0.999999999999999999999") - var case26_b_mojo = Decimal("1.000000000000000000000") - var case26_a_py = pydecimal.Decimal("0.999999999999999999999") - var case26_b_py = pydecimal.Decimal("1.000000000000000000000") + var case26_a_mojo = Decimal128("0.999999999999999999999") + var case26_b_mojo = Decimal128("1.000000000000000000000") + var case26_a_py = pydecimal.Decimal128("0.999999999999999999999") + var case26_b_py = pydecimal.Decimal128("1.000000000000000000000") run_comparison_benchmark( "Almost but not quite equal values (<)", case26_a_mojo, @@ -649,10 +651,10 @@ fn main() raises: ) # Test Case 27: Greater than comparison (negative numbers) - var case27_a_mojo = Decimal("-100") - var case27_b_mojo = Decimal("-200") - var case27_a_py = pydecimal.Decimal("-100") - var case27_b_py = pydecimal.Decimal("-200") + var case27_a_mojo = Decimal128("-100") + var case27_b_mojo = Decimal128("-200") + var case27_a_py = pydecimal.Decimal128("-100") + var case27_b_py = pydecimal.Decimal128("-200") run_comparison_benchmark( "Greater than with negative numbers (>)", case27_a_mojo, @@ -666,10 +668,10 @@ fn main() raises: ) # Test Case 28: Equal negative values - var case28_a_mojo = Decimal("-42.5") - var case28_b_mojo = Decimal("-42.50") - var case28_a_py = pydecimal.Decimal("-42.5") - var case28_b_py = pydecimal.Decimal("-42.50") + var case28_a_mojo = Decimal128("-42.5") + var case28_b_mojo = Decimal128("-42.50") + var case28_a_py = pydecimal.Decimal128("-42.5") + var case28_b_py = pydecimal.Decimal128("-42.50") run_comparison_benchmark( "Equal negative values (==)", case28_a_mojo, @@ -683,10 +685,10 @@ fn main() raises: ) # Test Case 29: Close to zero comparison - var case29_a_mojo = Decimal("0.0000000000000000000000000001") - var case29_b_mojo = Decimal("0") - var case29_a_py = pydecimal.Decimal("0.0000000000000000000000000001") - var case29_b_py = pydecimal.Decimal("0") + var case29_a_mojo = Decimal128("0.0000000000000000000000000001") + var case29_b_mojo = Decimal128("0") + var case29_a_py = pydecimal.Decimal128("0.0000000000000000000000000001") + var case29_b_py = pydecimal.Decimal128("0") run_comparison_benchmark( "Close to zero comparison (>)", case29_a_mojo, @@ -700,10 +702,10 @@ fn main() raises: ) # Test Case 30: Boundary values - var case30_a_mojo = Decimal.MAX() - var case30_b_mojo = Decimal.MAX() - 1 - var case30_a_py = pydecimal.Decimal(String(Decimal.MAX())) - var case30_b_py = pydecimal.Decimal(String(Decimal.MAX() - 1)) + var case30_a_mojo = Decimal128.MAX() + var case30_b_mojo = Decimal128.MAX() - 1 + var case30_a_py = pydecimal.Decimal128(String(Decimal128.MAX())) + var case30_b_py = pydecimal.Decimal128(String(Decimal128.MAX() - 1)) run_comparison_benchmark( "Boundary values (>)", case30_a_mojo, diff --git a/benches/decimal/bench_divide.mojo b/benches/decimal128/bench_divide.mojo similarity index 71% rename from benches/decimal/bench_divide.mojo rename to benches/decimal128/bench_divide.mojo index 5b211fc..c92d10c 100644 --- a/benches/decimal/bench_divide.mojo +++ b/benches/decimal128/bench_divide.mojo @@ -1,9 +1,9 @@ """ -Comprehensive benchmarks for Decimal division operations. +Comprehensive benchmarks for Decimal128 division operations. Compares performance against Python's decimal module. """ -from decimojo.prelude import dm, Decimal, RoundingMode +from decimojo.prelude import dm, Decimal128, RoundingMode from python import Python, PythonObject from time import perf_counter_ns import time @@ -48,22 +48,22 @@ fn log_print(msg: String, log_file: PythonObject) raises: fn run_benchmark( name: String, - a_mojo: Decimal, - b_mojo: Decimal, + a_mojo: Decimal128, + b_mojo: Decimal128, a_py: PythonObject, b_py: PythonObject, iterations: Int, log_file: PythonObject, ) raises: """ - Run a benchmark comparing Mojo Decimal division with Python Decimal division. + Run a benchmark comparing Mojo Dec128 division with Python decimal division. Args: name: Name of the benchmark case. - a_mojo: First Mojo Decimal operand (dividend). - b_mojo: Second Mojo Decimal operand (divisor). - a_py: First Python Decimal operand. - b_py: Second Python Decimal operand. + a_mojo: First Mojo Dec128 operand (dividend). + b_mojo: Second Mojo Dec128 operand (divisor). + a_py: First Python decimal operand. + b_py: Second Python decimal operand. iterations: Number of iterations to run. log_file: File object for logging results. """ @@ -92,11 +92,11 @@ fn run_benchmark( # Print results with speedup comparison log_print( - "Mojo Decimal: " + String(mojo_time) + " ns per iteration", + "Mojo decimal: " + String(mojo_time) + " ns per iteration", log_file, ) log_print( - "Python Decimal: " + String(python_time) + " ns per iteration", + "Python decimal: " + String(python_time) + " ns per iteration", log_file, ) log_print("Speedup factor: " + String(python_time / mojo_time), log_file) @@ -137,7 +137,9 @@ fn main() raises: "Python decimal precision: " + String(pydecimal.getcontext().prec), log_file, ) - log_print("Mojo decimal precision: " + String(Decimal.MAX_SCALE), log_file) + log_print( + "Mojo decimal precision: " + String(Decimal128.MAX_SCALE), log_file + ) # Define benchmark cases log_print( @@ -148,10 +150,10 @@ fn main() raises: ) # Case 1: Simple integer division with no remainder - var case1_a_mojo = Decimal("100") - var case1_b_mojo = Decimal("4") - var case1_a_py = pydecimal.Decimal("100") - var case1_b_py = pydecimal.Decimal("4") + var case1_a_mojo = Decimal128("100") + var case1_b_mojo = Decimal128("4") + var case1_a_py = pydecimal.Decimal128("100") + var case1_b_py = pydecimal.Decimal128("4") run_benchmark( "Integer division (no remainder)", case1_a_mojo, @@ -163,10 +165,10 @@ fn main() raises: ) # Case 2: Division with simple decimals - var case2_a_mojo = Decimal("10.5") - var case2_b_mojo = Decimal("2.5") - var case2_a_py = pydecimal.Decimal("10.5") - var case2_b_py = pydecimal.Decimal("2.5") + var case2_a_mojo = Decimal128("10.5") + var case2_b_mojo = Decimal128("2.5") + var case2_a_py = pydecimal.Decimal128("10.5") + var case2_b_py = pydecimal.Decimal128("2.5") run_benchmark( "Simple decimal division", case2_a_mojo, @@ -178,10 +180,10 @@ fn main() raises: ) # Case 3: Division with repeating decimal result - var case3_a_mojo = Decimal("10") - var case3_b_mojo = Decimal("3") - var case3_a_py = pydecimal.Decimal("10") - var case3_b_py = pydecimal.Decimal("3") + var case3_a_mojo = Decimal128("10") + var case3_b_mojo = Decimal128("3") + var case3_a_py = pydecimal.Decimal128("10") + var case3_b_py = pydecimal.Decimal128("3") run_benchmark( "Division with repeating decimal", case3_a_mojo, @@ -193,10 +195,10 @@ fn main() raises: ) # Case 4: Division by one (identity) - var case4_a_mojo = Decimal("123.45") - var case4_b_mojo = Decimal("1") - var case4_a_py = pydecimal.Decimal("123.45") - var case4_b_py = pydecimal.Decimal("1") + var case4_a_mojo = Decimal128("123.45") + var case4_b_mojo = Decimal128("1") + var case4_a_py = pydecimal.Decimal128("123.45") + var case4_b_py = pydecimal.Decimal128("1") run_benchmark( "Division by one", case4_a_mojo, @@ -208,10 +210,10 @@ fn main() raises: ) # Case 5: Division of zero by non-zero - var case5_a_mojo = Decimal("0") - var case5_b_mojo = Decimal("123.45") - var case5_a_py = pydecimal.Decimal("0") - var case5_b_py = pydecimal.Decimal("123.45") + var case5_a_mojo = Decimal128("0") + var case5_b_mojo = Decimal128("123.45") + var case5_a_py = pydecimal.Decimal128("0") + var case5_b_py = pydecimal.Decimal128("123.45") run_benchmark( "Division of zero", case5_a_mojo, @@ -223,10 +225,10 @@ fn main() raises: ) # Case 6: Division with negative numbers - var case6_a_mojo = Decimal("123.45") - var case6_b_mojo = Decimal("-2") - var case6_a_py = pydecimal.Decimal("123.45") - var case6_b_py = pydecimal.Decimal("-2") + var case6_a_mojo = Decimal128("123.45") + var case6_b_mojo = Decimal128("-2") + var case6_a_py = pydecimal.Decimal128("123.45") + var case6_b_py = pydecimal.Decimal128("-2") run_benchmark( "Division with negative numbers", case6_a_mojo, @@ -238,10 +240,10 @@ fn main() raises: ) # Case 7: Division with very small divisor - var case7_a_mojo = Decimal("1") - var case7_b_mojo = Decimal("0.0001") - var case7_a_py = pydecimal.Decimal("1") - var case7_b_py = pydecimal.Decimal("0.0001") + var case7_a_mojo = Decimal128("1") + var case7_b_mojo = Decimal128("0.0001") + var case7_a_py = pydecimal.Decimal128("1") + var case7_b_py = pydecimal.Decimal128("0.0001") run_benchmark( "Division by very small number", case7_a_mojo, @@ -253,10 +255,10 @@ fn main() raises: ) # Case 8: Division with high precision - var case8_a_mojo = Decimal("0.1234567890123456789") - var case8_b_mojo = Decimal("0.0987654321098765432") - var case8_a_py = pydecimal.Decimal("0.1234567890123456789") - var case8_b_py = pydecimal.Decimal("0.0987654321098765432") + var case8_a_mojo = Decimal128("0.1234567890123456789") + var case8_b_mojo = Decimal128("0.0987654321098765432") + var case8_a_py = pydecimal.Decimal128("0.1234567890123456789") + var case8_b_py = pydecimal.Decimal128("0.0987654321098765432") run_benchmark( "High precision division", case8_a_mojo, @@ -268,10 +270,10 @@ fn main() raises: ) # Case 9: Division resulting in a power of 10 - var case9_a_mojo = Decimal("10") - var case9_b_mojo = Decimal("0.001") - var case9_a_py = pydecimal.Decimal("10") - var case9_b_py = pydecimal.Decimal("0.001") + var case9_a_mojo = Decimal128("10") + var case9_b_mojo = Decimal128("0.001") + var case9_a_py = pydecimal.Decimal128("10") + var case9_b_py = pydecimal.Decimal128("0.001") run_benchmark( "Division resulting in power of 10", case9_a_mojo, @@ -283,10 +285,10 @@ fn main() raises: ) # Case 10: Division of very large by very large - var case10_a_mojo = Decimal("9" * 20) # 20 nines - var case10_b_mojo = Decimal("9" * 10) # 10 nines - var case10_a_py = pydecimal.Decimal("9" * 20) - var case10_b_py = pydecimal.Decimal("9" * 10) + var case10_a_mojo = Decimal128("9" * 20) # 20 nines + var case10_b_mojo = Decimal128("9" * 10) # 10 nines + var case10_a_py = pydecimal.Decimal128("9" * 20) + var case10_b_py = pydecimal.Decimal128("9" * 10) run_benchmark( "Division of very large numbers", case10_a_mojo, diff --git a/benches/decimal/bench_exp.mojo b/benches/decimal128/bench_exp.mojo similarity index 95% rename from benches/decimal/bench_exp.mojo rename to benches/decimal128/bench_exp.mojo index 1b27b9d..572ab7c 100644 --- a/benches/decimal/bench_exp.mojo +++ b/benches/decimal128/bench_exp.mojo @@ -1,9 +1,9 @@ """ -Comprehensive benchmarks for Decimal exponential function (exp). +Comprehensive benchmarks for Decimal128 exponential function (exp). Compares performance against Python's decimal module with 20 diverse test cases. """ -from decimojo.prelude import dm, Decimal, RoundingMode +from decimojo.prelude import dm, Decimal128, RoundingMode from python import Python, PythonObject from time import perf_counter_ns import time @@ -55,7 +55,7 @@ fn run_benchmark( mut speedup_factors: List[Float64], ) raises: """ - Run a benchmark comparing Mojo Decimal exp with Python Decimal exp. + Run a benchmark comparing Mojo Dec128 exp with Python decimal exp. Args: name: Name of the benchmark case. @@ -68,9 +68,9 @@ fn run_benchmark( log_print("Input value: " + input_value, log_file) # Set up Mojo and Python values - var mojo_decimal = Decimal(input_value) + var mojo_decimal = Decimal128(input_value) var pydecimal = Python.import_module("decimal") - var py_decimal = pydecimal.Decimal(input_value) + var py_decimal = pydecimal.Decimal128(input_value) # Execute the operations once to verify correctness var mojo_result = mojo_decimal.exp() @@ -148,7 +148,9 @@ fn main() raises: "Python decimal precision: " + String(pydecimal.getcontext().prec), log_file, ) - log_print("Mojo decimal precision: " + String(Decimal.MAX_SCALE), log_file) + log_print( + "Mojo decimal precision: " + String(Decimal128.MAX_SCALE), log_file + ) # Define benchmark cases log_print( diff --git a/benches/decimal/bench_from_float.mojo b/benches/decimal128/bench_from_float.mojo similarity index 92% rename from benches/decimal/bench_from_float.mojo rename to benches/decimal128/bench_from_float.mojo index 2eaecda..954df7d 100644 --- a/benches/decimal/bench_from_float.mojo +++ b/benches/decimal128/bench_from_float.mojo @@ -1,9 +1,9 @@ """ -Comprehensive benchmarks for Decimal(Float64) constructor operations. +Comprehensive benchmarks for Decimal128(Float64) constructor operations. Compares performance against Python's decimal module with 10 diverse test cases. """ -from decimojo.prelude import dm, Decimal, RoundingMode +from decimojo.prelude import dm, Decimal128, RoundingMode from python import Python, PythonObject from time import perf_counter_ns import time @@ -55,7 +55,7 @@ fn run_benchmark( mut speedup_factors: List[Float64], ) raises: """ - Run a benchmark comparing Mojo Decimal(Float64) with Python Decimal(float). + Run a benchmark comparing Mojo Dec128(Float64) with Python decimal(float). Args: name: Name of the benchmark case. @@ -71,9 +71,9 @@ fn run_benchmark( var pydecimal = Python.import_module("decimal") # Execute the operations once to verify correctness - var mojo_result = Decimal.from_float(value) + var mojo_result = Decimal128.from_float(value) var py_value = Python.evaluate("float(" + String(value) + ")") - var py_result = pydecimal.Decimal(py_value) + var py_result = pydecimal.Decimal128(py_value) # Display results for verification log_print("Mojo result: " + String(mojo_result), log_file) @@ -82,7 +82,7 @@ fn run_benchmark( # Benchmark Mojo implementation var t0 = perf_counter_ns() for _ in range(iterations): - _ = Decimal.from_float(value) + _ = Decimal128.from_float(value) var mojo_time = (perf_counter_ns() - t0) / iterations if mojo_time == 0: mojo_time = 1 # Prevent division by zero @@ -90,7 +90,7 @@ fn run_benchmark( # Benchmark Python implementation t0 = perf_counter_ns() for _ in range(iterations): - _ = pydecimal.Decimal(py_value) + _ = pydecimal.Decimal128(py_value) var python_time = (perf_counter_ns() - t0) / iterations # Calculate speedup factor @@ -99,11 +99,11 @@ fn run_benchmark( # Print results with speedup comparison log_print( - "Mojo Decimal: " + String(mojo_time) + " ns per iteration", + "Mojo decimal: " + String(mojo_time) + " ns per iteration", log_file, ) log_print( - "Python Decimal: " + String(python_time) + " ns per iteration", + "Python decimal: " + String(python_time) + " ns per iteration", log_file, ) log_print("Speedup factor: " + String(speedup), log_file) @@ -147,7 +147,9 @@ fn main() raises: "Python decimal precision: " + String(pydecimal.getcontext().prec), log_file, ) - log_print("Mojo decimal precision: " + String(Decimal.MAX_SCALE), log_file) + log_print( + "Mojo decimal precision: " + String(Decimal128.MAX_SCALE), log_file + ) # Define benchmark cases log_print( diff --git a/benches/decimal/bench_from_int.mojo b/benches/decimal128/bench_from_int.mojo similarity index 94% rename from benches/decimal/bench_from_int.mojo rename to benches/decimal128/bench_from_int.mojo index 5399ebe..7beac7f 100644 --- a/benches/decimal/bench_from_int.mojo +++ b/benches/decimal128/bench_from_int.mojo @@ -1,9 +1,9 @@ """ -Comprehensive benchmarks for Decimal.from_int() constructor. +Comprehensive benchmarks for Decimal128.from_int() constructor. Compares performance against Python's decimal module with 20 diverse test cases. """ -from decimojo.prelude import dm, Decimal, RoundingMode +from decimojo.prelude import dm, Decimal128, RoundingMode from python import Python, PythonObject from time import perf_counter_ns import time @@ -55,7 +55,7 @@ fn run_benchmark( mut speedup_factors: List[Float64], ) raises: """ - Run a benchmark comparing Mojo Decimal.from_int with Python Decimal constructor. + Run a benchmark comparing Mojo Dec128.from_int with Python decimal constructor. Args: name: Name of the benchmark case. @@ -68,9 +68,9 @@ fn run_benchmark( log_print("Input value: " + String(input_value), log_file) # Execute the operations once to verify correctness - var mojo_result = Decimal.from_int(input_value) + var mojo_result = Decimal128.from_int(input_value) var pydecimal = Python.import_module("decimal") - var py_result = pydecimal.Decimal(input_value) + var py_result = pydecimal.Decimal128(input_value) # Display results for verification log_print("Mojo result: " + String(mojo_result), log_file) @@ -79,7 +79,7 @@ fn run_benchmark( # Benchmark Mojo implementation var t0 = perf_counter_ns() for _ in range(iterations): - _ = Decimal.from_int(input_value) + _ = Decimal128.from_int(input_value) var mojo_time = (perf_counter_ns() - t0) / iterations if mojo_time == 0: mojo_time = 1 # Prevent division by zero @@ -87,7 +87,7 @@ fn run_benchmark( # Benchmark Python implementation t0 = perf_counter_ns() for _ in range(iterations): - _ = pydecimal.Decimal(input_value) + _ = pydecimal.Decimal128(input_value) var python_time = (perf_counter_ns() - t0) / iterations # Calculate speedup factor @@ -100,7 +100,7 @@ fn run_benchmark( log_file, ) log_print( - "Python Decimal(): " + String(python_time) + " ns per iteration", + "Python decimal(): " + String(python_time) + " ns per iteration", log_file, ) log_print("Speedup factor: " + String(speedup), log_file) @@ -144,7 +144,9 @@ fn main() raises: "Python decimal precision: " + String(pydecimal.getcontext().prec), log_file, ) - log_print("Mojo decimal precision: " + String(Decimal.MAX_SCALE), log_file) + log_print( + "Mojo decimal precision: " + String(Decimal128.MAX_SCALE), log_file + ) # Define benchmark cases log_print( diff --git a/benches/decimal/bench_from_string.mojo b/benches/decimal128/bench_from_string.mojo similarity index 92% rename from benches/decimal/bench_from_string.mojo rename to benches/decimal128/bench_from_string.mojo index 18e468c..a06c4a1 100644 --- a/benches/decimal/bench_from_string.mojo +++ b/benches/decimal128/bench_from_string.mojo @@ -1,9 +1,9 @@ """ -Comprehensive benchmarks for Decimal string constructor operations. +Comprehensive benchmarks for Decimal128 string constructor operations. Compares performance against Python's decimal module across diverse test cases. """ -from decimojo.prelude import dm, Decimal, RoundingMode +from decimojo.prelude import dm, Decimal128, RoundingMode from python import Python, PythonObject from time import perf_counter_ns import time @@ -55,7 +55,7 @@ fn run_benchmark( mut speedup_factors: List[Float64], ) raises: """ - Run a benchmark comparing Mojo Decimal(string) with Python Decimal(string). + Run a benchmark comparing Mojo Dec128(string) with Python decimal(string). Args: name: Name of the benchmark case. @@ -71,8 +71,8 @@ fn run_benchmark( var pydecimal = Python.import_module("decimal") # Execute the operations once to verify correctness - var mojo_result = Decimal(value_str) - var py_result = pydecimal.Decimal(value_str) + var mojo_result = Decimal128(value_str) + var py_result = pydecimal.Decimal128(value_str) # Display results for verification log_print("Mojo result: " + String(mojo_result), log_file) @@ -81,7 +81,7 @@ fn run_benchmark( # Benchmark Mojo implementation var t0 = perf_counter_ns() for _ in range(iterations): - _ = Decimal(value_str) + _ = Decimal128(value_str) var mojo_time = (perf_counter_ns() - t0) / iterations if mojo_time == 0: mojo_time = 1 # Prevent division by zero @@ -89,7 +89,7 @@ fn run_benchmark( # Benchmark Python implementation t0 = perf_counter_ns() for _ in range(iterations): - _ = pydecimal.Decimal(value_str) + _ = pydecimal.Decimal128(value_str) var python_time = (perf_counter_ns() - t0) / iterations # Calculate speedup factor @@ -98,11 +98,11 @@ fn run_benchmark( # Print results with speedup comparison log_print( - "Mojo Decimal: " + String(mojo_time) + " ns per iteration", + "Mojo decimal: " + String(mojo_time) + " ns per iteration", log_file, ) log_print( - "Python Decimal: " + String(python_time) + " ns per iteration", + "Python decimal: " + String(python_time) + " ns per iteration", log_file, ) log_print("Speedup factor: " + String(speedup), log_file) @@ -146,7 +146,9 @@ fn main() raises: "Python decimal precision: " + String(pydecimal.getcontext().prec), log_file, ) - log_print("Mojo decimal precision: " + String(Decimal.MAX_SCALE), log_file) + log_print( + "Mojo decimal precision: " + String(Decimal128.MAX_SCALE), log_file + ) # Define benchmark cases log_print( diff --git a/benches/decimal/bench_ln.mojo b/benches/decimal128/bench_ln.mojo similarity index 94% rename from benches/decimal/bench_ln.mojo rename to benches/decimal128/bench_ln.mojo index c950b90..324bd53 100644 --- a/benches/decimal/bench_ln.mojo +++ b/benches/decimal128/bench_ln.mojo @@ -1,9 +1,9 @@ """ -Comprehensive benchmarks for Decimal natural logarithm function (ln). +Comprehensive benchmarks for Decimal128 natural logarithm function (ln). Compares performance against Python's decimal module with 20 diverse test cases. """ -from decimojo.prelude import dm, Decimal, RoundingMode +from decimojo.prelude import dm, Decimal128, RoundingMode from python import Python, PythonObject from time import perf_counter_ns import time @@ -55,7 +55,7 @@ fn run_benchmark( mut speedup_factors: List[Float64], ) raises: """ - Run a benchmark comparing Mojo Decimal ln with Python Decimal ln. + Run a benchmark comparing Mojo Dec128 ln with Python decimal ln. Args: name: Name of the benchmark case. @@ -68,12 +68,12 @@ fn run_benchmark( log_print("Input value: " + input_value, log_file) # Set up Mojo and Python values - var mojo_decimal = Decimal(input_value) + var mojo_decimal = Decimal128(input_value) var pydecimal = Python.import_module("decimal") - var py_decimal = pydecimal.Decimal(input_value) + var py_decimal = pydecimal.Decimal128(input_value) # Execute the operations once to verify correctness - var mojo_result = dm.decimal.exponential.ln(mojo_decimal) + var mojo_result = dm.decimal128.exponential.ln(mojo_decimal) var py_result = py_decimal.ln() # Display results for verification @@ -83,7 +83,7 @@ fn run_benchmark( # Benchmark Mojo implementation var t0 = perf_counter_ns() for _ in range(iterations): - _ = dm.decimal.exponential.ln(mojo_decimal) + _ = dm.decimal128.exponential.ln(mojo_decimal) var mojo_time = (perf_counter_ns() - t0) / iterations if mojo_time == 0: mojo_time = 1 # Prevent division by zero @@ -150,7 +150,9 @@ fn main() raises: "Python decimal precision: " + String(pydecimal.getcontext().prec), log_file, ) - log_print("Mojo decimal precision: " + String(Decimal.MAX_SCALE), log_file) + log_print( + "Mojo decimal precision: " + String(Decimal128.MAX_SCALE), log_file + ) # Define benchmark cases log_print( diff --git a/benches/decimal/bench_log10.mojo b/benches/decimal128/bench_log10.mojo similarity index 95% rename from benches/decimal/bench_log10.mojo rename to benches/decimal128/bench_log10.mojo index 8702eb4..b2249c5 100644 --- a/benches/decimal/bench_log10.mojo +++ b/benches/decimal128/bench_log10.mojo @@ -1,9 +1,9 @@ """ -Comprehensive benchmarks for Decimal log10() function. +Comprehensive benchmarks for Decimal128 log10() function. Compares performance against Python's decimal module with diverse test cases. """ -from decimojo.prelude import dm, Decimal, RoundingMode +from decimojo.prelude import dm, Decimal128, RoundingMode from python import Python, PythonObject from time import perf_counter_ns import time @@ -55,7 +55,7 @@ fn run_benchmark_log10( mut speedup_factors: List[Float64], ) raises: """ - Run a benchmark comparing Mojo Decimal.log10 with Python Decimal.log10. + Run a benchmark comparing Mojo Dec128.log10 with Python decimal.log10. Args: name: Name of the benchmark case. @@ -68,9 +68,9 @@ fn run_benchmark_log10( log_print("Input value: " + input_value, log_file) # Set up Mojo and Python values - var mojo_decimal = Decimal(input_value) + var mojo_decimal = Decimal128(input_value) var pydecimal = Python.import_module("decimal") - var py_decimal = pydecimal.Decimal(input_value) + var py_decimal = pydecimal.Decimal128(input_value) # Execute the operations once to verify correctness var mojo_result = mojo_decimal.log10() @@ -148,7 +148,9 @@ fn main() raises: "Python decimal precision: " + String(pydecimal.getcontext().prec), log_file, ) - log_print("Mojo decimal precision: " + String(Decimal.MAX_SCALE), log_file) + log_print( + "Mojo decimal precision: " + String(Decimal128.MAX_SCALE), log_file + ) # Define benchmark cases log_print( @@ -206,9 +208,9 @@ fn main() raises: speedup_factors, ) - # Case 9: Decimal number with many digits + # Case 9: Decimal128 number with many digits run_benchmark_log10( - "Decimal with many digits", + "Decimal128 with many digits", "3.1415926535897932384626433832795", iterations, log_file, diff --git a/benches/decimal/bench_modulo.mojo b/benches/decimal128/bench_modulo.mojo similarity index 93% rename from benches/decimal/bench_modulo.mojo rename to benches/decimal128/bench_modulo.mojo index 74e085c..a5ea0e0 100644 --- a/benches/decimal/bench_modulo.mojo +++ b/benches/decimal128/bench_modulo.mojo @@ -1,9 +1,9 @@ """ -Comprehensive benchmarks for Decimal modulo (%) operation. +Comprehensive benchmarks for Decimal128 modulo (%) operation. Compares performance against Python's decimal module with diverse test cases. """ -from decimojo.prelude import dm, Decimal, RoundingMode +from decimojo.prelude import dm, Decimal128, RoundingMode from python import Python, PythonObject from time import perf_counter_ns import time @@ -56,7 +56,7 @@ fn run_benchmark_modulo( mut speedup_factors: List[Float64], ) raises: """ - Run a benchmark comparing Mojo Decimal modulo with Python Decimal modulo. + Run a benchmark comparing Mojo Dec128 modulo with Python decimal modulo. Args: name: Name of the benchmark case. @@ -71,11 +71,11 @@ fn run_benchmark_modulo( log_print("Divisor: " + divisor, log_file) # Set up Mojo and Python values - var mojo_dividend = Decimal(dividend) - var mojo_divisor = Decimal(divisor) + var mojo_dividend = Decimal128(dividend) + var mojo_divisor = Decimal128(divisor) var pydecimal = Python.import_module("decimal") - var py_dividend = pydecimal.Decimal(dividend) - var py_divisor = pydecimal.Decimal(divisor) + var py_dividend = pydecimal.Decimal128(dividend) + var py_divisor = pydecimal.Decimal128(divisor) # Execute the operations once to verify correctness try: @@ -157,7 +157,9 @@ fn main() raises: "Python decimal precision: " + String(pydecimal.getcontext().prec), log_file, ) - log_print("Mojo decimal precision: " + String(Decimal.MAX_SCALE), log_file) + log_print( + "Mojo decimal precision: " + String(Decimal128.MAX_SCALE), log_file + ) # Define benchmark cases log_print( @@ -189,7 +191,7 @@ fn main() raises: # Case 3: Modulo with decimal values run_benchmark_modulo( - "Decimal values (even division)", + "Decimal128 values (even division)", "10.5", "3.5", iterations, @@ -247,9 +249,9 @@ fn main() raises: speedup_factors, ) - # Case 9: Decimal values, negative dividend + # Case 9: Decimal128 values, negative dividend run_benchmark_modulo( - "Decimal values, negative dividend", + "Decimal128 values, negative dividend", "-10.5", "3.5", iterations, @@ -257,9 +259,9 @@ fn main() raises: speedup_factors, ) - # Case 10: Decimal values with remainder, negative dividend + # Case 10: Decimal128 values with remainder, negative dividend run_benchmark_modulo( - "Decimal with remainder, negative dividend", + "Decimal128 with remainder, negative dividend", "-10.5", "3", iterations, diff --git a/benches/decimal/bench_multiply.mojo b/benches/decimal128/bench_multiply.mojo similarity index 69% rename from benches/decimal/bench_multiply.mojo rename to benches/decimal128/bench_multiply.mojo index be46f76..8e204a6 100644 --- a/benches/decimal/bench_multiply.mojo +++ b/benches/decimal128/bench_multiply.mojo @@ -1,9 +1,9 @@ """ -Comprehensive benchmarks for Decimal multiplication operations. +Comprehensive benchmarks for Decimal128 multiplication operations. Compares performance against Python's decimal module. """ -from decimojo.prelude import dm, Decimal, RoundingMode +from decimojo.prelude import dm, Decimal128, RoundingMode from python import Python, PythonObject from time import perf_counter_ns import time @@ -48,22 +48,22 @@ fn log_print(msg: String, log_file: PythonObject) raises: fn run_benchmark( name: String, - a_mojo: Decimal, - b_mojo: Decimal, + a_mojo: Decimal128, + b_mojo: Decimal128, a_py: PythonObject, b_py: PythonObject, iterations: Int, log_file: PythonObject, ) raises: """ - Run a benchmark comparing Mojo Decimal multiplication with Python Decimal multiplication. + Run a benchmark comparing Mojo Dec128 multiplication with Python decimal multiplication. Args: name: Name of the benchmark case. - a_mojo: First Mojo Decimal operand. - b_mojo: Second Mojo Decimal operand. - a_py: First Python Decimal operand. - b_py: Second Python Decimal operand. + a_mojo: First Mojo Dec128 operand. + b_mojo: Second Mojo Dec128 operand. + a_py: First Python decimal operand. + b_py: Second Python decimal operand. iterations: Number of iterations to run. log_file: File object for logging results. """ @@ -92,11 +92,11 @@ fn run_benchmark( # Print results with speedup comparison log_print( - "Mojo Decimal: " + String(mojo_time) + " ns per iteration", + "Mojo decimal: " + String(mojo_time) + " ns per iteration", log_file, ) log_print( - "Python Decimal: " + String(python_time) + " ns per iteration", + "Python decimal: " + String(python_time) + " ns per iteration", log_file, ) log_print("Speedup factor: " + String(python_time / mojo_time), log_file) @@ -137,7 +137,9 @@ fn main() raises: "Python decimal precision: " + String(pydecimal.getcontext().prec), log_file, ) - log_print("Mojo decimal precision: " + String(Decimal.MAX_SCALE), log_file) + log_print( + "Mojo decimal precision: " + String(Decimal128.MAX_SCALE), log_file + ) # Define benchmark cases log_print( @@ -148,10 +150,10 @@ fn main() raises: ) # Case 1: Small integers - var case1_a_mojo = Decimal("12") - var case1_b_mojo = Decimal("34") - var case1_a_py = pydecimal.Decimal("12") - var case1_b_py = pydecimal.Decimal("34") + var case1_a_mojo = Decimal128("12") + var case1_b_mojo = Decimal128("34") + var case1_a_py = pydecimal.Decimal128("12") + var case1_b_py = pydecimal.Decimal128("34") run_benchmark( "Small integers", case1_a_mojo, @@ -163,10 +165,10 @@ fn main() raises: ) # Case 2: Simple decimals - var case2_a_mojo = Decimal("12.34") - var case2_b_mojo = Decimal("5.67") - var case2_a_py = pydecimal.Decimal("12.34") - var case2_b_py = pydecimal.Decimal("5.67") + var case2_a_mojo = Decimal128("12.34") + var case2_b_mojo = Decimal128("5.67") + var case2_a_py = pydecimal.Decimal128("12.34") + var case2_b_py = pydecimal.Decimal128("5.67") run_benchmark( "Simple decimals", case2_a_mojo, @@ -178,10 +180,10 @@ fn main() raises: ) # Case 3: Different scales - var case3_a_mojo = Decimal("12.3") - var case3_b_mojo = Decimal("4.56") - var case3_a_py = pydecimal.Decimal("12.3") - var case3_b_py = pydecimal.Decimal("4.56") + var case3_a_mojo = Decimal128("12.3") + var case3_b_mojo = Decimal128("4.56") + var case3_a_py = pydecimal.Decimal128("12.3") + var case3_b_py = pydecimal.Decimal128("4.56") run_benchmark( "Different scales", case3_a_mojo, @@ -193,10 +195,10 @@ fn main() raises: ) # Case 4: Multiplication by zero - var case4_a_mojo = Decimal("123.45") - var case4_b_mojo = Decimal("0") - var case4_a_py = pydecimal.Decimal("123.45") - var case4_b_py = pydecimal.Decimal("0") + var case4_a_mojo = Decimal128("123.45") + var case4_b_mojo = Decimal128("0") + var case4_a_py = pydecimal.Decimal128("123.45") + var case4_b_py = pydecimal.Decimal128("0") run_benchmark( "Multiplication by zero", case4_a_mojo, @@ -208,10 +210,10 @@ fn main() raises: ) # Case 5: Multiplication by one - var case5_a_mojo = Decimal("123.45") - var case5_b_mojo = Decimal("1") - var case5_a_py = pydecimal.Decimal("123.45") - var case5_b_py = pydecimal.Decimal("1") + var case5_a_mojo = Decimal128("123.45") + var case5_b_mojo = Decimal128("1") + var case5_a_py = pydecimal.Decimal128("123.45") + var case5_b_py = pydecimal.Decimal128("1") run_benchmark( "Multiplication by one", case5_a_mojo, @@ -223,10 +225,10 @@ fn main() raises: ) # Case 6: Negative numbers - var case6_a_mojo = Decimal("12.34") - var case6_b_mojo = Decimal("-5.67") - var case6_a_py = pydecimal.Decimal("12.34") - var case6_b_py = pydecimal.Decimal("-5.67") + var case6_a_mojo = Decimal128("12.34") + var case6_b_mojo = Decimal128("-5.67") + var case6_a_py = pydecimal.Decimal128("12.34") + var case6_b_py = pydecimal.Decimal128("-5.67") run_benchmark( "Negative numbers", case6_a_mojo, @@ -238,10 +240,10 @@ fn main() raises: ) # Case 7: High precision - var case7_a_mojo = Decimal("0.12345678901234567") - var case7_b_mojo = Decimal("0.98765432109876543") - var case7_a_py = pydecimal.Decimal("0.12345678901234567") - var case7_b_py = pydecimal.Decimal("0.98765432109876543") + var case7_a_mojo = Decimal128("0.12345678901234567") + var case7_b_mojo = Decimal128("0.98765432109876543") + var case7_a_py = pydecimal.Decimal128("0.12345678901234567") + var case7_b_py = pydecimal.Decimal128("0.98765432109876543") run_benchmark( "High precision multiplication", case7_a_mojo, @@ -253,10 +255,10 @@ fn main() raises: ) # Case 8: Large integer multiplication - var case8_a_mojo = Decimal("123456789") - var case8_b_mojo = Decimal("987654321") - var case8_a_py = pydecimal.Decimal("123456789") - var case8_b_py = pydecimal.Decimal("987654321") + var case8_a_mojo = Decimal128("123456789") + var case8_b_mojo = Decimal128("987654321") + var case8_a_py = pydecimal.Decimal128("123456789") + var case8_b_py = pydecimal.Decimal128("987654321") run_benchmark( "Large integer multiplication", case8_a_mojo, @@ -268,10 +270,10 @@ fn main() raises: ) # Case 9: Fractional multiplication resulting in very small scale - var case9_a_mojo = Decimal("0.01") - var case9_b_mojo = Decimal("0.01") - var case9_a_py = pydecimal.Decimal("0.01") - var case9_b_py = pydecimal.Decimal("0.01") + var case9_a_mojo = Decimal128("0.01") + var case9_b_mojo = Decimal128("0.01") + var case9_a_py = pydecimal.Decimal128("0.01") + var case9_b_py = pydecimal.Decimal128("0.01") run_benchmark( "Fractional multiplication", case9_a_mojo, @@ -283,10 +285,10 @@ fn main() raises: ) # Case 10: Powers of 10 - var case10_a_mojo = Decimal("10") - var case10_b_mojo = Decimal("10") - var case10_a_py = pydecimal.Decimal("10") - var case10_b_py = pydecimal.Decimal("10") + var case10_a_mojo = Decimal128("10") + var case10_b_mojo = Decimal128("10") + var case10_a_py = pydecimal.Decimal128("10") + var case10_b_py = pydecimal.Decimal128("10") run_benchmark( "Powers of 10", case10_a_mojo, @@ -297,11 +299,11 @@ fn main() raises: log_file, ) - # Case 11: Decimal multiplication with many digits after the decimal point - var case11_a_mojo = Decimal.E() - var case11_b_mojo = dm.decimal.constants.E0D5() - var case11_a_py = pydecimal.Decimal("1").exp() - var case11_b_py = pydecimal.Decimal("0.5").exp() + # Case 11: Decimal128 multiplication with many digits after the decimal point + var case11_a_mojo = Decimal128.E() + var case11_b_mojo = dm.decimal128.constants.E0D5() + var case11_a_py = pydecimal.Decimal128("1").exp() + var case11_b_py = pydecimal.Decimal128("0.5").exp() run_benchmark( "e * e^0.5", case11_a_mojo, diff --git a/benches/decimal/bench_power.mojo b/benches/decimal128/bench_power.mojo similarity index 91% rename from benches/decimal/bench_power.mojo rename to benches/decimal128/bench_power.mojo index 9860dc0..56bbf87 100644 --- a/benches/decimal/bench_power.mojo +++ b/benches/decimal128/bench_power.mojo @@ -1,9 +1,9 @@ """ -Comprehensive benchmarks for Decimal power function. +Comprehensive benchmarks for Decimal128 power function. Compares performance against Python's decimal module. """ -from decimojo.prelude import dm, Decimal, RoundingMode +from decimojo.prelude import dm, Decimal128, RoundingMode from python import Python, PythonObject from time import perf_counter_ns import time @@ -56,12 +56,12 @@ fn run_benchmark( mut speedup_factors: List[Float64], ) raises: """ - Run a benchmark comparing Mojo Decimal power with Python Decimal power. + Run a benchmark comparing Mojo Dec128 power with Python decimal power. Args: name: Name of the benchmark case. - base_value: String representation of the base Decimal. - exponent_value: String representation of the exponent Decimal. + base_value: String representation of the base Decimal128. + exponent_value: String representation of the exponent Decimal128. iterations: Number of iterations to run. log_file: File object for logging results. speedup_factors: Mojo List to store speedup factors for averaging. @@ -71,14 +71,14 @@ fn run_benchmark( log_print("Exponent: " + exponent_value, log_file) # Set up Mojo and Python values - var mojo_base = Decimal(base_value) - var mojo_exponent = Decimal(exponent_value) + var mojo_base = Decimal128(base_value) + var mojo_exponent = Decimal128(exponent_value) var pydecimal = Python.import_module("decimal") - var py_base = pydecimal.Decimal(base_value) - var py_exponent = pydecimal.Decimal(exponent_value) + var py_base = pydecimal.Decimal128(base_value) + var py_exponent = pydecimal.Decimal128(exponent_value) # Execute the operations once to verify correctness - var mojo_result = dm.decimal.exponential.power(mojo_base, mojo_exponent) + var mojo_result = dm.decimal128.exponential.power(mojo_base, mojo_exponent) var py_result = py_base**py_exponent # Display results for verification @@ -88,7 +88,7 @@ fn run_benchmark( # Benchmark Mojo implementation var t0 = perf_counter_ns() for _ in range(iterations): - _ = dm.decimal.exponential.power(mojo_base, mojo_exponent) + _ = dm.decimal128.exponential.power(mojo_base, mojo_exponent) var mojo_time = (perf_counter_ns() - t0) / iterations if mojo_time == 0: mojo_time = 1 # Prevent division by zero @@ -153,7 +153,9 @@ fn main() raises: "Python decimal precision: " + String(pydecimal.getcontext().prec), log_file, ) - log_print("Mojo decimal precision: " + String(Decimal.MAX_SCALE), log_file) + log_print( + "Mojo decimal precision: " + String(Decimal128.MAX_SCALE), log_file + ) # Define benchmark cases log_print( @@ -173,9 +175,9 @@ fn main() raises: speedup_factors, ) - # Case 2: Decimal base and integer exponent + # Case 2: Decimal128 base and integer exponent run_benchmark( - "Decimal base and integer exponent", + "Decimal128 base and integer exponent", "2.5", "2", iterations, @@ -193,9 +195,9 @@ fn main() raises: speedup_factors, ) - # Case 4: Decimal base and exponent + # Case 4: Decimal128 base and exponent run_benchmark( - "Decimal base and exponent", + "Decimal128 base and exponent", "2.0", "1.5", iterations, diff --git a/benches/decimal/bench_quantize.mojo b/benches/decimal128/bench_quantize.mojo similarity index 96% rename from benches/decimal/bench_quantize.mojo rename to benches/decimal128/bench_quantize.mojo index ff68a6c..bf6fb3a 100644 --- a/benches/decimal/bench_quantize.mojo +++ b/benches/decimal128/bench_quantize.mojo @@ -1,9 +1,9 @@ """ -Comprehensive benchmarks for Decimal.quantize() method. +Comprehensive benchmarks for Decimal128.quantize() method. Compares performance against Python's decimal module with diverse test cases. """ -from decimojo.prelude import dm, Decimal, RoundingMode +from decimojo.prelude import dm, Decimal128, RoundingMode from python import Python, PythonObject from time import perf_counter_ns import time @@ -57,7 +57,7 @@ fn run_benchmark_quantize( mut speedup_factors: List[Float64], ) raises: """ - Run a benchmark comparing Mojo Decimal.quantize with Python Decimal.quantize. + Run a benchmark comparing Mojo Dec128.quantize with Python decimal.quantize. Args: name: Name of the benchmark case. @@ -74,11 +74,11 @@ fn run_benchmark_quantize( log_print("Rounding mode: " + String(rounding_mode), log_file) # Set up Mojo and Python values - var mojo_value = Decimal(value_str) - var mojo_quant = Decimal(quant_str) + var mojo_value = Decimal128(value_str) + var mojo_quant = Decimal128(quant_str) var pydecimal = Python.import_module("decimal") - var py_value = pydecimal.Decimal(value_str) - var py_quant = pydecimal.Decimal(quant_str) + var py_value = pydecimal.Decimal128(value_str) + var py_quant = pydecimal.Decimal128(quant_str) # Map Mojo rounding mode to Python rounding mode var py_rounding_mode: PythonObject @@ -173,7 +173,9 @@ fn main() raises: "Python decimal precision: " + String(pydecimal.getcontext().prec), log_file, ) - log_print("Mojo decimal precision: " + String(Decimal.MAX_SCALE), log_file) + log_print( + "Mojo decimal precision: " + String(Decimal128.MAX_SCALE), log_file + ) # Define benchmark cases log_print( diff --git a/benches/decimal/bench_root.mojo b/benches/decimal128/bench_root.mojo similarity index 94% rename from benches/decimal/bench_root.mojo rename to benches/decimal128/bench_root.mojo index e5216c6..7806bc1 100644 --- a/benches/decimal/bench_root.mojo +++ b/benches/decimal128/bench_root.mojo @@ -1,9 +1,9 @@ """ -Comprehensive benchmarks for Decimal nth root function (root). +Comprehensive benchmarks for Decimal128 nth root function (root). Compares performance against Python's decimal module with diverse test cases. """ -from decimojo.prelude import dm, Decimal, RoundingMode +from decimojo.prelude import dm, Decimal128, RoundingMode from python import Python, PythonObject from time import perf_counter_ns import time @@ -56,7 +56,7 @@ fn run_benchmark( mut speedup_factors: List[Float64], ) raises: """ - Run a benchmark comparing Mojo Decimal root with Python Decimal power(1/n). + Run a benchmark comparing Mojo Dec128 root with Python decimal power(1/n). Args: name: Name of the benchmark case. @@ -71,18 +71,18 @@ fn run_benchmark( log_print("Root: " + String(nth_root), log_file) # Set up Mojo and Python values - var mojo_decimal = Decimal(value) + var mojo_decimal = Decimal128(value) var pydecimal = Python.import_module("decimal") - var py_decimal = pydecimal.Decimal(value) - var py_root = pydecimal.Decimal(String(nth_root)) - var py_frac = pydecimal.Decimal(1) / py_root + var py_decimal = pydecimal.Decimal128(value) + var py_root = pydecimal.Decimal128(String(nth_root)) + var py_frac = pydecimal.Decimal128(1) / py_root # Special case: Python can't directly compute odd root of negative number var is_negative_odd_root = value.startswith("-") and nth_root % 2 == 1 var py_result: PythonObject # Execute the operations once to verify correctness - var mojo_result = dm.decimal.exponential.root(mojo_decimal, nth_root) + var mojo_result = dm.decimal128.exponential.root(mojo_decimal, nth_root) # Handle Python calculation, accounting for negative odd root limitation if is_negative_odd_root: @@ -127,7 +127,7 @@ fn run_benchmark( # Benchmark Mojo implementation var t0 = perf_counter_ns() for _ in range(iterations): - _ = dm.decimal.exponential.root(mojo_decimal, nth_root) + _ = dm.decimal128.exponential.root(mojo_decimal, nth_root) var mojo_time = (perf_counter_ns() - t0) / iterations if mojo_time == 0: mojo_time = 1 # Prevent division by zero @@ -214,7 +214,9 @@ fn main() raises: "Python decimal precision: " + String(pydecimal.getcontext().prec), log_file, ) - log_print("Mojo decimal precision: " + String(Decimal.MAX_SCALE), log_file) + log_print( + "Mojo decimal precision: " + String(Decimal128.MAX_SCALE), log_file + ) # Define benchmark cases log_print( diff --git a/benches/decimal/bench_round.mojo b/benches/decimal128/bench_round.mojo similarity index 76% rename from benches/decimal/bench_round.mojo rename to benches/decimal128/bench_round.mojo index d394f81..4ed381d 100644 --- a/benches/decimal/bench_round.mojo +++ b/benches/decimal128/bench_round.mojo @@ -1,9 +1,9 @@ """ -Comprehensive benchmarks for Decimal rounding operations. +Comprehensive benchmarks for Decimal128 rounding operations. Compares performance against Python's decimal module across 32 diverse test cases. """ -from decimojo.prelude import dm, Decimal, RoundingMode +from decimojo.prelude import dm, Decimal128, RoundingMode from python import Python, PythonObject from time import perf_counter_ns import time @@ -49,7 +49,7 @@ fn log_print(msg: String, log_file: PythonObject) raises: fn run_benchmark( name: String, - d_mojo: Decimal, + d_mojo: Decimal128, places: Int, d_py: PythonObject, iterations: Int, @@ -57,19 +57,19 @@ fn run_benchmark( mut speedup_factors: List[Float64], ) raises: """ - Run a benchmark comparing Mojo Decimal round with Python Decimal quantize. + Run a benchmark comparing Mojo Dec128 round with Python decimal quantize. Args: name: Name of the benchmark case. - d_mojo: Mojo Decimal operand. + d_mojo: Mojo Dec128 operand. places: Number of decimal places to round to. - d_py: Python Decimal operand. + d_py: Python decimal operand. iterations: Number of iterations to run. log_file: File object for logging results. speedup_factors: Mojo List to store speedup factors for averaging. """ log_print("\nBenchmark: " + name, log_file) - log_print("Decimal: " + String(d_mojo), log_file) + log_print("Decimal128: " + String(d_mojo), log_file) log_print("Round to: " + String(places) + " places", log_file) # Get Python decimal module for quantize operation @@ -103,11 +103,11 @@ fn run_benchmark( # Print results with speedup comparison log_print( - "Mojo Decimal: " + String(mojo_time) + " ns per iteration", + "Mojo decimal: " + String(mojo_time) + " ns per iteration", log_file, ) log_print( - "Python Decimal: " + String(python_time) + " ns per iteration", + "Python decimal: " + String(python_time) + " ns per iteration", log_file, ) log_print("Speedup factor: " + String(speedup), log_file) @@ -151,7 +151,9 @@ fn main() raises: "Python decimal precision: " + String(pydecimal.getcontext().prec), log_file, ) - log_print("Mojo decimal precision: " + String(Decimal.MAX_SCALE), log_file) + log_print( + "Mojo decimal precision: " + String(Decimal128.MAX_SCALE), log_file + ) # Define benchmark cases log_print( @@ -162,8 +164,8 @@ fn main() raises: ) # Case 1: Standard rounding to 2 decimal places - var case1_mojo = Decimal("123.456789") - var case1_py = pydecimal.Decimal("123.456789") + var case1_mojo = Decimal128("123.456789") + var case1_py = pydecimal.Decimal128("123.456789") run_benchmark( "Standard rounding to 2 places", case1_mojo, @@ -175,8 +177,8 @@ fn main() raises: ) # Case 2: Banker's rounding (round half to even) for .5 with even preceding digit - var case2_mojo = Decimal("10.125") - var case2_py = pydecimal.Decimal("10.125") + var case2_mojo = Decimal128("10.125") + var case2_py = pydecimal.Decimal128("10.125") run_benchmark( "Banker's rounding with even preceding digit", case2_mojo, @@ -188,8 +190,8 @@ fn main() raises: ) # Case 3: Banker's rounding (round half to even) for .5 with odd preceding digit - var case3_mojo = Decimal("10.135") - var case3_py = pydecimal.Decimal("10.135") + var case3_mojo = Decimal128("10.135") + var case3_py = pydecimal.Decimal128("10.135") run_benchmark( "Banker's rounding with odd preceding digit", case3_mojo, @@ -201,8 +203,8 @@ fn main() raises: ) # Case 4: Rounding with less than half (<0.5) - var case4_mojo = Decimal("10.124") - var case4_py = pydecimal.Decimal("10.124") + var case4_mojo = Decimal128("10.124") + var case4_py = pydecimal.Decimal128("10.124") run_benchmark( "Rounding with less than half", case4_mojo, @@ -214,8 +216,8 @@ fn main() raises: ) # Case 5: Rounding with more than half (>0.5) - var case5_mojo = Decimal("10.126") - var case5_py = pydecimal.Decimal("10.126") + var case5_mojo = Decimal128("10.126") + var case5_py = pydecimal.Decimal128("10.126") run_benchmark( "Rounding with more than half", case5_mojo, @@ -227,8 +229,8 @@ fn main() raises: ) # Case 6: Rounding to 0 decimal places (whole number) - var case6_mojo = Decimal("123.456") - var case6_py = pydecimal.Decimal("123.456") + var case6_mojo = Decimal128("123.456") + var case6_py = pydecimal.Decimal128("123.456") run_benchmark( "Rounding to whole number", case6_mojo, @@ -240,8 +242,8 @@ fn main() raises: ) # Case 7: Rounding a negative number - var case7_mojo = Decimal("-123.456") - var case7_py = pydecimal.Decimal("-123.456") + var case7_mojo = Decimal128("-123.456") + var case7_py = pydecimal.Decimal128("-123.456") run_benchmark( "Rounding negative number", case7_mojo, @@ -253,8 +255,8 @@ fn main() raises: ) # Case 8: Rounding to negative places (tens) - var case8_mojo = Decimal("123.456") - var case8_py = pydecimal.Decimal("123.456") + var case8_mojo = Decimal128("123.456") + var case8_py = pydecimal.Decimal128("123.456") run_benchmark( "Rounding to tens (negative places)", case8_mojo, @@ -266,8 +268,8 @@ fn main() raises: ) # Case 9: Rounding to negative places (hundreds) - var case9_mojo = Decimal("1234.56") - var case9_py = pydecimal.Decimal("1234.56") + var case9_mojo = Decimal128("1234.56") + var case9_py = pydecimal.Decimal128("1234.56") run_benchmark( "Rounding to hundreds (negative places)", case9_mojo, @@ -279,8 +281,8 @@ fn main() raises: ) # Case 10: Rounding a very small number - var case10_mojo = Decimal("0.0000001234") - var case10_py = pydecimal.Decimal("0.0000001234") + var case10_mojo = Decimal128("0.0000001234") + var case10_py = pydecimal.Decimal128("0.0000001234") run_benchmark( "Rounding very small number", case10_mojo, @@ -292,8 +294,8 @@ fn main() raises: ) # Case 11: Rounding already rounded number (no change) - var case11_mojo = Decimal("123.45") - var case11_py = pydecimal.Decimal("123.45") + var case11_mojo = Decimal128("123.45") + var case11_py = pydecimal.Decimal128("123.45") run_benchmark( "Rounding already rounded number", case11_mojo, @@ -305,8 +307,8 @@ fn main() raises: ) # Case 12: Rounding to high precision (20 places) - var case12_mojo = Decimal("0.12345678901234567890123") - var case12_py = pydecimal.Decimal("0.12345678901234567890123") + var case12_mojo = Decimal128("0.12345678901234567890123") + var case12_py = pydecimal.Decimal128("0.12345678901234567890123") run_benchmark( "Rounding to high precision (20 places)", case12_mojo, @@ -318,8 +320,8 @@ fn main() raises: ) # Case 13: Rounding to more places than input has - var case13_mojo = Decimal("123.456") - var case13_py = pydecimal.Decimal("123.456") + var case13_mojo = Decimal128("123.456") + var case13_py = pydecimal.Decimal128("123.456") run_benchmark( "Rounding to more places than input (10)", case13_mojo, @@ -331,8 +333,8 @@ fn main() raises: ) # Case 14: Rounding number with trailing 9's - var case14_mojo = Decimal("9.999") - var case14_py = pydecimal.Decimal("9.999") + var case14_mojo = Decimal128("9.999") + var case14_py = pydecimal.Decimal128("9.999") run_benchmark( "Rounding number with trailing 9's", case14_mojo, @@ -344,8 +346,8 @@ fn main() raises: ) # Case 15: Rounding requiring carry propagation (9.99 -> 10.0) - var case15_mojo = Decimal("9.99") - var case15_py = pydecimal.Decimal("9.99") + var case15_mojo = Decimal128("9.99") + var case15_py = pydecimal.Decimal128("9.99") run_benchmark( "Rounding requiring carry propagation", case15_mojo, @@ -357,8 +359,8 @@ fn main() raises: ) # Case 16: Rounding exactly half with even preceding digit - var case16_mojo = Decimal("2.5") - var case16_py = pydecimal.Decimal("2.5") + var case16_mojo = Decimal128("2.5") + var case16_py = pydecimal.Decimal128("2.5") run_benchmark( "Rounding exactly half with even preceding digit", case16_mojo, @@ -370,8 +372,8 @@ fn main() raises: ) # Case 17: Rounding exactly half with odd preceding digit - var case17_mojo = Decimal("3.5") - var case17_py = pydecimal.Decimal("3.5") + var case17_mojo = Decimal128("3.5") + var case17_py = pydecimal.Decimal128("3.5") run_benchmark( "Rounding exactly half with odd preceding digit", case17_mojo, @@ -383,8 +385,8 @@ fn main() raises: ) # Case 18: Rounding value close to MAX - var case18_mojo = Decimal("12345678901234567890") - Decimal("0.12345") - var case18_py = pydecimal.Decimal(String(case18_mojo)) + var case18_mojo = Decimal128("12345678901234567890") - Decimal128("0.12345") + var case18_py = pydecimal.Decimal128(String(case18_mojo)) run_benchmark( "Rounding value close to MAX", case18_mojo, @@ -396,10 +398,10 @@ fn main() raises: ) # Case 19: Rounding minimum positive value - var case19_mojo = Decimal( + var case19_mojo = Decimal128( "0." + "0" * 27 + "1" ) # Smallest positive decimal - var case19_py = pydecimal.Decimal(String(case19_mojo)) + var case19_py = pydecimal.Decimal128(String(case19_mojo)) run_benchmark( "Rounding minimum positive value", case19_mojo, @@ -411,8 +413,8 @@ fn main() raises: ) # Case 20: Rounding perfectly formatted engineering value - var case20_mojo = Decimal("123456.789e-3") # 123.456789 - var case20_py = pydecimal.Decimal("123456.789e-3") + var case20_mojo = Decimal128("123456.789e-3") # 123.456789 + var case20_py = pydecimal.Decimal128("123456.789e-3") run_benchmark( "Rounding engineering notation value", case20_mojo, @@ -424,8 +426,8 @@ fn main() raises: ) # Case 21: Rounding a number with long integer part - var case21_mojo = Decimal("12345678901234567.8901") - var case21_py = pydecimal.Decimal("12345678901234567.8901") + var case21_mojo = Decimal128("12345678901234567.8901") + var case21_py = pydecimal.Decimal128("12345678901234567.8901") run_benchmark( "Rounding number with long integer part", case21_mojo, @@ -437,8 +439,8 @@ fn main() raises: ) # Case 22: Rounding a number that's just below half - var case22_mojo = Decimal("10.12499999999999999") - var case22_py = pydecimal.Decimal("10.12499999999999999") + var case22_mojo = Decimal128("10.12499999999999999") + var case22_py = pydecimal.Decimal128("10.12499999999999999") run_benchmark( "Rounding number just below half", case22_mojo, @@ -450,8 +452,8 @@ fn main() raises: ) # Case 23: Rounding a number that's just above half - var case23_mojo = Decimal("10.12500000000000001") - var case23_py = pydecimal.Decimal("10.12500000000000001") + var case23_mojo = Decimal128("10.12500000000000001") + var case23_py = pydecimal.Decimal128("10.12500000000000001") run_benchmark( "Rounding number just above half", case23_mojo, @@ -463,8 +465,10 @@ fn main() raises: ) # Case 24: Rounding to max precision (28 places) - var case24_mojo = Decimal("0." + "1" * 29) # More digits than max precision - var case24_py = pydecimal.Decimal(String(case24_mojo)) + var case24_mojo = Decimal128( + "0." + "1" * 29 + ) # More digits than max precision + var case24_py = pydecimal.Decimal128(String(case24_mojo)) run_benchmark( "Rounding to maximum precision (28)", case24_mojo, @@ -476,8 +480,8 @@ fn main() raises: ) # Case 25: Rounding zero - var case25_mojo = Decimal("0") - var case25_py = pydecimal.Decimal("0") + var case25_mojo = Decimal128("0") + var case25_py = pydecimal.Decimal128("0") run_benchmark( "Rounding zero", case25_mojo, @@ -489,8 +493,8 @@ fn main() raises: ) # Case 26: Rounding a series of mixed digits - var case26_mojo = Decimal("3.141592653589793238462643383") - var case26_py = pydecimal.Decimal("3.141592653589793238462643383") + var case26_mojo = Decimal128("3.141592653589793238462643383") + var case26_py = pydecimal.Decimal128("3.141592653589793238462643383") run_benchmark( "Rounding Pi to various places", case26_mojo, @@ -502,8 +506,8 @@ fn main() raises: ) # Case 27: Rounding negative number requiring carry propagation - var case27_mojo = Decimal("-9.99") - var case27_py = pydecimal.Decimal("-9.99") + var case27_mojo = Decimal128("-9.99") + var case27_py = pydecimal.Decimal128("-9.99") run_benchmark( "Rounding negative with carry propagation", case27_mojo, @@ -515,8 +519,8 @@ fn main() raises: ) # Case 28: Rounding with exact .5 and zeros after - var case28_mojo = Decimal("1.5000000000000000000") - var case28_py = pydecimal.Decimal("1.5000000000000000000") + var case28_mojo = Decimal128("1.5000000000000000000") + var case28_py = pydecimal.Decimal128("1.5000000000000000000") run_benchmark( "Rounding with exact .5 and zeros after", case28_mojo, @@ -528,8 +532,8 @@ fn main() raises: ) # Case 29: Rounding with exact .5 and non-zeros after - var case29_mojo = Decimal("1.50000000000000000001") - var case29_py = pydecimal.Decimal("1.50000000000000000001") + var case29_mojo = Decimal128("1.50000000000000000001") + var case29_py = pydecimal.Decimal128("1.50000000000000000001") run_benchmark( "Rounding with exact .5 and non-zeros after", case29_mojo, @@ -541,8 +545,10 @@ fn main() raises: ) # Case 30: Rounding extremely close to MAX value - var case30_mojo = Decimal("123456789012345678") - Decimal("0.000000001") - var case30_py = pydecimal.Decimal(String(case30_mojo)) + var case30_mojo = Decimal128("123456789012345678") - Decimal128( + "0.000000001" + ) + var case30_py = pydecimal.Decimal128(String(case30_mojo)) run_benchmark( "Rounding extremely close to MAX", case30_mojo, @@ -554,8 +560,8 @@ fn main() raises: ) # Case 31: Random decimal with various digits after decimal - var case31_mojo = Decimal("7.389465718934026719043") - var case31_py = pydecimal.Decimal("7.389465718934026719043") + var case31_mojo = Decimal128("7.389465718934026719043") + var case31_py = pydecimal.Decimal128("7.389465718934026719043") run_benchmark( "Random decimal with various digits", case31_mojo, @@ -567,8 +573,8 @@ fn main() raises: ) # Case 32: Number with alternating digits - var case32_mojo = Decimal("1.010101010101010101010101") - var case32_py = pydecimal.Decimal("1.010101010101010101010101") + var case32_mojo = Decimal128("1.010101010101010101010101") + var case32_py = pydecimal.Decimal128("1.010101010101010101010101") run_benchmark( "Number with alternating digits", case32_mojo, diff --git a/benches/decimal/bench_sqrt.mojo b/benches/decimal128/bench_sqrt.mojo similarity index 74% rename from benches/decimal/bench_sqrt.mojo rename to benches/decimal128/bench_sqrt.mojo index 2209ade..76f6434 100644 --- a/benches/decimal/bench_sqrt.mojo +++ b/benches/decimal128/bench_sqrt.mojo @@ -1,9 +1,9 @@ """ -Comprehensive benchmarks for Decimal square root operations. +Comprehensive benchmarks for Decimal128 square root operations. Compares performance against Python's decimal module with 20 diverse test cases. """ -from decimojo.prelude import dm, Decimal, RoundingMode +from decimojo.prelude import dm, Decimal128, RoundingMode from python import Python, PythonObject from time import perf_counter_ns import time @@ -48,27 +48,27 @@ fn log_print(msg: String, log_file: PythonObject) raises: fn run_benchmark( name: String, - d_mojo: Decimal, + d_mojo: Decimal128, d_py: PythonObject, iterations: Int, log_file: PythonObject, ) raises: """ - Run a benchmark comparing Mojo Decimal sqrt with Python Decimal sqrt. + Run a benchmark comparing Mojo Dec128 sqrt with Python decimal sqrt. Args: name: Name of the benchmark case. - d_mojo: Mojo Decimal operand. - d_py: Python Decimal operand. + d_mojo: Mojo Dec128 operand. + d_py: Python decimal operand. iterations: Number of iterations to run. log_file: File object for logging results. """ log_print("\nBenchmark: " + name, log_file) - log_print("Decimal: " + String(d_mojo), log_file) + log_print("Decimal128: " + String(d_mojo), log_file) # Verify correctness - import math module for Python's sqrt var math = Python.import_module("math") - var mojo_result = decimojo.sqrt(d_mojo) + var mojo_result = d_mojo.sqrt() var py_result = math.sqrt(d_py) log_print("Mojo result: " + String(mojo_result), log_file) log_print("Python result: " + String(py_result), log_file) @@ -76,7 +76,7 @@ fn run_benchmark( # Benchmark Mojo implementation var t0 = perf_counter_ns() for _ in range(iterations): - _ = decimojo.sqrt(d_mojo) + _ = d_mojo.sqrt() var mojo_time = (perf_counter_ns() - t0) / iterations # Benchmark Python implementation @@ -87,11 +87,11 @@ fn run_benchmark( # Print results with speedup comparison log_print( - "Mojo Decimal: " + String(mojo_time) + " ns per iteration", + "Mojo decimal: " + String(mojo_time) + " ns per iteration", log_file, ) log_print( - "Python Decimal: " + String(python_time) + " ns per iteration", + "Python decimal: " + String(python_time) + " ns per iteration", log_file, ) log_print("Speedup factor: " + String(python_time / mojo_time), log_file) @@ -132,7 +132,9 @@ fn main() raises: "Python decimal precision: " + String(pydecimal.getcontext().prec), log_file, ) - log_print("Mojo decimal precision: " + String(Decimal.MAX_SCALE), log_file) + log_print( + "Mojo decimal precision: " + String(Decimal128.MAX_SCALE), log_file + ) # Define benchmark cases log_print( @@ -143,8 +145,8 @@ fn main() raises: ) # Case 1: Perfect square (small) - var case1_mojo = Decimal("16") - var case1_py = pydecimal.Decimal("16") + var case1_mojo = Decimal128("16") + var case1_py = pydecimal.Decimal128("16") run_benchmark( "Perfect square (small)", case1_mojo, @@ -154,8 +156,8 @@ fn main() raises: ) # Case 2: Perfect square (large) - var case2_mojo = Decimal("1000000") # 1000^2 - var case2_py = pydecimal.Decimal("1000000") + var case2_mojo = Decimal128("1000000") # 1000^2 + var case2_py = pydecimal.Decimal128("1000000") run_benchmark( "Perfect square (large)", case2_mojo, @@ -165,8 +167,8 @@ fn main() raises: ) # Case 3: Non-perfect square (small irrational) - var case3_mojo = Decimal("2") # sqrt(2) is irrational - var case3_py = pydecimal.Decimal("2") + var case3_mojo = Decimal128("2") # sqrt(2) is irrational + var case3_py = pydecimal.Decimal128("2") run_benchmark( "Non-perfect square (small irrational)", case3_mojo, @@ -176,8 +178,8 @@ fn main() raises: ) # Case 4: Non-perfect square (medium) - var case4_mojo = Decimal("123.456") - var case4_py = pydecimal.Decimal("123.456") + var case4_mojo = Decimal128("123.456") + var case4_py = pydecimal.Decimal128("123.456") run_benchmark( "Non-perfect square (medium)", case4_mojo, @@ -187,8 +189,8 @@ fn main() raises: ) # Case 5: Very small number - var case5_mojo = Decimal("0.0000001") - var case5_py = pydecimal.Decimal("0.0000001") + var case5_mojo = Decimal128("0.0000001") + var case5_py = pydecimal.Decimal128("0.0000001") run_benchmark( "Very small number", case5_mojo, @@ -198,8 +200,8 @@ fn main() raises: ) # Case 6: Very large number - var case6_mojo = Decimal("1" + "0" * 20) # 10^20 - var case6_py = pydecimal.Decimal("1" + "0" * 20) + var case6_mojo = Decimal128("1" + "0" * 20) # 10^20 + var case6_py = pydecimal.Decimal128("1" + "0" * 20) run_benchmark( "Very large number", case6_mojo, @@ -209,8 +211,8 @@ fn main() raises: ) # Case 7: Number just above 1 - var case7_mojo = Decimal("1.0000001") - var case7_py = pydecimal.Decimal("1.0000001") + var case7_mojo = Decimal128("1.0000001") + var case7_py = pydecimal.Decimal128("1.0000001") run_benchmark( "Number just above 1", case7_mojo, @@ -220,8 +222,8 @@ fn main() raises: ) # Case 8: Number just below 1 - var case8_mojo = Decimal("0.9999999") - var case8_py = pydecimal.Decimal("0.9999999") + var case8_mojo = Decimal128("0.9999999") + var case8_py = pydecimal.Decimal128("0.9999999") run_benchmark( "Number just below 1", case8_mojo, @@ -231,8 +233,8 @@ fn main() raises: ) # Case 9: High precision value - var case9_mojo = Decimal("1.23456789012345678901234567") - var case9_py = pydecimal.Decimal("1.23456789012345678901234567") + var case9_mojo = Decimal128("1.23456789012345678901234567") + var case9_py = pydecimal.Decimal128("1.23456789012345678901234567") run_benchmark( "High precision value", case9_mojo, @@ -242,8 +244,8 @@ fn main() raises: ) # Case 10: Number with exact square root in decimal - var case10_mojo = Decimal("0.04") # sqrt = 0.2 - var case10_py = pydecimal.Decimal("0.04") + var case10_mojo = Decimal128("0.04") # sqrt = 0.2 + var case10_py = pydecimal.Decimal128("0.04") run_benchmark( "Number with exact square root", case10_mojo, @@ -253,8 +255,8 @@ fn main() raises: ) # Case 11: Number close to a perfect square - var case11_mojo = Decimal("99.99") # Close to 10² - var case11_py = pydecimal.Decimal("99.99") + var case11_mojo = Decimal128("99.99") # Close to 10² + var case11_py = pydecimal.Decimal128("99.99") run_benchmark( "Number close to a perfect square", case11_mojo, @@ -264,8 +266,8 @@ fn main() raises: ) # Case 12: Even larger perfect square - var case12_mojo = Decimal("1000000000") # 31622.78...^2 - var case12_py = pydecimal.Decimal("1000000000") + var case12_mojo = Decimal128("1000000000") # 31622.78...^2 + var case12_py = pydecimal.Decimal128("1000000000") run_benchmark( "Very large perfect square", case12_mojo, @@ -275,8 +277,8 @@ fn main() raises: ) # Case 13: Number with repeating pattern in result - var case13_mojo = Decimal("3") # sqrt(3) has repeating pattern - var case13_py = pydecimal.Decimal("3") + var case13_mojo = Decimal128("3") # sqrt(3) has repeating pattern + var case13_py = pydecimal.Decimal128("3") run_benchmark( "Number with repeating pattern in result", case13_mojo, @@ -286,8 +288,8 @@ fn main() raises: ) # Case 14: Number with trailing zeros (exact square) - var case14_mojo = Decimal("144.0000") - var case14_py = pydecimal.Decimal("144.0000") + var case14_mojo = Decimal128("144.0000") + var case14_py = pydecimal.Decimal128("144.0000") run_benchmark( "Number with trailing zeros", case14_mojo, @@ -297,8 +299,8 @@ fn main() raises: ) # Case 15: Number slightly larger than a perfect square - var case15_mojo = Decimal("4.0001") - var case15_py = pydecimal.Decimal("4.0001") + var case15_mojo = Decimal128("4.0001") + var case15_py = pydecimal.Decimal128("4.0001") run_benchmark( "Slightly larger than perfect square", case15_mojo, @@ -308,8 +310,8 @@ fn main() raises: ) # Case 16: Number slightly smaller than a perfect square - var case16_mojo = Decimal("15.9999") - var case16_py = pydecimal.Decimal("15.9999") + var case16_mojo = Decimal128("15.9999") + var case16_py = pydecimal.Decimal128("15.9999") run_benchmark( "Slightly smaller than perfect square", case16_mojo, @@ -319,8 +321,8 @@ fn main() raises: ) # Case 17: Number with many decimal places - var case17_mojo = Decimal("0.12345678901234567890") - var case17_py = pydecimal.Decimal("0.12345678901234567890") + var case17_mojo = Decimal128("0.12345678901234567890") + var case17_py = pydecimal.Decimal128("0.12345678901234567890") run_benchmark( "Number with many decimal places", case17_mojo, @@ -330,8 +332,8 @@ fn main() raises: ) # Case 18: Number close to maximum representable value - var case18_mojo = Decimal.MAX() - Decimal("1") - var case18_py = pydecimal.Decimal(String(case18_mojo)) + var case18_mojo = Decimal128.MAX() - Decimal128("1") + var case18_py = pydecimal.Decimal128(String(case18_mojo)) run_benchmark( "Number close to maximum value", case18_mojo, @@ -341,10 +343,10 @@ fn main() raises: ) # Case 19: Very tiny number (close to minimum positive value) - var case19_mojo = Decimal( + var case19_mojo = Decimal128( "0." + "0" * 27 + "1" ) # Smallest positive decimal - var case19_py = pydecimal.Decimal(String(case19_mojo)) + var case19_py = pydecimal.Decimal128(String(case19_mojo)) run_benchmark( "Very tiny positive number", case19_mojo, @@ -354,8 +356,8 @@ fn main() raises: ) # Case 20: Number requiring many Newton-Raphson iterations - var case20_mojo = Decimal("987654321.123456789") - var case20_py = pydecimal.Decimal("987654321.123456789") + var case20_mojo = Decimal128("987654321.123456789") + var case20_py = pydecimal.Decimal128("987654321.123456789") run_benchmark( "Number requiring many iterations", case20_mojo, diff --git a/benches/decimal/bench_subtract.mojo b/benches/decimal128/bench_subtract.mojo similarity index 69% rename from benches/decimal/bench_subtract.mojo rename to benches/decimal128/bench_subtract.mojo index 4249bba..7544800 100644 --- a/benches/decimal/bench_subtract.mojo +++ b/benches/decimal128/bench_subtract.mojo @@ -1,9 +1,9 @@ """ -Comprehensive benchmarks for Decimal subtraction operations. +Comprehensive benchmarks for Decimal128 subtraction operations. Compares performance against Python's decimal module. """ -from decimojo.prelude import dm, Decimal, RoundingMode +from decimojo.prelude import dm, Decimal128, RoundingMode from python import Python, PythonObject from time import perf_counter_ns import time @@ -48,22 +48,22 @@ fn log_print(msg: String, log_file: PythonObject) raises: fn run_benchmark( name: String, - a_mojo: Decimal, - b_mojo: Decimal, + a_mojo: Decimal128, + b_mojo: Decimal128, a_py: PythonObject, b_py: PythonObject, iterations: Int, log_file: PythonObject, ) raises: """ - Run a benchmark comparing Mojo Decimal subtraction with Python Decimal subtraction. + Run a benchmark comparing Mojo Dec128 subtraction with Python decimal subtraction. Args: name: Name of the benchmark case. - a_mojo: First Mojo Decimal operand (minuend). - b_mojo: Second Mojo Decimal operand (subtrahend). - a_py: First Python Decimal operand. - b_py: Second Python Decimal operand. + a_mojo: First Mojo Dec128 operand (minuend). + b_mojo: Second Mojo Dec128 operand (subtrahend). + a_py: First Python decimal operand. + b_py: Second Python decimal operand. iterations: Number of iterations to run. log_file: File object for logging results. """ @@ -92,11 +92,11 @@ fn run_benchmark( # Print results with speedup comparison log_print( - "Mojo Decimal: " + String(mojo_time) + " ns per iteration", + "Mojo decimal: " + String(mojo_time) + " ns per iteration", log_file, ) log_print( - "Python Decimal: " + String(python_time) + " ns per iteration", + "Python decimal: " + String(python_time) + " ns per iteration", log_file, ) log_print("Speedup factor: " + String(python_time / mojo_time), log_file) @@ -137,7 +137,9 @@ fn main() raises: "Python decimal precision: " + String(pydecimal.getcontext().prec), log_file, ) - log_print("Mojo decimal precision: " + String(Decimal.MAX_SCALE), log_file) + log_print( + "Mojo decimal precision: " + String(Decimal128.MAX_SCALE), log_file + ) # Define benchmark cases log_print( @@ -148,10 +150,10 @@ fn main() raises: ) # Case 1: Simple integer subtraction - var case1_a_mojo = Decimal("12345") - var case1_b_mojo = Decimal("6789") - var case1_a_py = pydecimal.Decimal("12345") - var case1_b_py = pydecimal.Decimal("6789") + var case1_a_mojo = Decimal128("12345") + var case1_b_mojo = Decimal128("6789") + var case1_a_py = pydecimal.Decimal128("12345") + var case1_b_py = pydecimal.Decimal128("6789") run_benchmark( "Simple integers", case1_a_mojo, @@ -163,10 +165,10 @@ fn main() raises: ) # Case 2: Simple decimals with same scale - var case2_a_mojo = Decimal("123.45") - var case2_b_mojo = Decimal("67.89") - var case2_a_py = pydecimal.Decimal("123.45") - var case2_b_py = pydecimal.Decimal("67.89") + var case2_a_mojo = Decimal128("123.45") + var case2_b_mojo = Decimal128("67.89") + var case2_a_py = pydecimal.Decimal128("123.45") + var case2_b_py = pydecimal.Decimal128("67.89") run_benchmark( "Simple decimals (same scale)", case2_a_mojo, @@ -178,10 +180,10 @@ fn main() raises: ) # Case 3: Decimals with different scales - var case3_a_mojo = Decimal("123.4") - var case3_b_mojo = Decimal("67.89") - var case3_a_py = pydecimal.Decimal("123.4") - var case3_b_py = pydecimal.Decimal("67.89") + var case3_a_mojo = Decimal128("123.4") + var case3_b_mojo = Decimal128("67.89") + var case3_a_py = pydecimal.Decimal128("123.4") + var case3_b_py = pydecimal.Decimal128("67.89") run_benchmark( "Decimals with different scales", case3_a_mojo, @@ -193,10 +195,10 @@ fn main() raises: ) # Case 4: Subtraction resulting in negative number - var case4_a_mojo = Decimal("67.89") - var case4_b_mojo = Decimal("123.45") - var case4_a_py = pydecimal.Decimal("67.89") - var case4_b_py = pydecimal.Decimal("123.45") + var case4_a_mojo = Decimal128("67.89") + var case4_b_mojo = Decimal128("123.45") + var case4_a_py = pydecimal.Decimal128("67.89") + var case4_b_py = pydecimal.Decimal128("123.45") run_benchmark( "Subtraction resulting in negative", case4_a_mojo, @@ -208,10 +210,10 @@ fn main() raises: ) # Case 5: Subtraction with negative number (a - (-b) = a + b) - var case5_a_mojo = Decimal("123.45") - var case5_b_mojo = Decimal("-67.89") - var case5_a_py = pydecimal.Decimal("123.45") - var case5_b_py = pydecimal.Decimal("-67.89") + var case5_a_mojo = Decimal128("123.45") + var case5_b_mojo = Decimal128("-67.89") + var case5_a_py = pydecimal.Decimal128("123.45") + var case5_b_py = pydecimal.Decimal128("-67.89") run_benchmark( "Subtracting a negative", case5_a_mojo, @@ -223,10 +225,10 @@ fn main() raises: ) # Case 6: Requiring borrow across multiple digits - var case6_a_mojo = Decimal("10000.00") - var case6_b_mojo = Decimal("0.01") - var case6_a_py = pydecimal.Decimal("10000.00") - var case6_b_py = pydecimal.Decimal("0.01") + var case6_a_mojo = Decimal128("10000.00") + var case6_b_mojo = Decimal128("0.01") + var case6_a_py = pydecimal.Decimal128("10000.00") + var case6_b_py = pydecimal.Decimal128("0.01") run_benchmark( "Requiring borrow across multiple digits", case6_a_mojo, @@ -238,10 +240,10 @@ fn main() raises: ) # Case 7: Subtraction with small difference - var case7_a_mojo = Decimal("1.0000001") - var case7_b_mojo = Decimal("1.0000000") - var case7_a_py = pydecimal.Decimal("1.0000001") - var case7_b_py = pydecimal.Decimal("1.0000000") + var case7_a_mojo = Decimal128("1.0000001") + var case7_b_mojo = Decimal128("1.0000000") + var case7_a_py = pydecimal.Decimal128("1.0000001") + var case7_b_py = pydecimal.Decimal128("1.0000000") run_benchmark( "Small difference", case7_a_mojo, @@ -253,10 +255,10 @@ fn main() raises: ) # Case 8: High precision subtraction - var case8_a_mojo = Decimal("0.123456789012345678901234567") - var case8_b_mojo = Decimal("0.023456789012345678901234567") - var case8_a_py = pydecimal.Decimal("0.123456789012345678901234567") - var case8_b_py = pydecimal.Decimal("0.023456789012345678901234567") + var case8_a_mojo = Decimal128("0.123456789012345678901234567") + var case8_b_mojo = Decimal128("0.023456789012345678901234567") + var case8_a_py = pydecimal.Decimal128("0.123456789012345678901234567") + var case8_b_py = pydecimal.Decimal128("0.023456789012345678901234567") run_benchmark( "High precision subtraction", case8_a_mojo, @@ -268,10 +270,10 @@ fn main() raises: ) # Case 9: Subtraction resulting in zero - var case9_a_mojo = Decimal("123.45") - var case9_b_mojo = Decimal("123.45") - var case9_a_py = pydecimal.Decimal("123.45") - var case9_b_py = pydecimal.Decimal("123.45") + var case9_a_mojo = Decimal128("123.45") + var case9_b_mojo = Decimal128("123.45") + var case9_a_py = pydecimal.Decimal128("123.45") + var case9_b_py = pydecimal.Decimal128("123.45") run_benchmark( "Subtraction resulting in zero", case9_a_mojo, @@ -283,10 +285,10 @@ fn main() raises: ) # Case 10: Very large numbers subtraction - var case10_a_mojo = Decimal("79228162514264337593543950334") # MAX - 1 - var case10_b_mojo = Decimal("79228162514264337593543950333") # MAX - 2 - var case10_a_py = pydecimal.Decimal("79228162514264337593543950334") - var case10_b_py = pydecimal.Decimal("79228162514264337593543950333") + var case10_a_mojo = Decimal128("79228162514264337593543950334") # MAX - 1 + var case10_b_mojo = Decimal128("79228162514264337593543950333") # MAX - 2 + var case10_a_py = pydecimal.Decimal128("79228162514264337593543950334") + var case10_b_py = pydecimal.Decimal128("79228162514264337593543950333") run_benchmark( "Very large numbers", case10_a_mojo, diff --git a/benches/decimal/bench_truncate_divide.mojo b/benches/decimal128/bench_truncate_divide.mojo similarity index 94% rename from benches/decimal/bench_truncate_divide.mojo rename to benches/decimal128/bench_truncate_divide.mojo index e8b2ec2..e6641da 100644 --- a/benches/decimal/bench_truncate_divide.mojo +++ b/benches/decimal128/bench_truncate_divide.mojo @@ -1,9 +1,9 @@ """ -Comprehensive benchmarks for Decimal floor division (//) operation. +Comprehensive benchmarks for Decimal128 floor division (//) operation. Compares performance against Python's decimal module with diverse test cases. """ -from decimojo.prelude import dm, Decimal, RoundingMode +from decimojo.prelude import dm, Decimal128, RoundingMode from python import Python, PythonObject from time import perf_counter_ns import time @@ -58,7 +58,7 @@ fn run_benchmark_truncate_divide( mut speedup_factors: List[Float64], ) raises: """ - Run a benchmark comparing Mojo Decimal floor division with Python Decimal floor division. + Run a benchmark comparing Mojo Dec128 floor division with Python decimal floor division. Args: name: Name of the benchmark case. @@ -73,11 +73,11 @@ fn run_benchmark_truncate_divide( log_print("Divisor: " + divisor, log_file) # Set up Mojo and Python values - var mojo_dividend = Decimal(dividend) - var mojo_divisor = Decimal(divisor) + var mojo_dividend = Decimal128(dividend) + var mojo_divisor = Decimal128(divisor) var pydecimal = Python.import_module("decimal") - var py_dividend = pydecimal.Decimal(dividend) - var py_divisor = pydecimal.Decimal(divisor) + var py_dividend = pydecimal.Decimal128(dividend) + var py_divisor = pydecimal.Decimal128(divisor) # Execute the operations once to verify correctness try: @@ -159,7 +159,9 @@ fn main() raises: "Python decimal precision: " + String(pydecimal.getcontext().prec), log_file, ) - log_print("Mojo decimal precision: " + String(Decimal.MAX_SCALE), log_file) + log_print( + "Mojo decimal precision: " + String(Decimal128.MAX_SCALE), log_file + ) # Define benchmark cases log_print( @@ -191,7 +193,7 @@ fn main() raises: # Case 3: Division with decimal values run_benchmark_truncate_divide( - "Decimal division", + "Decimal128 division", "10.5", "2.5", iterations, @@ -249,9 +251,9 @@ fn main() raises: speedup_factors, ) - # Case 9: Decimal values, negative dividend, positive divisor + # Case 9: Decimal128 values, negative dividend, positive divisor run_benchmark_truncate_divide( - "Decimal, negative dividend, positive divisor", + "Decimal128, negative dividend, positive divisor", "-10.5", "3.5", iterations, diff --git a/docs/examples.md b/docs/examples.md deleted file mode 100644 index a43b82e..0000000 --- a/docs/examples.md +++ /dev/null @@ -1,162 +0,0 @@ -# Examples on Decimal - -Here are 8 key examples highlighting the most important features of the `Decimal` type in its current state: - -## 1. Fixed-Point Precision for Financial Calculations - -```mojo -from decimojo import dm, Decimal - -# The classic floating-point problem -print(0.1 + 0.2) # 0.30000000000000004 (not exactly 0.3) - -# Decimal solves this with exact representation -var d1 = Decimal("0.1") -var d2 = Decimal("0.2") -var sum = d1 + d2 -print(sum) # Exactly 0.3 - -# Financial calculation example - computing tax -var price = Decimal("19.99") -var tax_rate = Decimal("0.0725") -var tax = price * tax_rate # Exactly 1.449275 -var total = price + tax # Exactly 21.439275 -``` - -## 2. Basic Arithmetic with Proper Banker's Rounding - -```mojo -# Addition with different scales -var a = Decimal("123.45") -var b = Decimal("67.8") -print(a + b) # 191.25 (preserves highest precision) - -# Subtraction with negative result -var c = Decimal("50") -var d = Decimal("75.25") -print(c - d) # -25.25 - -# Multiplication with banker's rounding (round to even) -var e = Decimal("12.345") -var f = Decimal("5.67") -print(round(e * f, 2)) # 69.96 (rounds to nearest even) - -# Division with banker's rounding -var g = Decimal("10") -var h = Decimal("3") -print(round(g / h, 2)) # 3.33 (rounded banker's style) -``` - -## 3. Scale and Precision Management - -```mojo -# Scale refers to number of decimal places -var d1 = Decimal("123.45") -print(d1.scale()) # 2 - -# Precision control with explicit rounding -var d2 = Decimal("123.456") -print(d2.round_to_scale(1)) # 123.5 (banker's rounding) - -# High precision is preserved (up to 28 decimal places) -var precise = Decimal("0.1234567890123456789012345678") -print(precise) # 0.1234567890123456789012345678 -``` - -## 4. Sign Handling and Absolute Value - -```mojo -# Negation operator -var pos = Decimal("123.45") -var neg = -pos -print(neg) # -123.45 - -# Absolute value -var abs_val = abs(Decimal("-987.65")) -print(abs_val) # 987.65 - -# Sign checking -print(Decimal("-123.45").is_negative()) # True -print(Decimal("0").is_negative()) # False - -# Sign preservation in multiplication -print(Decimal("-5") * Decimal("3")) # -15 -print(Decimal("-5") * Decimal("-3")) # 15 -``` - -## 5. Advanced Mathematical Operations - -```mojo -# Highly accurate square root implementation -var root2 = Decimal("2").sqrt() -print(root2) # 1.4142135623730950488016887242 - -# Square root of imperfect squares -var root_15_9999 = Decimal("15.9999").sqrt() -print(root_15_9999) # 3.9999874999804686889646053305 - -# Integer powers with fast binary exponentiation -var cubed = Decimal("3") ** 3 -print(cubed) # 27 - -# Negative powers (reciprocals) -var recip = Decimal("2") ** (-1) -print(recip) # 0.5 -``` - -## 6. Robust Edge Case Handling - -```mojo -# Division by zero is properly caught -try: - var result = Decimal("10") / Decimal("0") -except: - print("Division by zero properly detected") - -# Zero raised to negative power -try: - var invalid = Decimal("0") ** (-1) -except: - print("Zero to negative power properly detected") - -# Overflow detection and prevention -var max_val = Decimal.MAX() -try: - var overflow = max_val * Decimal("2") -except: - print("Overflow correctly detected") -``` - -## 7. Equality and Comparison Operations - -```mojo -# Equal values with different scales -var a = Decimal("123.4500") -var b = Decimal("123.45") -print(a == b) # True (numeric value equality) - -# Comparison operators -var c = Decimal("100") -var d = Decimal("200") -print(c < d) # True -print(c <= d) # True -print(c > d) # False -print(c >= d) # False -print(c != d) # True -``` - -## 8. Real World Financial Examples - -```mojo -# Monthly loan payment calculation with precise interest -var principal = Decimal("200000") # $200,000 loan -var annual_rate = Decimal("0.05") # 5% interest rate -var monthly_rate = annual_rate / Decimal("12") -var num_payments = Decimal("360") # 30 years - -# Monthly payment formula: P * r(1+r)^n/((1+r)^n-1) -var numerator = monthly_rate * (Decimal("1") + monthly_rate) ** 360 -var denominator = (Decimal("1") + monthly_rate) ** 360 - Decimal("1") -var payment = principal * (numerator / denominator) -print("Monthly payment: $" + String(round(payment, 2))) # $1,073.64 -``` diff --git a/docs/examples_on_bdec.mojo b/docs/examples_on_bdec.mojo index c3c2417..b26aa6c 100644 --- a/docs/examples_on_bdec.mojo +++ b/docs/examples_on_bdec.mojo @@ -2,14 +2,16 @@ from decimojo.prelude import * fn main() raises: - var a = BDec("123456789.123456789") - var b = BDec("1234.56789") + var a = BDec("123456789.123456789") # BDec is an alias for BigDecimal + var b = Decimal( + "1234.56789" + ) # Decimal is a Python-like alias for BigDecimal # === Basic Arithmetic === # print(a + b) # 123458023.691346789 print(a - b) # 123455554.555566789 print(a * b) # 152415787654.32099750190521 - print(a.true_divide(b, precision=80)) # 100000.0001 + print(a.true_divide(b + 1)) # 99919.0656560820700835791386582569736 # === Exponential Functions === # print(a.sqrt(precision=80)) @@ -47,7 +49,7 @@ fn main() raises: # === Internal representation of the number === # ( - BDec( + Decimal( "3.141592653589793238462643383279502884197169399375105820974944" ).power(2, precision=60) ).print_internal_representation() diff --git a/docs/readme_zht.md b/docs/readme_zht.md index d4a5c30..e48bfc0 100644 --- a/docs/readme_zht.md +++ b/docs/readme_zht.md @@ -10,7 +10,7 @@ DeciMojo 爲 Mojo 提供了全面的定點數和整數運算庫,專爲處理 核心類型包括: -- 128 位定點數 (`Decimal`),支持最多 29 位有效數字,小數點後最多 28 位數字[^fixed]。 +- 128 位定點數 (`Decimal128`),支持最多 29 位有效數字,小數點後最多 28 位數字[^fixed]。 - 任意精度定點數 (`BigDecimal`) ,允許進行無限位數和小數位的計算。 - 基於 10 進制的任意精度有符號整數 (`BigInt`) 和任意精度無符號整數 (`BigUInt`)[^integer],支持無限位數。它具有全面的算術運算、比較功能,並能高效支持超大整數計算。 @@ -32,113 +32,6 @@ DeciMojo 可在 [modular-community](https://repo.prefix.dev/modular-community) | v0.3.1 | >=25.2, <25.4 | pixi | | v0.4.x | ==25.4 | pixi | -## 快速入門 - -以下是展示 `Decimal` 類型每個主要功能的全面快速入門指南。 - -```mojo -from decimojo import Decimal, RoundingMode - -fn main() raises: - # === 構造 === - var a = Decimal("123.45") # 從字符串 - var b = Decimal(123) # 從整數 - var c = Decimal(123, 2) # 帶比例的整數 (1.23) - var d = Decimal.from_float(3.14159) # 從浮點數 - - # === 基本算術 === - print(a + b) # 加法: 246.45 - print(a - b) # 減法: 0.45 - print(a * b) # 乘法: 15184.35 - print(a / b) # 除法: 1.0036585365853658536585365854 - - # === 捨入與精度 === - print(a.round(1)) # 捨入到1位小數: 123.5 - print(a.quantize(Decimal("0.01"))) # 格式化到2位小數: 123.45 - print(a.round(0, RoundingMode.ROUND_DOWN)) # 向下捨入到整數: 123 - - # === 比較 === - print(a > b) # 大於: True - print(a == Decimal("123.45")) # 等於: True - print(a.is_zero()) # 檢查是否爲零: False - print(Decimal("0").is_zero()) # 檢查是否爲零: True - - # === 類型轉換 === - print(Float64(a)) # 轉爲浮點數: 123.45 - print(a.to_int()) # 轉爲整數: 123 - print(a.to_str()) # 轉爲字符串: "123.45" - print(a.coefficient()) # 獲取係數: 12345 - print(a.scale()) # 獲取比例: 2 - - # === 數學函數 === - print(Decimal("2").sqrt()) # 平方根: 1.4142135623730950488016887242 - print(Decimal("100").root(3)) # 立方根: 4.641588833612778892410076351 - print(Decimal("2.71828").ln()) # 自然對數: 0.9999993273472820031578910056 - print(Decimal("10").log10()) # 10爲底的對數: 1 - print(Decimal("16").log(Decimal("2"))) # 以2爲底的對數: 3.9999999999999999999999999999 - print(Decimal("10").exp()) # e^10: 22026.465794806716516957900645 - print(Decimal("2").power(10)) # 冪: 1024 - - # === 符號處理 === - print(-a) # 取反: -123.45 - print(abs(Decimal("-123.45"))) # 絕對值: 123.45 - print(Decimal("123.45").is_negative()) # 檢查是否爲負: False - - # === 特殊值 === - print(Decimal.PI()) # π常數: 3.1415926535897932384626433833 - print(Decimal.E()) # e常數: 2.7182818284590452353602874714 - print(Decimal.ONE()) # 值1: 1 - print(Decimal.ZERO()) # 值0: 0 - print(Decimal.MAX()) # 最大值: 79228162514264337593543950335 - - # === 便捷方法 === - print(Decimal("123.400").is_integer()) # 檢查是否爲整數: False - print(a.number_of_significant_digits()) # 計算有效數字: 5 - print(Decimal("12.34").to_str_scientific()) # 科學計數法: 1.234E+1 -``` - -以下是展示 `BigInt` 類型每個主要功能的全面快速入門指南。 - -```mojo -from decimojo import BigInt - -fn main() raises: - # === 構造 === - var a = BigInt("12345678901234567890") # 從字符串構造 - var b = BigInt(12345) # 從整數構造 - - # === 基本算術 === - print(a + b) # 加法: 12345678901234580235 - print(a - b) # 減法: 12345678901234555545 - print(a * b) # 乘法: 152415787814108380241050 - - # === 除法運算 === - print(a // b) # 向下整除: 999650944609516 - print(a.truncate_divide(b)) # 截斷除法: 999650944609516 - print(a % b) # 取模: 9615 - - # === 冪運算 === - print(BigInt(2).power(10)) # 冪: 1024 - print(BigInt(2) ** 10) # 冪 (使用 ** 運算符): 1024 - - # === 比較 === - print(a > b) # 大於: True - print(a == BigInt("12345678901234567890")) # 等於: True - print(a.is_zero()) # 檢查是否爲零: False - - # === 類型轉換 === - print(a.to_str()) # 轉爲字符串: "12345678901234567890" - - # === 符號處理 === - print(-a) # 取反: -12345678901234567890 - print(abs(BigInt("-12345678901234567890"))) # 絕對值: 12345678901234567890 - print(a.is_negative()) # 檢查是否爲負: False - - # === 超大數值計算 === - # 3600 位數 // 1800 位數 - print(BigInt("123456789" * 400) // BigInt("987654321" * 200)) -``` - ## 目標 金融計算和數據分析需要精確的小數算術,而浮點數無法可靠地提供這種精確性。作爲一名從事金融學研究和信用風險模型驗證工作的人員,在將個人項目從 Python 遷移到 Mojo 時,我需要一個可靠的、能够正確捨入的、固定精度的數值類型。 @@ -160,46 +53,7 @@ DeciMojo 結合了 "Deci" 和 "Mojo" 兩詞,反映了其目的和實現語言 ## 狀態 -羅馬不是一日建成的。DeciMojo 目前正在積極開發中。對於 128 位的 `Decimal` 類型,它已成功通過 **"讓它工作"** 階段,並已深入 **"讓它正確"** 階段,同時已實施多項優化。歡迎錯誤報告和功能請求!如果您遇到問題,請[在此提交](https://github.com/forfudan/decimojo/issues)。 - -### 讓它工作 ✅(已完成) - -- 核心小數實現採用穩健的 128 位表示(96 位係數 + 32 位標誌) -- 全面的算術運算(+, -, *, /, %, **)並正確處理溢出 -- 各類型間的轉換(字符串、整數、浮點數等) -- 特殊值(NaN、無限)的適當表示 -- 具有正確十進制語義的完整比較運算符集 - -### 讓它正確 🔄(大部分完成) - -- 重組的代碼庫具有模塊化結構(小數、算術、比較、指數等) -- 處理所有運算的邊緣情況(除以零、零的負冪等) -- 精度和比例管理表現出複雜性 -- 財務計算展示了正確的捨入(ROUND_HALF_EVEN) -- 高精度高級數學函數(開方、根源、對數、指數等) -- 正確實現特徵(可取絶對值、可比較、可轉爲浮點、可捨入等) -- **BigInt 和 BigUInt** 實現提供完整的算術運算、正確的除法語義(向下整除和截斷除法),以及支持任意精度計算 - -### 讓它快速 ⚡(顯著進展) - -DeciMojo 相較於 Python 的 `decimal` 模塊提供了卓越的性能,同時保持計算精確度。這一性能差異源於基本設計選擇: - -- **DeciMojo**:使用固定的 128 位表示(96 位係數 + 32 位標誌),最多支持 28 位小數,針對現代硬件和 Mojo 的性能能力進行了優化。 -- **Python decimal**:實現任意精度,可表示具有無限有效位數的數字,但需要動態内存分配和更複雜的算法。 - -此架構差異解釋了我們的基準測試結果: - -- 核心算術運算(+, -, *, /)比 Python 的 decimal 模塊快 100-3500 倍。 -- 特殊情況處理(0 的冪、1 的冪等)顯示出高達 3500 倍的性能提升。 -- 高級數學函數(sqrt、ln、exp)展示了 5-600 倍的更好性能。 -- 只有特定的邊緣情況(例如計算 10^(1/100))偶爾在 Python 中表現更好,這是由於其任意精度算法。 - -`bench/` 文件夾中提供了與 Python 的 `decimal` 模塊的定期基準測試,記錄了性能優勢以及需要不同方法的少數特定操作。 - -### 未來擴展 🚀(計劃中) - -- **BigDecimal**:🔄 **進行中** - 具有可配置精度的任意精度小數類型[^arbitrary]。 -- **BigComplex**:📝 **計劃中** - 基於 BigDecimal 構建的任意精度複數類型。 +羅馬不是一日建成的。DeciMojo 目前正在積極開發中。它已成功通過 **"讓它工作"** 階段,並已深入 **"讓它正確"** 階段,同時已實施多項優化。歡迎錯誤報告和功能請求!如果您遇到問題,請[在此提交](https://github.com/forfudan/decimojo/issues)。 ## 測試與基準 @@ -227,8 +81,6 @@ DeciMojo 相較於 Python 的 `decimal` 模塊提供了卓越的性能,同時 本倉庫及其所有貢獻内容均採用 Apache 許可證 2.0 版本授權。 -[^fixed]: Decimal 類型可以表示最多 29 位有效數字,小數點後最多 28 位數字的值。當數值超過最大可表示值(2^96 - 1)時,DeciMojo 會拋出錯誤或將數值捨入以符合這些約束。例如,8.8888888888888888888888888888(總共 29 個 8,小數點後 28 位)的有效數字超過了最大可表示值(2^96 - 1),會自動捨入爲 8.888888888888888888888888889(總共 28 個 8,小數點後 27 位)。DeciMojo 的 Decimal 類型類似於 System.Decimal(C#/.NET)、Rust 中的 rust_decimal、SQL Server 中的 DECIMAL/NUMERIC 等。 +[^fixed]: Decimal128 類型可以表示最多 29 位有效數字,小數點後最多 28 位數字的值。當數值超過最大可表示值(2^96 - 1)時,DeciMojo 會拋出錯誤或將數值捨入以符合這些約束。例如,8.8888888888888888888888888888(總共 29 個 8,小數點後 28 位)的有效數字超過了最大可表示值(2^96 - 1),會自動捨入爲 8.888888888888888888888888889(總共 28 個 8,小數點後 27 位)。DeciMojo 的 Decimal128 類型類似於 System.Decimal(C#/.NET)、Rust 中的 rust_decimal、SQL Server 中的 DECIMAL/NUMERIC 等。 [^integer]: BigInt 實現使用基於 10 的表示進行高效存儲和計算,支持對具有無限精度的整數進行操作。它提供了向下整除(向負無窮舍入)和截斷除法(向零舍入)語義,無論操作數符號如何,都能確保除法操作處理具有正確的數學行爲。 - -[^arbitrary]: 基於已完成的 BigInt 實現構建,BigDecimal 將支持整數和小數部分的任意精度,類似於 Python 中的 decimal 和 mpmath、Java 中的 java.math.BigDecimal 等。 diff --git a/src/decimojo/__init__.mojo b/src/decimojo/__init__.mojo index e5e6744..ab089ac 100644 --- a/src/decimojo/__init__.mojo +++ b/src/decimojo/__init__.mojo @@ -25,10 +25,10 @@ from decimojo import Decimal, BigInt, RoundingMode """ # Core types -from .decimal.decimal import Decimal, Dec +from .decimal128.decimal128 import Decimal128, Dec128 from .bigint.bigint import BigInt, BInt from .biguint.biguint import BigUInt, BUInt -from .bigdecimal.bigdecimal import BigDecimal, BDec +from .bigdecimal.bigdecimal import BigDecimal, BDec, Decimal from .rounding_mode import ( RoundingMode, RM, @@ -37,26 +37,3 @@ from .rounding_mode import ( ROUND_HALF_EVEN, ROUND_UP, ) - -# Core functions -from .decimal.arithmetics import ( - add, - subtract, - absolute, - negative, - multiply, - divide, - truncate_divide, - modulo, -) -from .decimal.comparison import ( - greater, - greater_equal, - less, - less_equal, - equal, - not_equal, -) -from .decimal.exponential import power, root, sqrt, exp, ln, log, log10 -from .decimal.rounding import round, quantize -from .decimal.special import factorial diff --git a/src/decimojo/bigdecimal/arithmetics.mojo b/src/decimojo/bigdecimal/arithmetics.mojo index 9f4e315..d35939c 100644 --- a/src/decimojo/bigdecimal/arithmetics.mojo +++ b/src/decimojo/bigdecimal/arithmetics.mojo @@ -21,7 +21,6 @@ Implements functions for mathematical operations on BigDecimal objects. import math from decimojo.rounding_mode import RoundingMode -import decimojo.utility # ===----------------------------------------------------------------------=== # # Arithmetic operations on BigDecimal objects diff --git a/src/decimojo/bigdecimal/bigdecimal.mojo b/src/decimojo/bigdecimal/bigdecimal.mojo index a5866e8..28a81b4 100644 --- a/src/decimojo/bigdecimal/bigdecimal.mojo +++ b/src/decimojo/bigdecimal/bigdecimal.mojo @@ -31,8 +31,13 @@ from decimojo.bigdecimal.rounding import round_to_precision alias BDec = BigDecimal """Short alias for `BigDecimal`.""" -alias bdec = BigDecimal -"""Short alias for `BigDecimal` constructor.""" +alias Decimal = BigDecimal +"""Python-like alias for `BigDecimal`.""" + +alias PRECISION = 36 +"""Default precision for BigDecimal operations. +This will be configurable in future when Mojo supports global variables. +""" @value @@ -521,7 +526,7 @@ struct BigDecimal( @always_inline fn __truediv__(self, other: Self) raises -> Self: return decimojo.bigdecimal.arithmetics.true_divide( - self, other, precision=28 + self, other, precision=PRECISION ) @always_inline @@ -537,14 +542,14 @@ struct BigDecimal( See `arithmetics.truncate_modulo()` for more information. """ return decimojo.bigdecimal.arithmetics.truncate_modulo( - self, other, precision=28 + self, other, precision=PRECISION ) @always_inline fn __pow__(self, exponent: Self) raises -> Self: """Returns the result of exponentiation.""" return decimojo.bigdecimal.exponential.power( - self, exponent, precision=28 + self, exponent, precision=PRECISION ) # ===------------------------------------------------------------------=== # @@ -572,12 +577,14 @@ struct BigDecimal( @always_inline fn __rmod__(self, other: Self) raises -> Self: return decimojo.bigdecimal.arithmetics.truncate_modulo( - other, self, precision=28 + other, self, precision=PRECISION ) @always_inline fn __rpow__(self, base: Self) raises -> Self: - return decimojo.bigdecimal.exponential.power(base, self, precision=28) + return decimojo.bigdecimal.exponential.power( + base, self, precision=PRECISION + ) # ===------------------------------------------------------------------=== # # Basic binary augmented arithmetic assignments dunders @@ -601,7 +608,7 @@ struct BigDecimal( @always_inline fn __itruediv__(mut self, other: Self) raises: self = decimojo.bigdecimal.arithmetics.true_divide( - self, other, precision=28 + self, other, precision=PRECISION ) # ===------------------------------------------------------------------=== # @@ -727,43 +734,43 @@ struct BigDecimal( # === Exponentional operations === # @always_inline - fn exp(self, precision: Int = 28) raises -> Self: + fn exp(self, precision: Int = PRECISION) raises -> Self: """Returns the exponential of the BigDecimal number.""" return decimojo.bigdecimal.exponential.exp(self, precision) @always_inline - fn ln(self, precision: Int = 28) raises -> Self: + fn ln(self, precision: Int = PRECISION) raises -> Self: """Returns the natural logarithm of the BigDecimal number.""" return decimojo.bigdecimal.exponential.ln(self, precision) @always_inline - fn log(self, base: Self, precision: Int = 28) raises -> Self: + fn log(self, base: Self, precision: Int = PRECISION) raises -> Self: """Returns the logarithm of the BigDecimal number with the given base. """ return decimojo.bigdecimal.exponential.log(self, base, precision) @always_inline - fn log10(self, precision: Int = 28) raises -> Self: + fn log10(self, precision: Int = PRECISION) raises -> Self: """Returns the base-10 logarithm of the BigDecimal number.""" return decimojo.bigdecimal.exponential.log10(self, precision) @always_inline - fn root(self, root: Self, precision: Int = 28) raises -> Self: + fn root(self, root: Self, precision: Int = PRECISION) raises -> Self: """Returns the root of the BigDecimal number.""" return decimojo.bigdecimal.exponential.root(self, root, precision) @always_inline - fn sqrt(self, precision: Int = 28) raises -> Self: + fn sqrt(self, precision: Int = PRECISION) raises -> Self: """Returns the square root of the BigDecimal number.""" return decimojo.bigdecimal.exponential.sqrt(self, precision) @always_inline - fn cbrt(self, precision: Int = 28) raises -> Self: + fn cbrt(self, precision: Int = PRECISION) raises -> Self: """Returns the cube root of the BigDecimal number.""" return decimojo.bigdecimal.exponential.cbrt(self, precision) @always_inline - fn power(self, exponent: Self, precision: Int) raises -> Self: + fn power(self, exponent: Self, precision: Int = PRECISION) raises -> Self: """Returns the result of exponentiation with the given precision. See `exponential.power()` for more information. """ @@ -771,44 +778,46 @@ struct BigDecimal( # === Trigonometric operations === # @always_inline - fn sin(self, precision: Int = 28) raises -> Self: + fn sin(self, precision: Int = PRECISION) raises -> Self: """Returns the sine of the BigDecimal number.""" return decimojo.bigdecimal.trigonometric.sin(self, precision) @always_inline - fn cos(self, precision: Int = 28) raises -> Self: + fn cos(self, precision: Int = PRECISION) raises -> Self: """Returns the cosine of the BigDecimal number.""" return decimojo.bigdecimal.trigonometric.cos(self, precision) @always_inline - fn tan(self, precision: Int = 28) raises -> Self: + fn tan(self, precision: Int = PRECISION) raises -> Self: """Returns the tangent of the BigDecimal number.""" return decimojo.bigdecimal.trigonometric.tan(self, precision) @always_inline - fn cot(self, precision: Int = 28) raises -> Self: + fn cot(self, precision: Int = PRECISION) raises -> Self: """Returns the cotangent of the BigDecimal number.""" return decimojo.bigdecimal.trigonometric.cot(self, precision) @always_inline - fn csc(self, precision: Int = 28) raises -> Self: + fn csc(self, precision: Int = PRECISION) raises -> Self: """Returns the cosecant of the BigDecimal number.""" return decimojo.bigdecimal.trigonometric.csc(self, precision) @always_inline - fn sec(self, precision: Int = 28) raises -> Self: + fn sec(self, precision: Int = PRECISION) raises -> Self: """Returns the secant of the BigDecimal number.""" return decimojo.bigdecimal.trigonometric.sec(self, precision) @always_inline - fn arctan(self, precision: Int = 28) raises -> Self: + fn arctan(self, precision: Int = PRECISION) raises -> Self: """Returns the arctangent of the BigDecimal number.""" return decimojo.bigdecimal.trigonometric.arctan(self, precision) # === Arithmetic operations === # @always_inline - fn true_divide(self, other: Self, precision: Int) raises -> Self: + fn true_divide( + self, other: Self, precision: Int = PRECISION + ) raises -> Self: """Returns the result of true division of two BigDecimal numbers. See `arithmetics.true_divide()` for more information. """ diff --git a/src/decimojo/bigdecimal/exponential.mojo b/src/decimojo/bigdecimal/exponential.mojo index eae42e9..bae5365 100644 --- a/src/decimojo/bigdecimal/exponential.mojo +++ b/src/decimojo/bigdecimal/exponential.mojo @@ -18,7 +18,6 @@ from decimojo.bigdecimal.bigdecimal import BigDecimal from decimojo.rounding_mode import RoundingMode -import decimojo.utility # ===----------------------------------------------------------------------=== # # List of functions in this module: @@ -532,7 +531,7 @@ fn sqrt_decimal_approach(x: BigDecimal, precision: Int) raises -> BigDecimal: ) if odd_ndigits_frac_part: value = value * UInt128(10) - var sqrt_value = decimojo.utility.sqrt(value) + var sqrt_value = decimojo.decimal128.utility.sqrt(value) var sqrt_value_biguint = BigUInt.from_unsigned_integral_scalar(sqrt_value) guess = BigDecimal( sqrt_value_biguint, diff --git a/src/decimojo/bigdecimal/trigonometric.mojo b/src/decimojo/bigdecimal/trigonometric.mojo index 534deb5..caf78a2 100644 --- a/src/decimojo/bigdecimal/trigonometric.mojo +++ b/src/decimojo/bigdecimal/trigonometric.mojo @@ -22,7 +22,6 @@ import time from decimojo.bigdecimal.bigdecimal import BigDecimal from decimojo.rounding_mode import RoundingMode -import decimojo.utility import decimojo.bigdecimal.constants diff --git a/src/decimojo/decimal/constants.mojo b/src/decimojo/decimal/constants.mojo deleted file mode 100644 index 7f4c575..0000000 --- a/src/decimojo/decimal/constants.mojo +++ /dev/null @@ -1,625 +0,0 @@ -# ===----------------------------------------------------------------------=== # -# Copyright 2025 Yuhao Zhu -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ===----------------------------------------------------------------------=== # -# -# Useful constants for Decimal type -# -# ===----------------------------------------------------------------------=== # - -"""Useful constants for Decimal type.""" - -from decimojo.decimal.decimal import Decimal - -# ===----------------------------------------------------------------------=== # -# -# Integer and decimal constants -# The prefix "M" stands for a decimal (money) value. -# This is a convention in C. -# -# ===----------------------------------------------------------------------=== # - -# Integer constants - - -@always_inline -fn M0() -> Decimal: - """Returns 0 as a Decimal.""" - return Decimal(0x0, 0x0, 0x0, 0x0) - - -@always_inline -fn M1() -> Decimal: - """Returns 1 as a Decimal.""" - return Decimal(0x1, 0x0, 0x0, 0x0) - - -@always_inline -fn M2() -> Decimal: - """Returns 2 as a Decimal.""" - return Decimal(0x2, 0x0, 0x0, 0x0) - - -@always_inline -fn M3() -> Decimal: - """Returns 3 as a Decimal.""" - return Decimal(0x3, 0x0, 0x0, 0x0) - - -@always_inline -fn M4() -> Decimal: - """Returns 4 as a Decimal.""" - return Decimal(0x4, 0x0, 0x0, 0x0) - - -@always_inline -fn M5() -> Decimal: - """Returns 5 as a Decimal.""" - return Decimal(0x5, 0x0, 0x0, 0x0) - - -@always_inline -fn M6() -> Decimal: - """Returns 6 as a Decimal.""" - return Decimal(0x6, 0x0, 0x0, 0x0) - - -@always_inline -fn M7() -> Decimal: - """Returns 7 as a Decimal.""" - return Decimal(0x7, 0x0, 0x0, 0x0) - - -@always_inline -fn M8() -> Decimal: - """Returns 8 as a Decimal.""" - return Decimal(0x8, 0x0, 0x0, 0x0) - - -@always_inline -fn M9() -> Decimal: - """Returns 9 as a Decimal.""" - return Decimal(0x9, 0x0, 0x0, 0x0) - - -@always_inline -fn M10() -> Decimal: - """Returns 10 as a Decimal.""" - return Decimal(0xA, 0x0, 0x0, 0x0) - - -# Decimal constants - - -@always_inline -fn M0D5() -> Decimal: - """Returns 0.5 as a Decimal.""" - return Decimal(5, 0, 0, 0x10000) - - -@always_inline -fn M0D25() -> Decimal: - """Returns 0.25 as a Decimal.""" - return Decimal(25, 0, 0, 0x20000) - - -# ===----------------------------------------------------------------------=== # -# -# Inverse constants -# -# ===----------------------------------------------------------------------=== # - - -@always_inline -fn INV2() -> Decimal: - """Returns 1/2 = 0.5.""" - return Decimal(0x5, 0x0, 0x0, 0x10000) - - -@always_inline -fn INV10() -> Decimal: - """Returns 1/10 = 0.1.""" - return Decimal(0x1, 0x0, 0x0, 0x10000) - - -@always_inline -fn INV0D1() -> Decimal: - """Returns 1/0.1 = 10.""" - return Decimal(0xA, 0x0, 0x0, 0x0) - - -@always_inline -fn INV0D2() -> Decimal: - """Returns 1/0.2 = 5.""" - return Decimal(0x5, 0x0, 0x0, 0x0) - - -@always_inline -fn INV0D3() -> Decimal: - """Returns 1/0.3 = 3.33333333333333333333333333333333...""" - return Decimal(0x35555555, 0xCF2607EE, 0x6BB4AFE4, 0x1C0000) - - -@always_inline -fn INV0D4() -> Decimal: - """Returns 1/0.4 = 2.5.""" - return Decimal(0x19, 0x0, 0x0, 0x10000) - - -@always_inline -fn INV0D5() -> Decimal: - """Returns 1/0.5 = 2.""" - return Decimal(0x2, 0x0, 0x0, 0x0) - - -@always_inline -fn INV0D6() -> Decimal: - """Returns 1/0.6 = 1.66666666666666666666666666666667...""" - return Decimal(0x1AAAAAAB, 0x679303F7, 0x35DA57F2, 0x1C0000) - - -@always_inline -fn INV0D7() -> Decimal: - """Returns 1/0.7 = 1.42857142857142857142857142857143...""" - return Decimal(0xCDB6DB6E, 0x3434DED3, 0x2E28DDAB, 0x1C0000) - - -@always_inline -fn INV0D8() -> Decimal: - """Returns 1/0.8 = 1.25.""" - return Decimal(0x7D, 0x0, 0x0, 0x20000) - - -@always_inline -fn INV0D9() -> Decimal: - """Returns 1/0.9 = 1.11111111111111111111111111111111...""" - return Decimal(0x671C71C7, 0x450CAD4F, 0x23E6E54C, 0x1C0000) - - -@always_inline -fn INV1() -> Decimal: - """Returns 1/1 = 1.""" - return Decimal(0x1, 0x0, 0x0, 0x0) - - -@always_inline -fn INV1D1() -> Decimal: - """Returns 1/1.1 = 0.90909090909090909090909090909091...""" - return Decimal(0x9A2E8BA3, 0x4FC48DCC, 0x1D5FD2E1, 0x1C0000) - - -@always_inline -fn INV1D2() -> Decimal: - """Returns 1/1.2 = 0.83333333333333333333333333333333...""" - return Decimal(0x8D555555, 0x33C981FB, 0x1AED2BF9, 0x1C0000) - - -@always_inline -fn INV1D3() -> Decimal: - """Returns 1/1.3 = 0.76923076923076923076923076923077...""" - return Decimal(0xC4EC4EC, 0x9243DA72, 0x18DAED83, 0x1C0000) - - -@always_inline -fn INV1D4() -> Decimal: - """Returns 1/1.4 = 0.71428571428571428571428571428571...""" - return Decimal(0xE6DB6DB7, 0x9A1A6F69, 0x17146ED5, 0x1C0000) - - -@always_inline -fn INV1D5() -> Decimal: - """Returns 1/1.5 = 0.66666666666666666666666666666667...""" - return Decimal(0xAAAAAAB, 0x296E0196, 0x158A8994, 0x1C0000) - - -@always_inline -fn INV1D6() -> Decimal: - """Returns 1/1.6 = 0.625.""" - return Decimal(0x271, 0x0, 0x0, 0x30000) - - -@always_inline -fn INV1D7() -> Decimal: - """Returns 1/1.7 = 0.58823529411764705882352941176471...""" - return Decimal(0x45A5A5A6, 0xE8520166, 0x1301C4AF, 0x1C0000) - - -@always_inline -fn INV1D8() -> Decimal: - """Returns 1/1.8 = 0.55555555555555555555555555555556...""" - return Decimal(0xB38E38E4, 0x228656A7, 0x11F372A6, 0x1C0000) - - -@always_inline -fn INV1D9() -> Decimal: - """Returns 1/1.9 = 0.52631578947368421052631578947368...""" - return Decimal(0xAA1AF287, 0x2E2E6D0A, 0x11019509, 0x1C0000) - - -# ===----------------------------------------------------------------------=== # -# -# N / (N+1) constants -# -# ===----------------------------------------------------------------------=== # - - -@always_inline -fn N_DIVIDE_NEXT(n: Int) raises -> Decimal: - """ - Returns the pre-calculated value of n/(n+1) for n between 1 and 20. - - Args: - n: An integer between 1 and 20, inclusive. - - Returns: - A Decimal representing the value of n/(n+1). - - Raises: - Error: If n is outside the range [1, 20]. - """ - if n == 1: - # 1/2 = 0.5 - return Decimal(0x5, 0x0, 0x0, 0x10000) - elif n == 2: - # 2/3 = 0.66666666666666666666666666666667... - return Decimal(0xAAAAAAB, 0x296E0196, 0x158A8994, 0x1C0000) - elif n == 3: - # 3/4 = 0.75 - return Decimal(0x4B, 0x0, 0x0, 0x20000) - elif n == 4: - # 4/5 = 0.8 - return Decimal(0x8, 0x0, 0x0, 0x10000) - elif n == 5: - # 5/6 = 0.83333333333333333333333333333333... - return Decimal(0x8D555555, 0x33C981FB, 0x1AED2BF9, 0x1C0000) - elif n == 6: - # 6/7 = 0.85714285714285714285714285714286... - return Decimal(0x7B6DB6DB, 0xEC1FB8E5, 0x1BB21E99, 0x1C0000) - elif n == 7: - # 7/8 = 0.875 - return Decimal(0x36B, 0x0, 0x0, 0x30000) - elif n == 8: - # 8/9 = 0.88888888888888888888888888888889... - return Decimal(0xB8E38E39, 0x373D5772, 0x1CB8B770, 0x1C0000) - elif n == 9: - # 9/10 = 0.9 - return Decimal(0x9, 0x0, 0x0, 0x10000) - elif n == 10: - # 10/11 = 0.90909090909090909090909090909091... - return Decimal(0x9A2E8BA3, 0x4FC48DCC, 0x1D5FD2E1, 0x1C0000) - elif n == 11: - # 11/12 = 0.91666666666666666666666666666667... - return Decimal(0x4EAAAAAB, 0xB8F7422E, 0x1D9E7D2B, 0x1C0000) - elif n == 12: - # 12/13 = 0.92307692307692307692307692307692... - return Decimal(0xEC4EC4F, 0xAF849FBC, 0x1DD3836A, 0x1C0000) - elif n == 13: - # 13/14 = 0.92857142857142857142857142857143... - return Decimal(0x45B6DB6E, 0x15225DA3, 0x1E00F67C, 0x1C0000) - elif n == 14: - # 14/15 = 0.93333333333333333333333333333333... - return Decimal(0x75555555, 0xD39A0238, 0x1E285A35, 0x1C0000) - elif n == 15: - # 15/16 = 0.9375 - return Decimal(0x249F, 0x0, 0x0, 0x40000) - elif n == 16: - # 16/17 = 0.94117647058823529411764705882353... - return Decimal(0x3C3C3C3C, 0xD50023D, 0x1E693AB3, 0x1C0000) - elif n == 17: - # 17/18 = 0.94444444444444444444444444444444... - return Decimal(0xE471C71C, 0x3AB12CE9, 0x1E8442E7, 0x1C0000) - elif n == 18: - # 18/19 = 0.94736842105263157894736842105263... - return Decimal(0xCBCA1AF3, 0x1FED2AAC, 0x1E9C72AA, 0x1C0000) - elif n == 19: - # 19/20 = 0.95 - return Decimal(0x5F, 0x0, 0x0, 0x20000) - elif n == 20: - # 20/21 = 0.95238095238095238095238095238095... - return Decimal(0x33CF3CF4, 0xCD78948D, 0x1EC5E91C, 0x1C0000) - else: - raise Error("N_DIVIDE_NEXT: n must be between 1 and 20, inclusive") - - -# ===----------------------------------------------------------------------=== # -# -# PI constants -# -# ===----------------------------------------------------------------------=== # - - -@always_inline -fn PI() -> Decimal: - """Returns the value of pi (π) as a Decimal.""" - return Decimal(0x41B65F29, 0xB143885, 0x6582A536, 0x1C0000) - - -# ===----------------------------------------------------------------------=== # -# -# EXP constants -# -# ===----------------------------------------------------------------------=== # - - -@always_inline -fn E() -> Decimal: - """ - Returns the value of Euler's number (e) as a Decimal. - - Returns: - A Decimal representation of Euler's number with maximum precision. - """ - return Decimal(0x857AED5A, 0xEBECDE35, 0x57D519AB, 0x1C0000) - - -@always_inline -fn E2() -> Decimal: - """Returns the value of e^2 as a Decimal.""" - return Decimal(0xE4DFDCAE, 0x89F7E295, 0xEEC0D6E9, 0x1C0000) - - -@always_inline -fn E3() -> Decimal: - """Returns the value of e^3 as a Decimal.""" - return Decimal(0x236454F7, 0x62055A80, 0x40E65DE2, 0x1B0000) - - -@always_inline -fn E4() -> Decimal: - """Returns the value of e^4 as a Decimal.""" - return Decimal(0x7121EFD3, 0xFB318FB5, 0xB06A87FB, 0x1B0000) - - -@always_inline -fn E5() -> Decimal: - """Returns the value of e^5 as a Decimal.""" - return Decimal(0xD99BD974, 0x9F4BE5C7, 0x2FF472E3, 0x1A0000) - - -@always_inline -fn E6() -> Decimal: - """Returns the value of e^6 as a Decimal.""" - return Decimal(0xADB57A66, 0xBD7A423F, 0x825AD8FF, 0x1A0000) - - -@always_inline -fn E7() -> Decimal: - """Returns the value of e^7 as a Decimal.""" - return Decimal(0x22313FCF, 0x64D5D12F, 0x236F230A, 0x190000) - - -@always_inline -fn E8() -> Decimal: - """Returns the value of e^8 as a Decimal.""" - return Decimal(0x1E892E63, 0xD1BF8B5C, 0x6051E812, 0x190000) - - -@always_inline -fn E9() -> Decimal: - """Returns the value of e^9 as a Decimal.""" - return Decimal(0x34FAB691, 0xE7CD8DEA, 0x1A2EB6C3, 0x180000) - - -@always_inline -fn E10() -> Decimal: - """Returns the value of e^10 as a Decimal.""" - return Decimal(0xBA7F4F65, 0x58692B62, 0x472BDD8F, 0x180000) - - -@always_inline -fn E11() -> Decimal: - """Returns the value of e^11 as a Decimal.""" - return Decimal(0x8C2C6D20, 0x2A86F9E7, 0xC176BAAE, 0x180000) - - -@always_inline -fn E12() -> Decimal: - """Returns the value of e^12 as a Decimal.""" - return Decimal(0xE924992A, 0x31CDC314, 0x3496C2C4, 0x170000) - - -@always_inline -fn E13() -> Decimal: - """Returns the value of e^13 as a Decimal.""" - return Decimal(0x220130DB, 0xC386029A, 0x8EF393FB, 0x170000) - - -@always_inline -fn E14() -> Decimal: - """Returns the value of e^14 as a Decimal.""" - return Decimal(0x3A24795C, 0xC412DF01, 0x26DBB5A0, 0x160000) - - -@always_inline -fn E15() -> Decimal: - """Returns the value of e^15 as a Decimal.""" - return Decimal(0x6C1248BD, 0x90456557, 0x69A0AD8C, 0x160000) - - -@always_inline -fn E16() -> Decimal: - """Returns the value of e^16 as a Decimal.""" - return Decimal(0xB46A97D, 0x90655BBD, 0x1CB66B18, 0x150000) - - -@always_inline -fn E32() -> Decimal: - """Returns the value of e^32 as a Decimal.""" - return Decimal(0x18420EB, 0xCC2501E6, 0xFF24A138, 0xF0000) - - -@always_inline -fn E0D5() -> Decimal: - """Returns the value of e^0.5 = e^(1/2) as a Decimal.""" - return Decimal(0x8E99DD66, 0xC210E35C, 0x3545E717, 0x1C0000) - - -@always_inline -fn E0D25() -> Decimal: - """Returns the value of e^0.25 = e^(1/4) as a Decimal.""" - return Decimal(0xB43646F1, 0x2654858A, 0x297D3595, 0x1C0000) - - -# ===----------------------------------------------------------------------=== # -# -# LN constants -# -# ===----------------------------------------------------------------------=== # - -# The repr of the magic numbers can be obtained by the following code: -# -# ```mojo -# fn print_repr_words(value: String, ln_value: String) raises: -# """ -# Prints the hex representation of a logarithm value. -# Args: -# value: The original value (for display purposes). -# ln_value: The natural logarithm as a String. -# """ -# var log_decimal = Decimal(ln_value) -# print("ln(" + value + "): " + log_decimal.repr_words()) -# ``` - - -# Constants for integers - - -@always_inline -fn LN1() -> Decimal: - """Returns ln(1) = 0.""" - return Decimal(0x0, 0x0, 0x0, 0x0) - - -@always_inline -fn LN2() -> Decimal: - """Returns ln(2) = 0.69314718055994530941723212145818...""" - return Decimal(0xAA7A65BF, 0x81F52F01, 0x1665943F, 0x1C0000) - - -@always_inline -fn LN10() -> Decimal: - """Returns ln(10) = 2.30258509299404568401799145468436...""" - return Decimal(0x9FA69733, 0x1414B220, 0x4A668998, 0x1C0000) - - -# Constants for values less than 1 -@always_inline -fn LN0D1() -> Decimal: - """Returns ln(0.1) = -2.30258509299404568401799145468436...""" - return Decimal(0x9FA69733, 0x1414B220, 0x4A668998, 0x801C0000) - - -@always_inline -fn LN0D2() -> Decimal: - """Returns ln(0.2) = -1.60943791243410037460075933322619...""" - return Decimal(0xF52C3174, 0x921F831E, 0x3400F558, 0x801C0000) - - -@always_inline -fn LN0D3() -> Decimal: - """Returns ln(0.3) = -1.20397280432593599262274621776184...""" - return Decimal(0x2B8E6822, 0x8258467, 0x26E70795, 0x801C0000) - - -@always_inline -fn LN0D4() -> Decimal: - """Returns ln(0.4) = -0.91629073187415506518352721176801...""" - return Decimal(0x4AB1CBB6, 0x102A541D, 0x1D9B6119, 0x801C0000) - - -@always_inline -fn LN0D5() -> Decimal: - """Returns ln(0.5) = -0.69314718055994530941723212145818...""" - return Decimal(0xAA7A65BF, 0x81F52F01, 0x1665943F, 0x801C0000) - - -@always_inline -fn LN0D6() -> Decimal: - """Returns ln(0.6) = -0.51082562376599068320551409630366...""" - return Decimal(0x81140263, 0x86305565, 0x10817355, 0x801C0000) - - -@always_inline -fn LN0D7() -> Decimal: - """Returns ln(0.7) = -0.35667494393873237891263871124118...""" - return Decimal(0x348BC5A8, 0x8B755D08, 0xB865892, 0x801C0000) - - -@always_inline -fn LN0D8() -> Decimal: - """Returns ln(0.8) = -0.22314355131420975576629509030983...""" - return Decimal(0xA03765F7, 0x8E35251B, 0x735CCD9, 0x801C0000) - - -@always_inline -fn LN0D9() -> Decimal: - """Returns ln(0.9) = -0.10536051565782630122750098083931...""" - return Decimal(0xB7763910, 0xFC3656AD, 0x3678591, 0x801C0000) - - -# Constants for values greater than 1 - - -@always_inline -fn LN1D1() -> Decimal: - """Returns ln(1.1) = 0.09531017980432486004395212328077...""" - return Decimal(0x7212FFD1, 0x7D9A10, 0x3146328, 0x1C0000) - - -@always_inline -fn LN1D2() -> Decimal: - """Returns ln(1.2) = 0.18232155679395462621171802515451...""" - return Decimal(0x2966635C, 0xFBC4D99C, 0x5E420E9, 0x1C0000) - - -@always_inline -fn LN1D3() -> Decimal: - """Returns ln(1.3) = 0.26236426446749105203549598688095...""" - return Decimal(0xE0BE71FD, 0xC254E078, 0x87A39F0, 0x1C0000) - - -@always_inline -fn LN1D4() -> Decimal: - """Returns ln(1.4) = 0.33647223662121293050459341021699...""" - return Decimal(0x75EEA016, 0xF67FD1F9, 0xADF3BAC, 0x1C0000) - - -@always_inline -fn LN1D5() -> Decimal: - """Returns ln(1.5) = 0.40546510810816438197801311546435...""" - return Decimal(0xC99DC953, 0x89F9FEB7, 0xD19EDC3, 0x1C0000) - - -@always_inline -fn LN1D6() -> Decimal: - """Returns ln(1.6) = 0.47000362924573555365093703114834...""" - return Decimal(0xA42FFC8, 0xF3C009E6, 0xF2FC765, 0x1C0000) - - -@always_inline -fn LN1D7() -> Decimal: - """Returns ln(1.7) = 0.53062825106217039623154316318876...""" - return Decimal(0x64BB9ED0, 0x4AB9978F, 0x11254107, 0x1C0000) - - -@always_inline -fn LN1D8() -> Decimal: - """Returns ln(1.8) = 0.58778666490211900818973114061886...""" - return Decimal(0xF3042CAE, 0x85BED853, 0x12FE0EAD, 0x1C0000) - - -@always_inline -fn LN1D9() -> Decimal: - """Returns ln(1.9) = 0.64185388617239477599103597720349...""" - return Decimal(0x12F992DC, 0xE7374425, 0x14BD4A78, 0x1C0000) diff --git a/src/decimojo/decimal/special.mojo b/src/decimojo/decimal/special.mojo deleted file mode 100644 index 61a0bdf..0000000 --- a/src/decimojo/decimal/special.mojo +++ /dev/null @@ -1,278 +0,0 @@ -# ===----------------------------------------------------------------------=== # -# Copyright 2025 Yuhao Zhu -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ===----------------------------------------------------------------------=== # -# -# Implements special functions for the Decimal type -# -# ===----------------------------------------------------------------------=== # - -"""Implements functions for special operations on Decimal objects.""" - - -fn factorial(n: Int) raises -> Decimal: - """Calculates the factorial of a non-negative integer. - - Args: - n: The non-negative integer to calculate the factorial of. - - Returns: - The factorial of n. - - Notes: - - 27! is the largest factorial that can be represented by Decimal. - An error will be raised if n is greater than 27. - """ - - if n < 0: - raise Error("Factorial is not defined for negative numbers") - - if n > 27: - raise Error( - String("{}! is too large to be represented by Decimal").format(n) - ) - - # Directly return the factorial for n = 0 to 27 - if n == 0 or n == 1: - return Decimal.from_words(1, 0, 0, 0) # 1 - elif n == 2: - return Decimal.from_words(2, 0, 0, 0) # 2 - elif n == 3: - return Decimal.from_words(6, 0, 0, 0) # 6 - elif n == 4: - return Decimal.from_words(24, 0, 0, 0) # 24 - elif n == 5: - return Decimal.from_words(120, 0, 0, 0) # 120 - elif n == 6: - return Decimal.from_words(720, 0, 0, 0) # 720 - elif n == 7: - return Decimal.from_words(5040, 0, 0, 0) # 5040 - elif n == 8: - return Decimal.from_words(40320, 0, 0, 0) # 40320 - elif n == 9: - return Decimal.from_words(362880, 0, 0, 0) # 362880 - elif n == 10: - return Decimal.from_words(3628800, 0, 0, 0) # 3628800 - elif n == 11: - return Decimal.from_words(39916800, 0, 0, 0) # 39916800 - elif n == 12: - return Decimal.from_words(479001600, 0, 0, 0) # 479001600 - elif n == 13: - return Decimal.from_words(1932053504, 1, 0, 0) # 6227020800 - elif n == 14: - return Decimal.from_words(1278945280, 20, 0, 0) # 87178291200 - elif n == 15: - return Decimal.from_words(2004310016, 304, 0, 0) # 1307674368000 - elif n == 16: - return Decimal.from_words(2004189184, 4871, 0, 0) # 20922789888000 - elif n == 17: - return Decimal.from_words(4006445056, 82814, 0, 0) # 355687428096000 - elif n == 18: - return Decimal.from_words(3396534272, 1490668, 0, 0) # 6402373705728000 - elif n == 19: - return Decimal.from_words( - 109641728, 28322707, 0, 0 - ) # 121645100408832000 - elif n == 20: - return Decimal.from_words( - 2192834560, 566454140, 0, 0 - ) # 2432902008176640000 - elif n == 21: - return Decimal.from_words( - 3099852800, 3305602358, 2, 0 - ) # 51090942171709440000 - elif n == 22: - return Decimal.from_words( - 3772252160, 4003775155, 60, 0 - ) # 1124000727777607680000 - elif n == 23: - return Decimal.from_words( - 862453760, 1892515369, 1401, 0 - ) # 25852016738884976640000 - elif n == 24: - return Decimal.from_words( - 3519021056, 2470695900, 33634, 0 - ) # 620448401733239439360000 - elif n == 25: - return Decimal.from_words( - 2076180480, 1637855376, 840864, 0 - ) # 15511210043330985984000000 - elif n == 26: - return Decimal.from_words( - 2441084928, 3929534124, 21862473, 0 - ) # 403291461126605650322784000 - else: - return Decimal.from_words( - 1484783616, 3018206259, 590286795, 0 - ) # 10888869450418352160768000000 - - -fn factorial_reciprocal(n: Int) raises -> Decimal: - """Calculates the reciprocal of factorial of a non-negative integer (1/n!). - - Args: - n: The non-negative integer to calculate the reciprocal factorial of. - - Returns: - The reciprocal of factorial of n (1/n!). - - Notes: - This function is optimized for Taylor series calculations. - The function uses pre-computed values for speed. - For n > 27, the result is effectively 0 at Decimal precision. - """ - - # 1/0! = 1, Decimal.from_words(0x1, 0x0, 0x0, 0x0) - # 1/1! = 1, Decimal.from_words(0x1, 0x0, 0x0, 0x0) - # 1/2! = 0.5, Decimal.from_words(0x5, 0x0, 0x0, 0x10000) - # 1/3! = 0.1666666666666666666666666667, Decimal.from_words(0x82aaaaab, 0xa5b8065, 0x562a265, 0x1c0000) - # 1/4! = 0.0416666666666666666666666667, Decimal.from_words(0x60aaaaab, 0x4296e019, 0x158a899, 0x1c0000) - # 1/5! = 0.0083333333333333333333333333, Decimal.from_words(0x13555555, 0xd516005, 0x44ee85, 0x1c0000) - # 1/6! = 0.0013888888888888888888888889, Decimal.from_words(0x2de38e39, 0x2ce2e556, 0xb7d16, 0x1c0000) - # 1/7! = 0.0001984126984126984126984127, Decimal.from_words(0xe1fbefbf, 0xbd44fc30, 0x1a427, 0x1c0000) - # 1/8! = 0.0000248015873015873015873016, Decimal.from_words(0x1c3f7df8, 0xf7a89f86, 0x3484, 0x1c0000) - # 1/9! = 0.0000027557319223985890652557, Decimal.from_words(0xca3ff18d, 0xe2a0f547, 0x5d5, 0x1c0000) - # 1/10! = 0.0000002755731922398589065256, Decimal.from_words(0x94399828, 0x63767eed, 0x95, 0x1c0000) - # 1/11! = 0.0000000250521083854417187751, Decimal.from_words(0xb06253a7, 0x94adae72, 0xd, 0x1c0000) - # 1/12! = 0.0000000020876756987868098979, Decimal.from_words(0xe40831a3, 0x21b923de, 0x1, 0x1c0000) - # 1/13! = 0.0000000001605904383682161460, Decimal.from_words(0x4c9e2b34, 0x16495187, 0x0, 0x1c0000) - # 1/14! = 0.0000000000114707455977297247, Decimal.from_words(0xce9d955f, 0x19785d2, 0x0, 0x1c0000) - # 1/15! = 0.0000000000007647163731819816, Decimal.from_words(0xdc63d28, 0x1b2b0e, 0x0, 0x1c0000) - # 1/16! = 0.0000000000000477947733238739, Decimal.from_words(0xe0dc63d3, 0x1b2b0, 0x0, 0x1c0000) - # 1/17! = 0.0000000000000028114572543455, Decimal.from_words(0xef1c05df, 0x1991, 0x0, 0x1c0000) - # 1/18! = 0.0000000000000001561920696859, Decimal.from_words(0xa9ba721b, 0x16b, 0x0, 0x1c0000) - # 1/19! = 0.0000000000000000082206352466, Decimal.from_words(0x23e16452, 0x13, 0x0, 0x1c0000) - # 1/20! = 0.0000000000000000004110317623, Decimal.from_words(0xf4fe7837, 0x0, 0x0, 0x1c0000) - # 1/21! = 0.0000000000000000000195729411, Decimal.from_words(0xbaa9803, 0x0, 0x0, 0x1c0000) - # 1/22! = 0.0000000000000000000008896791, Decimal.from_words(0x87c117, 0x0, 0x0, 0x1c0000) - # 1/23! = 0.0000000000000000000000386817, Decimal.from_words(0x5e701, 0x0, 0x0, 0x1c0000) - # 1/24! = 0.0000000000000000000000016117, Decimal.from_words(0x3ef5, 0x0, 0x0, 0x1c0000) - # 1/25! = 0.0000000000000000000000000645, Decimal.from_words(0x285, 0x0, 0x0, 0x1c0000) - # 1/26! = 0.0000000000000000000000000025, Decimal.from_words(0x19, 0x0, 0x0, 0x1c0000) - # 1/27! = 0.0000000000000000000000000001, Decimal.from_words(0x1, 0x0, 0x0, 0x1c0000) - - if n < 0: - raise Error("Factorial reciprocal is not defined for negative numbers") - - # For n > 27, 1/n! is essentially 0 at Decimal precision - # Return 0 with max scale - if n > 27: - return Decimal.from_words(0, 0, 0, 0x001C0000) - - # Directly return pre-computed reciprocal factorials - if n == 0 or n == 1: - return Decimal.from_words(0x1, 0x0, 0x0, 0x0) # 1 - elif n == 2: - return Decimal.from_words(0x5, 0x0, 0x0, 0x10000) # 0.5 - elif n == 3: - return Decimal.from_words( - 0x82AAAAAB, 0xA5B8065, 0x562A265, 0x1C0000 - ) # 0.1666... - elif n == 4: - return Decimal.from_words( - 0x60AAAAAB, 0x4296E019, 0x158A899, 0x1C0000 - ) # 0.0416... - elif n == 5: - return Decimal.from_words( - 0x13555555, 0xD516005, 0x44EE85, 0x1C0000 - ) # 0.0083... - elif n == 6: - return Decimal.from_words( - 0x2DE38E39, 0x2CE2E556, 0xB7D16, 0x1C0000 - ) # 0.0013... - elif n == 7: - return Decimal.from_words( - 0xE1FBEFBF, 0xBD44FC30, 0x1A427, 0x1C0000 - ) # 0.0001... - elif n == 8: - return Decimal.from_words( - 0x1C3F7DF8, 0xF7A89F86, 0x3484, 0x1C0000 - ) # 0.0000248... - elif n == 9: - return Decimal.from_words( - 0xCA3FF18D, 0xE2A0F547, 0x5D5, 0x1C0000 - ) # 0.0000027... - elif n == 10: - return Decimal.from_words( - 0x94399828, 0x63767EED, 0x95, 0x1C0000 - ) # 0.00000027... - elif n == 11: - return Decimal.from_words( - 0xB06253A7, 0x94ADAE72, 0xD, 0x1C0000 - ) # 0.000000025... - elif n == 12: - return Decimal.from_words( - 0xE40831A3, 0x21B923DE, 0x1, 0x1C0000 - ) # 0.0000000020... - elif n == 13: - return Decimal.from_words( - 0x4C9E2B34, 0x16495187, 0x0, 0x1C0000 - ) # 0.0000000001... - elif n == 14: - return Decimal.from_words( - 0xCE9D955F, 0x19785D2, 0x0, 0x1C0000 - ) # 0.00000000001... - elif n == 15: - return Decimal.from_words( - 0xDC63D28, 0x1B2B0E, 0x0, 0x1C0000 - ) # 0.0000000000007... - elif n == 16: - return Decimal.from_words( - 0xE0DC63D3, 0x1B2B0, 0x0, 0x1C0000 - ) # 0.00000000000004... - elif n == 17: - return Decimal.from_words( - 0xEF1C05DF, 0x1991, 0x0, 0x1C0000 - ) # 0.000000000000002... - elif n == 18: - return Decimal.from_words( - 0xA9BA721B, 0x16B, 0x0, 0x1C0000 - ) # 0.0000000000000001... - elif n == 19: - return Decimal.from_words( - 0x23E16452, 0x13, 0x0, 0x1C0000 - ) # 0.0000000000000000082... - elif n == 20: - return Decimal.from_words( - 0xF4FE7837, 0x0, 0x0, 0x1C0000 - ) # 0.0000000000000000004... - elif n == 21: - return Decimal.from_words( - 0xBAA9803, 0x0, 0x0, 0x1C0000 - ) # 0.0000000000000000000195... - elif n == 22: - return Decimal.from_words( - 0x87C117, 0x0, 0x0, 0x1C0000 - ) # 0.0000000000000000000008... - elif n == 23: - return Decimal.from_words( - 0x5E701, 0x0, 0x0, 0x1C0000 - ) # 0.0000000000000000000000386... - elif n == 24: - return Decimal.from_words( - 0x3EF5, 0x0, 0x0, 0x1C0000 - ) # 0.0000000000000000000000016... - elif n == 25: - return Decimal.from_words( - 0x285, 0x0, 0x0, 0x1C0000 - ) # 0.0000000000000000000000000645 - elif n == 26: - return Decimal.from_words( - 0x19, 0x0, 0x0, 0x1C0000 - ) # 0.0000000000000000000000000025 - else: # n == 27 - return Decimal.from_words( - 0x1, 0x0, 0x0, 0x1C0000 - ) # 0.0000000000000000000000000001 diff --git a/src/decimojo/decimal/__init__.mojo b/src/decimojo/decimal128/__init__.mojo similarity index 100% rename from src/decimojo/decimal/__init__.mojo rename to src/decimojo/decimal128/__init__.mojo diff --git a/src/decimojo/decimal/arithmetics.mojo b/src/decimojo/decimal128/arithmetics.mojo similarity index 64% rename from src/decimojo/decimal/arithmetics.mojo rename to src/decimojo/decimal128/arithmetics.mojo index 64456e3..b290e13 100644 --- a/src/decimojo/decimal/arithmetics.mojo +++ b/src/decimojo/decimal128/arithmetics.mojo @@ -14,43 +14,43 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # -# Implements basic arithmetic functions for the Decimal type +# Implements basic arithmetic functions for the Decimal128 type # # ===----------------------------------------------------------------------=== # # # List of functions in this module: # -# add(x1: Decimal, x2: Decimal): Adds two Decimal values and returns a new Decimal containing the sum -# subtract(x1: Decimal, x2: Decimal): Subtracts the x2 Decimal from x1 and returns a new Decimal -# multiply(x1: Decimal, x2: Decimal): Multiplies two Decimal values and returns a new Decimal containing the product -# true_divide(x1: Decimal, x2: Decimal): Divides x1 by x2 and returns a new Decimal containing the quotient +# add(x1: Decimal128, x2: Decimal128): Adds two Decimal128 values and returns a new Decimal128 containing the sum +# subtract(x1: Decimal128, x2: Decimal128): Subtracts the x2 Decimal128 from x1 and returns a new Decimal128 +# multiply(x1: Decimal128, x2: Decimal128): Multiplies two Decimal128 values and returns a new Decimal128 containing the product +# true_divide(x1: Decimal128, x2: Decimal128): Divides x1 by x2 and returns a new Decimal128 containing the quotient # # ===----------------------------------------------------------------------=== # """ -Implements functions for mathematical operations on Decimal objects. +Implements functions for mathematical operations on Decimal128 objects. """ import time import testing -from decimojo.decimal.decimal import Decimal +from decimojo.decimal128.decimal128 import Decimal128 from decimojo.rounding_mode import RoundingMode -import decimojo.utility +import decimojo.decimal128.utility # TODO: Like `multiply` use combined bits to determine the appropriate method -fn add(x1: Decimal, x2: Decimal) raises -> Decimal: +fn add(x1: Decimal128, x2: Decimal128) raises -> Decimal128: """ - Adds two Decimal values and returns a new Decimal containing the sum. + Adds two Decimal128 values and returns a new Decimal128 containing the sum. The results will be rounded (up to even) if digits are too many. Args: - x1: The first Decimal operand. - x2: The second Decimal operand. + x1: The first Decimal128 operand. + x2: The second Decimal128 operand. Returns: - A new Decimal containing the sum of x1 and x2. + A new Decimal128 containing the sum of x1 and x2. Raises: Error: If the operation would overflow. @@ -63,7 +63,7 @@ fn add(x1: Decimal, x2: Decimal) raises -> Decimal: # CASE: Zeros if x1_coef == 0 and x2_coef == 0: var scale = max(x1_scale, x2_scale) - return Decimal(0, 0, 0, scale, False) + return Decimal128(0, 0, 0, scale, False) elif x1_coef == 0: if x1_scale <= x2_scale: @@ -75,18 +75,18 @@ fn add(x1: Decimal, x2: Decimal) raises -> Decimal: var sum_coef = x2_coef var scale = min( max(x1_scale, x2_scale), - Decimal.MAX_NUM_DIGITS - - decimojo.utility.number_of_digits(x2.to_uint128()), + Decimal128.MAX_NUM_DIGITS + - decimojo.decimal128.utility.number_of_digits(x2.to_uint128()), ) ## If x2_coef > 7922816251426433759354395033 if ( - (x2_coef > Decimal.MAX_AS_UINT128 // 10) + (x2_coef > Decimal128.MAX_AS_UINT128 // 10) and (scale > 0) and (scale > x2_scale) ): scale -= 1 sum_coef *= UInt128(10) ** (scale - x2_scale) - return Decimal.from_uint128(sum_coef, scale, x2.is_negative()) + return Decimal128.from_uint128(sum_coef, scale, x2.is_negative()) elif x2_coef == 0: if x2_scale <= x1_scale: @@ -97,18 +97,18 @@ fn add(x1: Decimal, x2: Decimal) raises -> Decimal: var sum_coef = x1_coef var scale = min( max(x1_scale, x2_scale), - Decimal.MAX_NUM_DIGITS - - decimojo.utility.number_of_digits(x1.to_uint128()), + Decimal128.MAX_NUM_DIGITS + - decimojo.decimal128.utility.number_of_digits(x1.to_uint128()), ) ## If x1_coef > 7922816251426433759354395033 if ( - (x1_coef > Decimal.MAX_AS_UINT128 // 10) + (x1_coef > Decimal128.MAX_AS_UINT128 // 10) and (scale > 0) and (scale > x1_scale) ): scale -= 1 sum_coef *= UInt128(10) ** (scale - x1_scale) - return Decimal.from_uint128(sum_coef, scale, x1.is_negative()) + return Decimal128.from_uint128(sum_coef, scale, x1.is_negative()) # CASE: Integer addition with scale of 0 (true integers) elif x1_scale == 0 and x2_scale == 0: @@ -118,11 +118,11 @@ fn add(x1: Decimal, x2: Decimal) raises -> Decimal: var summation = x1_coef + x2_coef # Check for overflow (UInt128 can store values beyond our 96-bit limit) - # We need to make sure the sum fits in 96 bits (our Decimal capacity) - if summation > Decimal.MAX_AS_UINT128: # 2^96-1 - raise Error("Error in `addition()`: Decimal overflow") + # We need to make sure the sum fits in 96 bits (our Decimal128 capacity) + if summation > Decimal128.MAX_AS_UINT128: # 2^96-1 + raise Error("Error in `addition()`: Decimal128 overflow") - return Decimal.from_uint128(summation, 0, x1.is_negative()) + return Decimal128.from_uint128(summation, 0, x1.is_negative()) # Different signs: subtract the smaller from the larger else: @@ -138,7 +138,7 @@ fn add(x1: Decimal, x2: Decimal) raises -> Decimal: diff = UInt128(0) is_negative = False - return Decimal.from_uint128(diff, 0, is_negative) + return Decimal128.from_uint128(diff, 0, is_negative) # CASE: Integer addition with positive scales elif x1.is_integer() and x2.is_integer(): @@ -148,22 +148,22 @@ fn add(x1: Decimal, x2: Decimal) raises -> Decimal: var summation = x1.to_uint128() + x2.to_uint128() # Check for overflow (UInt128 can store values beyond our 96-bit limit) - # We need to make sure the sum fits in 96 bits (our Decimal capacity) - if summation > Decimal.MAX_AS_UINT128: # 2^96-1 - raise Error("Error in `addition()`: Decimal overflow") + # We need to make sure the sum fits in 96 bits (our Decimal128 capacity) + if summation > Decimal128.MAX_AS_UINT128: # 2^96-1 + raise Error("Error in `addition()`: Decimal128 overflow") # Determine the scale for the result var scale = min( max(x1_scale, x2_scale), - Decimal.MAX_NUM_DIGITS - - decimojo.utility.number_of_digits(summation), + Decimal128.MAX_NUM_DIGITS + - decimojo.decimal128.utility.number_of_digits(summation), ) ## If summation > 7922816251426433759354395033 - if (summation > Decimal.MAX_AS_UINT128 // 10) and (scale > 0): + if (summation > Decimal128.MAX_AS_UINT128 // 10) and (scale > 0): scale -= 1 summation *= UInt128(10) ** scale - return Decimal.from_uint128(summation, scale, x1.is_negative()) + return Decimal128.from_uint128(summation, scale, x1.is_negative()) # Different signs: subtract the smaller from the larger else: @@ -182,15 +182,15 @@ fn add(x1: Decimal, x2: Decimal) raises -> Decimal: # Determine the scale for the result var scale = min( max(x1_scale, x2_scale), - Decimal.MAX_NUM_DIGITS - - decimojo.utility.number_of_digits(diff), + Decimal128.MAX_NUM_DIGITS + - decimojo.decimal128.utility.number_of_digits(diff), ) ## If summation > 7922816251426433759354395033 - if (diff > Decimal.MAX_AS_UINT128 // 10) and (scale > 0): + if (diff > Decimal128.MAX_AS_UINT128 // 10) and (scale > 0): scale -= 1 diff *= UInt128(10) ** scale - return Decimal.from_uint128(diff, scale, is_negative) + return Decimal128.from_uint128(diff, scale, is_negative) # CASE: Float addition with the same scale elif x1_scale == x2_scale: @@ -208,33 +208,35 @@ fn add(x1: Decimal, x2: Decimal) raises -> Decimal: summation = x2_coef - x1_coef is_negative = x2.is_negative() else: # x1_coef == x2_coef - return Decimal.from_uint128(UInt128(0), x1_scale, False) + return Decimal128.from_uint128(UInt128(0), x1_scale, False) # If the summation fits in 96 bits, we can use the original scale - if summation < Decimal.MAX_AS_UINT128: - return Decimal.from_uint128(summation, x1_scale, is_negative) + if summation < Decimal128.MAX_AS_UINT128: + return Decimal128.from_uint128(summation, x1_scale, is_negative) # Otherwise, it is >= 29 digits # we need to truncate the summation to fit in 96 bits else: - var ndigits_summation = decimojo.utility.number_of_digits(summation) + var ndigits_summation = ( + decimojo.decimal128.utility.number_of_digits(summation) + ) var ndigits_int_summation = ndigits_summation - x1_scale - var final_scale = Decimal.MAX_NUM_DIGITS - ndigits_int_summation + var final_scale = Decimal128.MAX_NUM_DIGITS - ndigits_int_summation var truncated_summation = ( - decimojo.utility.round_to_keep_first_n_digits( - summation, Decimal.MAX_NUM_DIGITS + decimojo.decimal128.utility.round_to_keep_first_n_digits( + summation, Decimal128.MAX_NUM_DIGITS ) ) - if truncated_summation > Decimal.MAX_AS_UINT128: + if truncated_summation > Decimal128.MAX_AS_UINT128: truncated_summation = ( - decimojo.utility.round_to_keep_first_n_digits( - summation, Decimal.MAX_NUM_DIGITS - 1 + decimojo.decimal128.utility.round_to_keep_first_n_digits( + summation, Decimal128.MAX_NUM_DIGITS - 1 ) ) final_scale -= 1 - return Decimal.from_uint128( + return Decimal128.from_uint128( truncated_summation, final_scale, is_negative ) @@ -261,7 +263,7 @@ fn add(x1: Decimal, x2: Decimal) raises -> Decimal: summation = x2_coef_scaled - x1_coef_scaled is_negative = x2.is_negative() else: - return Decimal.from_uint128(UInt128(0), x1_scale, False) + return Decimal128.from_uint128(UInt128(0), x1_scale, False) else: # x1_scale < x2_scale # Scale up x1_coef to match x2_scale @@ -281,11 +283,11 @@ fn add(x1: Decimal, x2: Decimal) raises -> Decimal: summation = x2_coef_scaled - x1_coef_scaled is_negative = x2.is_negative() else: - return Decimal.from_uint128(UInt128(0), x2_scale, False) + return Decimal128.from_uint128(UInt128(0), x2_scale, False) # If the summation fits in 96 bits, we can use the original scale - if summation < Decimal.MAX_AS_UINT256: - return Decimal.from_uint128( + if summation < Decimal128.MAX_AS_UINT256: + return Decimal128.from_uint128( UInt128(summation & 0x00000000_FFFFFFFF_FFFFFFFF_FFFFFFFF), max(x1_scale, x2_scale), is_negative, @@ -294,24 +296,28 @@ fn add(x1: Decimal, x2: Decimal) raises -> Decimal: # Otherwise, it is >= 29 digits # Otherwise, we need to truncate the summation to fit in 96 bits else: - var ndigits_summation = decimojo.utility.number_of_digits(summation) + var ndigits_summation = ( + decimojo.decimal128.utility.number_of_digits(summation) + ) var ndigits_int_summation = ndigits_summation - max( x1_scale, x2_scale ) - var final_scale = Decimal.MAX_NUM_DIGITS - ndigits_int_summation + var final_scale = Decimal128.MAX_NUM_DIGITS - ndigits_int_summation - truncated_summation = decimojo.utility.round_to_keep_first_n_digits( - summation, Decimal.MAX_NUM_DIGITS + truncated_summation = ( + decimojo.decimal128.utility.round_to_keep_first_n_digits( + summation, Decimal128.MAX_NUM_DIGITS + ) ) - if truncated_summation > Decimal.MAX_AS_UINT256: + if truncated_summation > Decimal128.MAX_AS_UINT256: truncated_summation = ( - decimojo.utility.round_to_keep_first_n_digits( - summation, Decimal.MAX_NUM_DIGITS - 1 + decimojo.decimal128.utility.round_to_keep_first_n_digits( + summation, Decimal128.MAX_NUM_DIGITS - 1 ) ) final_scale -= 1 - return Decimal.from_uint128( + return Decimal128.from_uint128( UInt128( truncated_summation & 0x00000000_FFFFFFFF_FFFFFFFF_FFFFFFFF ), @@ -320,16 +326,16 @@ fn add(x1: Decimal, x2: Decimal) raises -> Decimal: ) -fn subtract(x1: Decimal, x2: Decimal) raises -> Decimal: +fn subtract(x1: Decimal128, x2: Decimal128) raises -> Decimal128: """ - Subtracts the x2 Decimal from x1 and returns a new Decimal. + Subtracts the x2 Decimal128 from x1 and returns a new Decimal128. Args: - x1: The Decimal to subtract from. - x2: The Decimal to subtract. + x1: The Decimal128 to subtract from. + x2: The Decimal128 to subtract. Returns: - A new Decimal containing the difference. + A new Decimal128 containing the difference. Notes: ------ @@ -338,8 +344,8 @@ fn subtract(x1: Decimal, x2: Decimal) raises -> Decimal: Examples: --------- ```console - var a = Decimal("10.5") - var b = Decimal("3.2") + var a = Decimal128("10.5") + var b = Decimal128("3.2") var result = a - b # Returns 7.3 ``` . @@ -351,54 +357,54 @@ fn subtract(x1: Decimal, x2: Decimal) raises -> Decimal: raise Error("Error in `subtract()`; ", e) -fn negative(x: Decimal) -> Decimal: +fn negative(x: Decimal128) -> Decimal128: """ - Returns the negative of a Decimal number. + Returns the negative of a Decimal128 number. Args: - x: The Decimal value to compute the negative of. + x: The Decimal128 value to compute the negative of. Returns: - A new Decimal containing the negative of x. + A new Decimal128 containing the negative of x. """ var result = x if x.is_zero(): # Set the sign bit to 0 and keep the scale bits - result.flags &= ~Decimal.SIGN_MASK + result.flags &= ~Decimal128.SIGN_MASK else: - result.flags ^= Decimal.SIGN_MASK # Flip sign bit + result.flags ^= Decimal128.SIGN_MASK # Flip sign bit return result -fn absolute(x: Decimal) -> Decimal: +fn absolute(x: Decimal128) -> Decimal128: """ - Returns the absolute value of a Decimal number. + Returns the absolute value of a Decimal128 number. Args: - x: The Decimal value to compute the absolute value of. + x: The Decimal128 value to compute the absolute value of. Returns: - A new Decimal containing the absolute value of x. + A new Decimal128 containing the absolute value of x. """ if x.is_negative(): return -x return x -fn multiply(x1: Decimal, x2: Decimal) raises -> Decimal: +fn multiply(x1: Decimal128, x2: Decimal128) raises -> Decimal128: """ - Multiplies two Decimal values and returns a new Decimal containing the product. + Multiplies two Decimal128 values and returns a new Decimal128 containing the product. Args: - x1: The first Decimal operand. - x2: The second Decimal operand. + x1: The first Decimal128 operand. + x2: The second Decimal128 operand. Returns: - A new Decimal containing the product of x1 and x2. + A new Decimal128 containing the product of x1 and x2. """ var x1_coef = x1.coefficient() @@ -418,11 +424,11 @@ fn multiply(x1: Decimal, x2: Decimal) raises -> Decimal: # SPECIAL CASE: zero # Return zero while preserving the scale if x1_coef == 0 or x2_coef == 0: - return Decimal( + return Decimal128( 0, 0, 0, - scale=min(combined_scale, Decimal.MAX_SCALE), + scale=min(combined_scale, Decimal128.MAX_SCALE), sign=is_negative, ) @@ -430,39 +436,43 @@ fn multiply(x1: Decimal, x2: Decimal) raises -> Decimal: if x1_coef == 1 and x2_coef == 1: # If the combined scale exceeds the maximum precision, # return 0 with leading zeros after the decimal point and correct sign - if combined_scale > Decimal.MAX_SCALE: - return Decimal( + if combined_scale > Decimal128.MAX_SCALE: + return Decimal128( 0, 0, 0, - Decimal.MAX_SCALE, + Decimal128.MAX_SCALE, is_negative, ) # Otherwise, return 1 with correct sign and scale - var final_scale = min(Decimal.MAX_SCALE, combined_scale) - return Decimal(1, 0, 0, final_scale, is_negative) + var final_scale = min(Decimal128.MAX_SCALE, combined_scale) + return Decimal128(1, 0, 0, final_scale, is_negative) # SPECIAL CASE: First operand has coefficient of 1 if x1_coef == 1: # If x1 is 1, return x2 with correct sign if x1_scale == 0: var result = x2 - result.flags &= ~Decimal.SIGN_MASK + result.flags &= ~Decimal128.SIGN_MASK if is_negative: - result.flags |= Decimal.SIGN_MASK + result.flags |= Decimal128.SIGN_MASK return result else: var prod = x2_coef # Rounding may be needed. - var num_digits_prod = decimojo.utility.number_of_digits(prod) + var num_digits_prod = decimojo.decimal128.utility.number_of_digits( + prod + ) var num_digits_to_keep = num_digits_prod - ( - combined_scale - Decimal.MAX_SCALE + combined_scale - Decimal128.MAX_SCALE ) - var truncated_prod = decimojo.utility.round_to_keep_first_n_digits( - prod, num_digits_to_keep + var truncated_prod = ( + decimojo.decimal128.utility.round_to_keep_first_n_digits( + prod, num_digits_to_keep + ) ) - var final_scale = min(Decimal.MAX_SCALE, combined_scale) - return Decimal.from_uint128( + var final_scale = min(Decimal128.MAX_SCALE, combined_scale) + return Decimal128.from_uint128( truncated_prod, final_scale, is_negative ) @@ -471,30 +481,34 @@ fn multiply(x1: Decimal, x2: Decimal) raises -> Decimal: # If x2 is 1, return x1 with correct sign if x2_scale == 0: var result = x1 - result.flags &= ~Decimal.SIGN_MASK + result.flags &= ~Decimal128.SIGN_MASK if is_negative: - result.flags |= Decimal.SIGN_MASK + result.flags |= Decimal128.SIGN_MASK return result else: var prod = x1_coef # Rounding may be needed. - var num_digits_prod = decimojo.utility.number_of_digits(prod) + var num_digits_prod = decimojo.decimal128.utility.number_of_digits( + prod + ) var num_digits_to_keep = num_digits_prod - ( - combined_scale - Decimal.MAX_SCALE + combined_scale - Decimal128.MAX_SCALE ) - var truncated_prod = decimojo.utility.round_to_keep_first_n_digits( - prod, num_digits_to_keep + var truncated_prod = ( + decimojo.decimal128.utility.round_to_keep_first_n_digits( + prod, num_digits_to_keep + ) ) - var final_scale = min(Decimal.MAX_SCALE, combined_scale) - return Decimal.from_uint128( + var final_scale = min(Decimal128.MAX_SCALE, combined_scale) + return Decimal128.from_uint128( truncated_prod, final_scale, is_negative ) # Determine the number of bits in the coefficients # Used to determine the appropriate multiplication method # The coefficient of result would be the sum of the two numbers of bits - var x1_num_bits = decimojo.utility.number_of_bits(x1_coef) - var x2_num_bits = decimojo.utility.number_of_bits(x2_coef) + var x1_num_bits = decimojo.decimal128.utility.number_of_bits(x1_coef) + var x2_num_bits = decimojo.decimal128.utility.number_of_bits(x2_coef) var combined_num_bits = x1_num_bits + x2_num_bits # SPECIAL CASE: Both operands are true integers @@ -504,20 +518,20 @@ fn multiply(x1: Decimal, x2: Decimal) raises -> Decimal: var prod: UInt64 = UInt64(x1_coef) * UInt64(x2_coef) var low = UInt32(prod & 0xFFFFFFFF) var mid = UInt32((prod >> 32) & 0xFFFFFFFF) - return Decimal(low, mid, 0, 0, is_negative) + return Decimal128(low, mid, 0, 0, is_negative) # Moderate integers, use UInt128 multiplication elif combined_num_bits <= 128: var prod: UInt128 = UInt128(x1_coef) * UInt128(x2_coef) - if prod > Decimal.MAX_AS_UINT128: + if prod > Decimal128.MAX_AS_UINT128: raise Error( String( "Error in `multiply()`: The product is {}, which" - " exceeds the capacity of Decimal (2^96-1)" + " exceeds the capacity of Decimal128 (2^96-1)" ).format(prod) ) else: - return Decimal.from_uint128(prod, 0, is_negative) + return Decimal128.from_uint128(prod, 0, is_negative) # Large integers, it will definitely overflow else: @@ -525,42 +539,44 @@ fn multiply(x1: Decimal, x2: Decimal) raises -> Decimal: raise Error( String( "Error in `multiply()`: The product is {}, which exceeds" - " the capacity of Decimal (2^96-1)" + " the capacity of Decimal128 (2^96-1)" ).format(prod) ) # SPECIAL CASE: Both operands are integers but with scales # Examples: 123.0 * 456.00 if x1.is_integer() and x2.is_integer(): - var x1_integral_part = x1_coef // decimojo.utility.power_of_10[ - DType.uint128 - ](x1_scale) - var x2_integral_part = x2_coef // decimojo.utility.power_of_10[ - DType.uint128 - ](x2_scale) + var x1_integral_part = ( + x1_coef + // decimojo.decimal128.utility.power_of_10[DType.uint128](x1_scale) + ) + var x2_integral_part = ( + x2_coef + // decimojo.decimal128.utility.power_of_10[DType.uint128](x2_scale) + ) var prod: UInt256 = UInt256(x1_integral_part) * UInt256( x2_integral_part ) - if prod > Decimal.MAX_AS_UINT256: - raise Error("Error in `multiply()`: Decimal overflow") + if prod > Decimal128.MAX_AS_UINT256: + raise Error("Error in `multiply()`: Decimal128 overflow") else: - var num_digits = decimojo.utility.number_of_digits(prod) + var num_digits = decimojo.decimal128.utility.number_of_digits(prod) var final_scale = min( - Decimal.MAX_NUM_DIGITS - num_digits, combined_scale + Decimal128.MAX_NUM_DIGITS - num_digits, combined_scale ) # Scale up by adding trailing zeros - prod = prod * decimojo.utility.power_of_10[DType.uint256]( - final_scale - ) + prod = prod * decimojo.decimal128.utility.power_of_10[ + DType.uint256 + ](final_scale) # If it overflows, remove the last zero - if prod > Decimal.MAX_AS_UINT256: + if prod > Decimal128.MAX_AS_UINT256: prod = prod // 10 final_scale -= 1 var low = UInt32(prod & 0xFFFFFFFF) var mid = UInt32((prod >> 32) & 0xFFFFFFFF) var high = UInt32((prod >> 64) & 0xFFFFFFFF) - return Decimal( + return Decimal128( low, mid, high, @@ -568,65 +584,67 @@ fn multiply(x1: Decimal, x2: Decimal) raises -> Decimal: is_negative, ) - # GENERAL CASES: Decimal multiplication with any scales + # GENERAL CASES: Decimal128 multiplication with any scales # SUB-CASE: Both operands are small # The bits of the product will not exceed 96 bits - # It can just fit into Decimal's capacity without overflow + # It can just fit into Decimal128's capacity without overflow # Result coefficient will less than 2^96 - 1 = 79228162514264337593543950335 # Examples: 1.23 * 4.56 if combined_num_bits <= 96: var prod: UInt128 = x1_coef * x2_coef # Combined scale more than max precision, no need to truncate - if combined_scale <= Decimal.MAX_SCALE: - return Decimal.from_uint128(prod, combined_scale, is_negative) + if combined_scale <= Decimal128.MAX_SCALE: + return Decimal128.from_uint128(prod, combined_scale, is_negative) # Combined scale no more than max precision, truncate with rounding else: - var num_digits = decimojo.utility.number_of_digits(prod) + var num_digits = decimojo.decimal128.utility.number_of_digits(prod) var num_digits_to_keep = num_digits - ( - combined_scale - Decimal.MAX_SCALE + combined_scale - Decimal128.MAX_SCALE ) - prod = decimojo.utility.round_to_keep_first_n_digits( + prod = decimojo.decimal128.utility.round_to_keep_first_n_digits( prod, num_digits_to_keep ) - var final_scale = min(Decimal.MAX_SCALE, combined_scale) + var final_scale = min(Decimal128.MAX_SCALE, combined_scale) - if final_scale > Decimal.MAX_SCALE: - var ndigits_prod = decimojo.utility.number_of_digits(prod) - prod = decimojo.utility.round_to_keep_first_n_digits( - prod, ndigits_prod - (final_scale - Decimal.MAX_SCALE) + if final_scale > Decimal128.MAX_SCALE: + var ndigits_prod = decimojo.decimal128.utility.number_of_digits( + prod + ) + prod = decimojo.decimal128.utility.round_to_keep_first_n_digits( + prod, ndigits_prod - (final_scale - Decimal128.MAX_SCALE) ) - final_scale = Decimal.MAX_SCALE + final_scale = Decimal128.MAX_SCALE - return Decimal.from_uint128(prod, final_scale, is_negative) + return Decimal128.from_uint128(prod, final_scale, is_negative) # SUB-CASE: Both operands are moderate # The bits of the product will not exceed 128 bits # Result coefficient will less than 2^128 - 1 but more than 2^96 - 1 - # IMPORTANT: This means that the product will exceed Decimal's capacity + # IMPORTANT: This means that the product will exceed Decimal128's capacity # Either raises an error if intergral part overflows - # Or truncates the product to fit into Decimal's capacity + # Or truncates the product to fit into Decimal128's capacity if combined_num_bits <= 128: var prod: UInt128 = x1_coef * x2_coef # Truncated first 29 digits var truncated_prod_at_max_length = ( - decimojo.utility.round_to_keep_first_n_digits( - prod, Decimal.MAX_NUM_DIGITS + decimojo.decimal128.utility.round_to_keep_first_n_digits( + prod, Decimal128.MAX_NUM_DIGITS ) ) # Check outflow # The number of digits of the integral part var num_digits_of_integral_part = ( - decimojo.utility.number_of_digits(prod) - combined_scale + decimojo.decimal128.utility.number_of_digits(prod) - combined_scale ) - if (num_digits_of_integral_part >= Decimal.MAX_NUM_DIGITS) & ( - truncated_prod_at_max_length > Decimal.MAX_AS_UINT128 + if (num_digits_of_integral_part >= Decimal128.MAX_NUM_DIGITS) & ( + truncated_prod_at_max_length > Decimal128.MAX_AS_UINT128 ): - raise Error("Error in `multiply()`: Decimal overflow") + raise Error("Error in `multiply()`: Decimal128 overflow") # Otherwise, the value will not overflow even after rounding # Determine the final scale after rounding @@ -634,15 +652,15 @@ fn multiply(x1: Decimal, x2: Decimal) raises -> Decimal: # the final coefficient can be of 29 digits. # The final scale can be 29 - num_digits_of_integral_part. var num_digits_of_decimal_part = ( - Decimal.MAX_NUM_DIGITS - num_digits_of_integral_part + Decimal128.MAX_NUM_DIGITS - num_digits_of_integral_part ) # If the first 29 digits exceed the limit, # we need to adjust the num_digits_of_decimal_part by -1 # so that the final coefficient will be of 28 digits. - if truncated_prod_at_max_length > Decimal.MAX_AS_UINT128: + if truncated_prod_at_max_length > Decimal128.MAX_AS_UINT128: num_digits_of_decimal_part -= 1 - prod = decimojo.utility.round_to_keep_first_n_digits( - prod, Decimal.MAX_NUM_DIGITS - 1 + prod = decimojo.decimal128.utility.round_to_keep_first_n_digits( + prod, Decimal128.MAX_NUM_DIGITS - 1 ) else: prod = truncated_prod_at_max_length @@ -650,42 +668,44 @@ fn multiply(x1: Decimal, x2: Decimal) raises -> Decimal: # Yuhao's notes: I think combined_scale should always be smaller var final_scale = min(num_digits_of_decimal_part, combined_scale) - if final_scale > Decimal.MAX_SCALE: - var ndigits_prod = decimojo.utility.number_of_digits(prod) - prod = decimojo.utility.round_to_keep_first_n_digits( - prod, ndigits_prod - (final_scale - Decimal.MAX_SCALE) + if final_scale > Decimal128.MAX_SCALE: + var ndigits_prod = decimojo.decimal128.utility.number_of_digits( + prod ) - final_scale = Decimal.MAX_SCALE + prod = decimojo.decimal128.utility.round_to_keep_first_n_digits( + prod, ndigits_prod - (final_scale - Decimal128.MAX_SCALE) + ) + final_scale = Decimal128.MAX_SCALE - return Decimal.from_uint128(prod, final_scale, is_negative) + return Decimal128.from_uint128(prod, final_scale, is_negative) # REMAINING CASES: Both operands are big # The bits of the product will not exceed 192 bits # Result coefficient will less than 2^192 - 1 but more than 2^128 - 1 - # IMPORTANT: This means that the product will exceed Decimal's capacity + # IMPORTANT: This means that the product will exceed Decimal128's capacity # Either raises an error if intergral part overflows - # Or truncates the product to fit into Decimal's capacity + # Or truncates the product to fit into Decimal128's capacity var prod: UInt256 = UInt256(x1_coef) * UInt256(x2_coef) # Truncated first 29 digits var truncated_prod_at_max_length = ( - decimojo.utility.round_to_keep_first_n_digits( - prod, Decimal.MAX_NUM_DIGITS + decimojo.decimal128.utility.round_to_keep_first_n_digits( + prod, Decimal128.MAX_NUM_DIGITS ) ) # Check outflow # The number of digits of the integral part var num_digits_of_integral_part = ( - decimojo.utility.number_of_digits(prod) - combined_scale + decimojo.decimal128.utility.number_of_digits(prod) - combined_scale ) # Check for overflow of the integral part after rounding - if (num_digits_of_integral_part >= Decimal.MAX_NUM_DIGITS) & ( - truncated_prod_at_max_length > Decimal.MAX_AS_UINT256 + if (num_digits_of_integral_part >= Decimal128.MAX_NUM_DIGITS) & ( + truncated_prod_at_max_length > Decimal128.MAX_AS_UINT256 ): - raise Error("Error in `multiply()`: Decimal overflow") + raise Error("Error in `multiply()`: Decimal128 overflow") # Otherwise, the value will not overflow even after rounding # Determine the final scale after rounding @@ -693,15 +713,15 @@ fn multiply(x1: Decimal, x2: Decimal) raises -> Decimal: # the final coefficient can be of 29 digits. # The final scale can be 29 - num_digits_of_integral_part. var num_digits_of_decimal_part = ( - Decimal.MAX_NUM_DIGITS - num_digits_of_integral_part + Decimal128.MAX_NUM_DIGITS - num_digits_of_integral_part ) # If the first 29 digits exceed the limit, # we need to adjust the num_digits_of_decimal_part by -1 # so that the final coefficient will be of 28 digits. - if truncated_prod_at_max_length > Decimal.MAX_AS_UINT256: + if truncated_prod_at_max_length > Decimal128.MAX_AS_UINT256: num_digits_of_decimal_part -= 1 - prod = decimojo.utility.round_to_keep_first_n_digits( - prod, Decimal.MAX_NUM_DIGITS - 1 + prod = decimojo.decimal128.utility.round_to_keep_first_n_digits( + prod, Decimal128.MAX_NUM_DIGITS - 1 ) else: prod = truncated_prod_at_max_length @@ -709,24 +729,24 @@ fn multiply(x1: Decimal, x2: Decimal) raises -> Decimal: # I think combined_scale should always be smaller var final_scale = min(num_digits_of_decimal_part, combined_scale) - if final_scale > Decimal.MAX_SCALE: - var ndigits_prod = decimojo.utility.number_of_digits(prod) - prod = decimojo.utility.round_to_keep_first_n_digits( - prod, ndigits_prod - (final_scale - Decimal.MAX_SCALE) + if final_scale > Decimal128.MAX_SCALE: + var ndigits_prod = decimojo.decimal128.utility.number_of_digits(prod) + prod = decimojo.decimal128.utility.round_to_keep_first_n_digits( + prod, ndigits_prod - (final_scale - Decimal128.MAX_SCALE) ) - final_scale = Decimal.MAX_SCALE + final_scale = Decimal128.MAX_SCALE # Extract the 32-bit components from the UInt256 product var low = UInt32(prod & 0xFFFFFFFF) var mid = UInt32((prod >> 32) & 0xFFFFFFFF) var high = UInt32((prod >> 64) & 0xFFFFFFFF) - return Decimal(low, mid, high, final_scale, is_negative) + return Decimal128(low, mid, high, final_scale, is_negative) -fn divide(x1: Decimal, x2: Decimal) raises -> Decimal: +fn divide(x1: Decimal128, x2: Decimal128) raises -> Decimal128: """ - Divides x1 by x2 and returns a new Decimal containing the quotient. + Divides x1 by x2 and returns a new Decimal128 containing the quotient. Uses a simpler string-based long division approach as fallback. Args: @@ -734,7 +754,7 @@ fn divide(x1: Decimal, x2: Decimal) raises -> Decimal: x2: The divisor. Returns: - A new Decimal containing the result of x1 / x2. + A new Decimal128 containing the result of x1 / x2. Raises: Error: If x2 is zero. @@ -755,10 +775,10 @@ fn divide(x1: Decimal, x2: Decimal) raises -> Decimal: # For example, 0.000 / 1234.0 = 0.00 # For example, 0.00 / 1.3456 = 0 if x1.is_zero(): - var result = Decimal.ZERO() + var result = Decimal128.ZERO() var result_scale = max(0, x1.scale() - x2.scale()) result.flags = UInt32( - (result_scale << Decimal.SCALE_SHIFT) & Decimal.SCALE_MASK + (result_scale << Decimal128.SCALE_SHIFT) & Decimal128.SCALE_MASK ) return result @@ -779,33 +799,34 @@ fn divide(x1: Decimal, x2: Decimal) raises -> Decimal: # SUB-CASE: divisor is 1 # If divisor is 1, return dividend with correct sign if x2_scale == 0: - return Decimal(x1.low, x1.mid, x1.high, x1_scale, is_negative) + return Decimal128(x1.low, x1.mid, x1.high, x1_scale, is_negative) # SUB-CASE: divisor is of coefficient 1 with positive scale # diff_scale > 0, then final scale is diff_scale elif diff_scale > 0: - return Decimal(x1.low, x1.mid, x1.high, diff_scale, is_negative) + return Decimal128(x1.low, x1.mid, x1.high, diff_scale, is_negative) # diff_scale < 0, then times 10 ** (-diff_scale) else: # If the result can be stored in UInt128 if ( - decimojo.utility.number_of_digits(x1_coef) - diff_scale - < Decimal.MAX_NUM_DIGITS + decimojo.decimal128.utility.number_of_digits(x1_coef) + - diff_scale + < Decimal128.MAX_NUM_DIGITS ): var quot = x1_coef * UInt128(10) ** (-diff_scale) - return Decimal.from_uint128(quot, 0, is_negative) + return Decimal128.from_uint128(quot, 0, is_negative) # If the result should be stored in UInt256 else: var quot = UInt256(x1_coef) * UInt256(10) ** (-diff_scale) - if quot > Decimal.MAX_AS_UINT256: - raise Error("Error in `true_divide()`: Decimal overflow") + if quot > Decimal128.MAX_AS_UINT256: + raise Error("Error in `true_divide()`: Decimal128 overflow") else: var low = UInt32(quot & 0xFFFFFFFF) var mid = UInt32((quot >> 32) & 0xFFFFFFFF) var high = UInt32((quot >> 64) & 0xFFFFFFFF) - return Decimal(low, mid, high, 0, is_negative) + return Decimal128(low, mid, high, 0, is_negative) # SPECIAL CASE: The coefficients are equal # 特例: 係數相等 @@ -819,7 +840,7 @@ fn divide(x1: Decimal, x2: Decimal) raises -> Decimal: # If the scales are positive, return 1 with the difference in scales # For example, 0.1234 / 1234 = 0.0001 if diff_scale >= 0: - return Decimal(1, 0, 0, diff_scale, is_negative) + return Decimal128(1, 0, 0, diff_scale, is_negative) # SUB-CASE: The scales are negative # diff_scale < 0, then times 1e-diff_scale @@ -827,7 +848,7 @@ fn divide(x1: Decimal, x2: Decimal) raises -> Decimal: # Since -diff_scale is less than 28, the result would not overflow else: var quot = UInt128(1) * UInt128(10) ** (-diff_scale) - return Decimal.from_uint128(quot, 0, is_negative) + return Decimal128.from_uint128(quot, 0, is_negative) # SPECIAL CASE: Modulus of coefficients is zero (exact division) # 特例: 係數的餘數爲零 (可除盡) @@ -843,7 +864,7 @@ fn divide(x1: Decimal, x2: Decimal) raises -> Decimal: # High will be zero because the quotient is less than 2^48 # For safety, we still calcuate the high word var quot = x1_coef // x2_coef - return Decimal.from_uint128(quot, diff_scale, is_negative) + return Decimal128.from_uint128(quot, diff_scale, is_negative) else: # If diff_scale < 0, return the quotient with scaling up @@ -853,22 +874,22 @@ fn divide(x1: Decimal, x2: Decimal) raises -> Decimal: # If the result can be stored in UInt128 if ( - decimojo.utility.number_of_digits(quot) - diff_scale - < Decimal.MAX_NUM_DIGITS + decimojo.decimal128.utility.number_of_digits(quot) - diff_scale + < Decimal128.MAX_NUM_DIGITS ): var quot = quot * UInt128(10) ** (-diff_scale) - return Decimal.from_uint128(quot, 0, is_negative) + return Decimal128.from_uint128(quot, 0, is_negative) # If the result should be stored in UInt256 else: var quot = UInt256(quot) * UInt256(10) ** (-diff_scale) - if quot > Decimal.MAX_AS_UINT256: - raise Error("Error in `true_divide()`: Decimal overflow") + if quot > Decimal128.MAX_AS_UINT256: + raise Error("Error in `true_divide()`: Decimal128 overflow") else: var low = UInt32(quot & 0xFFFFFFFF) var mid = UInt32((quot >> 32) & 0xFFFFFFFF) var high = UInt32((quot >> 64) & 0xFFFFFFFF) - return Decimal(low, mid, high, 0, is_negative) + return Decimal128(low, mid, high, 0, is_negative) # REMAINING CASES: Perform long division # 其他情況: 進行長除法 @@ -910,13 +931,15 @@ fn divide(x1: Decimal, x2: Decimal) raises -> Decimal: # Yuhao's notes: remainder should be positive beacuse the previous cases have been handled # 朱宇浩注: 餘數應該爲正,因爲之前的特例已經處理過了 - var x1_ndigits = decimojo.utility.number_of_digits(x1_coef) - var x2_ndigits = decimojo.utility.number_of_digits(x2_coef) + var x1_ndigits = decimojo.decimal128.utility.number_of_digits(x1_coef) + var x2_ndigits = decimojo.decimal128.utility.number_of_digits(x2_coef) var diff_digits = x1_ndigits - x2_ndigits # Here is an estimation of the maximum possible number of digits of the quotient's integral part # If it is higher than 28, we need to use UInt256 to store the quotient var est_max_ndigits_quot_int_part = diff_digits - diff_scale + 1 - var is_use_uint128 = est_max_ndigits_quot_int_part < Decimal.MAX_NUM_DIGITS + var is_use_uint128 = ( + est_max_ndigits_quot_int_part < Decimal128.MAX_NUM_DIGITS + ) # SUB-CASE: Use UInt128 to store the quotient # If the quotient's integral part is less than 28 digits, we can use UInt128 @@ -938,7 +961,7 @@ fn divide(x1: Decimal, x2: Decimal) raises -> Decimal: if is_use_uint128: # Maximum number of steps is minimum of the following two values: # - MAX_NUM_DIGITS - ndigits_initial_quot + 1 - # - Decimal.MAX_SCALE - diff_scale - adjusted_scale + 1 (significant digits be rounded off) + # - Decimal128.MAX_SCALE - diff_scale - adjusted_scale + 1 (significant digits be rounded off) # ndigits_initial_quot is the number of digits of the quotient before using long division # The extra digit is used for rounding up when it is 5 and not exact division @@ -946,16 +969,18 @@ fn divide(x1: Decimal, x2: Decimal) raises -> Decimal: var digit = UInt128(0) # The final step counter stands for the number of dicimal points var step_counter = 0 - var ndigits_initial_quot = decimojo.utility.number_of_digits(quot) + var ndigits_initial_quot = decimojo.decimal128.utility.number_of_digits( + quot + ) while ( (rem != 0) and ( step_counter - < (Decimal.MAX_NUM_DIGITS - ndigits_initial_quot + 1) + < (Decimal128.MAX_NUM_DIGITS - ndigits_initial_quot + 1) ) and ( step_counter - < Decimal.MAX_SCALE - diff_scale - adjusted_scale + 1 + < Decimal128.MAX_SCALE - diff_scale - adjusted_scale + 1 ) ): # Multiply remainder by 10 @@ -986,7 +1011,7 @@ fn divide(x1: Decimal, x2: Decimal) raises -> Decimal: if scale_of_quot < 0: quot = quot * UInt128(10) ** (-scale_of_quot) scale_of_quot = 0 - var ndigits_quot = decimojo.utility.number_of_digits(quot) + var ndigits_quot = decimojo.decimal128.utility.number_of_digits(quot) var ndigits_quot_int_part = ndigits_quot - scale_of_quot # print( @@ -998,43 +1023,49 @@ fn divide(x1: Decimal, x2: Decimal) raises -> Decimal: # TODO: 可以考慮先降 scale 再判斷是否超出最大值. # TODO: 爲降 scale 引入 round_to_remove_last_n_digits 函數 # If quot is within MAX, return the result - if quot <= Decimal.MAX_AS_UINT128: - if scale_of_quot > Decimal.MAX_SCALE: - quot = decimojo.utility.round_to_keep_first_n_digits( + if quot <= Decimal128.MAX_AS_UINT128: + if scale_of_quot > Decimal128.MAX_SCALE: + quot = decimojo.decimal128.utility.round_to_keep_first_n_digits( quot, - ndigits_quot - (scale_of_quot - Decimal.MAX_SCALE), + ndigits_quot - (scale_of_quot - Decimal128.MAX_SCALE), ) - scale_of_quot = Decimal.MAX_SCALE + scale_of_quot = Decimal128.MAX_SCALE - return Decimal.from_uint128(quot, scale_of_quot, is_negative) + return Decimal128.from_uint128(quot, scale_of_quot, is_negative) # Otherwise, we need to truncate the first 29 or 28 digits else: - var truncated_quot = decimojo.utility.round_to_keep_first_n_digits( - quot, Decimal.MAX_NUM_DIGITS + var truncated_quot = ( + decimojo.decimal128.utility.round_to_keep_first_n_digits( + quot, Decimal128.MAX_NUM_DIGITS + ) ) var scale_of_truncated_quot = ( - Decimal.MAX_NUM_DIGITS - ndigits_quot_int_part + Decimal128.MAX_NUM_DIGITS - ndigits_quot_int_part ) - if truncated_quot > Decimal.MAX_AS_UINT128: - truncated_quot = decimojo.utility.round_to_keep_first_n_digits( - quot, Decimal.MAX_NUM_DIGITS - 1 + if truncated_quot > Decimal128.MAX_AS_UINT128: + truncated_quot = ( + decimojo.decimal128.utility.round_to_keep_first_n_digits( + quot, Decimal128.MAX_NUM_DIGITS - 1 + ) ) scale_of_truncated_quot -= 1 - if scale_of_truncated_quot > Decimal.MAX_SCALE: + if scale_of_truncated_quot > Decimal128.MAX_SCALE: var num_digits_truncated_quot = ( - decimojo.utility.number_of_digits(truncated_quot) + decimojo.decimal128.utility.number_of_digits(truncated_quot) ) - truncated_quot = decimojo.utility.round_to_keep_first_n_digits( - truncated_quot, - num_digits_truncated_quot - - (scale_of_truncated_quot - Decimal.MAX_SCALE), + truncated_quot = ( + decimojo.decimal128.utility.round_to_keep_first_n_digits( + truncated_quot, + num_digits_truncated_quot + - (scale_of_truncated_quot - Decimal128.MAX_SCALE), + ) ) - scale_of_truncated_quot = Decimal.MAX_SCALE + scale_of_truncated_quot = Decimal128.MAX_SCALE - return Decimal.from_uint128( + return Decimal128.from_uint128( truncated_quot, scale_of_truncated_quot, is_negative ) @@ -1054,16 +1085,18 @@ fn divide(x1: Decimal, x2: Decimal) raises -> Decimal: var digit = UInt256(0) # The final step counter stands for the number of dicimal points var step_counter = 0 - var ndigits_initial_quot = decimojo.utility.number_of_digits(quot256) + var ndigits_initial_quot = decimojo.decimal128.utility.number_of_digits( + quot256 + ) while ( (rem256 != 0) and ( step_counter - < (Decimal.MAX_NUM_DIGITS - ndigits_initial_quot + 1) + < (Decimal128.MAX_NUM_DIGITS - ndigits_initial_quot + 1) ) and ( step_counter - < Decimal.MAX_SCALE - diff_scale - adjusted_scale + 1 + < Decimal128.MAX_SCALE - diff_scale - adjusted_scale + 1 ) ): # Multiply remainder by 10 @@ -1088,66 +1121,76 @@ fn divide(x1: Decimal, x2: Decimal) raises -> Decimal: if scale_of_quot < 0: quot256 = quot256 * UInt256(10) ** (-scale_of_quot) scale_of_quot = 0 - var ndigits_quot = decimojo.utility.number_of_digits(quot256) + var ndigits_quot = decimojo.decimal128.utility.number_of_digits(quot256) var ndigits_quot_int_part = ndigits_quot - scale_of_quot # If quot is within MAX, return the result - if quot256 <= Decimal.MAX_AS_UINT256: - if scale_of_quot > Decimal.MAX_SCALE: - quot256 = decimojo.utility.round_to_keep_first_n_digits( - quot256, - ndigits_quot - (scale_of_quot - Decimal.MAX_SCALE), + if quot256 <= Decimal128.MAX_AS_UINT256: + if scale_of_quot > Decimal128.MAX_SCALE: + quot256 = ( + decimojo.decimal128.utility.round_to_keep_first_n_digits( + quot256, + ndigits_quot - (scale_of_quot - Decimal128.MAX_SCALE), + ) ) - scale_of_quot = Decimal.MAX_SCALE + scale_of_quot = Decimal128.MAX_SCALE var low = UInt32(quot256 & 0xFFFFFFFF) var mid = UInt32((quot256 >> 32) & 0xFFFFFFFF) var high = UInt32((quot256 >> 64) & 0xFFFFFFFF) - return Decimal(low, mid, high, scale_of_quot, is_negative) + return Decimal128(low, mid, high, scale_of_quot, is_negative) # Otherwise, we need to truncate the first 29 or 28 digits else: - var truncated_quot = decimojo.utility.round_to_keep_first_n_digits( - quot256, Decimal.MAX_NUM_DIGITS + var truncated_quot = ( + decimojo.decimal128.utility.round_to_keep_first_n_digits( + quot256, Decimal128.MAX_NUM_DIGITS + ) ) # If integer part of quot is more than max, raise error - if (ndigits_quot_int_part > Decimal.MAX_NUM_DIGITS) or ( - (ndigits_quot_int_part == Decimal.MAX_NUM_DIGITS) - and (truncated_quot > Decimal.MAX_AS_UINT256) + if (ndigits_quot_int_part > Decimal128.MAX_NUM_DIGITS) or ( + (ndigits_quot_int_part == Decimal128.MAX_NUM_DIGITS) + and (truncated_quot > Decimal128.MAX_AS_UINT256) ): - raise Error("Error in `true_divide()`: Decimal overflow") + raise Error("Error in `true_divide()`: Decimal128 overflow") var scale_of_truncated_quot = ( - Decimal.MAX_NUM_DIGITS - ndigits_quot_int_part + Decimal128.MAX_NUM_DIGITS - ndigits_quot_int_part ) - if truncated_quot > Decimal.MAX_AS_UINT256: - truncated_quot = decimojo.utility.round_to_keep_first_n_digits( - quot256, Decimal.MAX_NUM_DIGITS - 1 + if truncated_quot > Decimal128.MAX_AS_UINT256: + truncated_quot = ( + decimojo.decimal128.utility.round_to_keep_first_n_digits( + quot256, Decimal128.MAX_NUM_DIGITS - 1 + ) ) scale_of_truncated_quot -= 1 - if scale_of_truncated_quot > Decimal.MAX_SCALE: + if scale_of_truncated_quot > Decimal128.MAX_SCALE: var num_digits_truncated_quot = ( - decimojo.utility.number_of_digits(truncated_quot) + decimojo.decimal128.utility.number_of_digits(truncated_quot) ) - truncated_quot = decimojo.utility.round_to_keep_first_n_digits( - truncated_quot, - num_digits_truncated_quot - - (scale_of_truncated_quot - Decimal.MAX_SCALE), + truncated_quot = ( + decimojo.decimal128.utility.round_to_keep_first_n_digits( + truncated_quot, + num_digits_truncated_quot + - (scale_of_truncated_quot - Decimal128.MAX_SCALE), + ) ) - scale_of_truncated_quot = Decimal.MAX_SCALE + scale_of_truncated_quot = Decimal128.MAX_SCALE var low = UInt32(truncated_quot & 0xFFFFFFFF) var mid = UInt32((truncated_quot >> 32) & 0xFFFFFFFF) var high = UInt32((truncated_quot >> 64) & 0xFFFFFFFF) - return Decimal(low, mid, high, scale_of_truncated_quot, is_negative) + return Decimal128( + low, mid, high, scale_of_truncated_quot, is_negative + ) -fn truncate_divide(x1: Decimal, x2: Decimal) raises -> Decimal: +fn truncate_divide(x1: Decimal128, x2: Decimal128) raises -> Decimal128: """Returns the integral part of the quotient (truncating towards zero). The following identity always holds: x_1 == (x_1 // x_2) * x_2 + x_1 % x_2. @@ -1156,7 +1199,7 @@ fn truncate_divide(x1: Decimal, x2: Decimal) raises -> Decimal: x2: The divisor. Returns: - A new Decimal containing the integral part of x1 / x2. + A new Decimal128 containing the integral part of x1 / x2. """ try: return divide(x1, x2).round(0, RoundingMode.ROUND_DOWN) @@ -1164,7 +1207,7 @@ fn truncate_divide(x1: Decimal, x2: Decimal) raises -> Decimal: raise Error("Error in `divide()`: ", e) -fn modulo(x1: Decimal, x2: Decimal) raises -> Decimal: +fn modulo(x1: Decimal128, x2: Decimal128) raises -> Decimal128: """Returns the remainder of the division of x1 by x2. The following identity always holds: x_1 == (x_1 // x_2) * x_2 + x_1 % x_2. @@ -1173,7 +1216,7 @@ fn modulo(x1: Decimal, x2: Decimal) raises -> Decimal: x2: The divisor. Returns: - A new Decimal containing the remainder of x1 / x2. + A new Decimal128 containing the remainder of x1 / x2. """ try: return x1 - (truncate_divide(x1, x2) * x2) diff --git a/src/decimojo/decimal/comparison.mojo b/src/decimojo/decimal128/comparison.mojo similarity index 67% rename from src/decimojo/decimal/comparison.mojo rename to src/decimojo/decimal128/comparison.mojo index 3cef951..23383b7 100644 --- a/src/decimojo/decimal/comparison.mojo +++ b/src/decimojo/decimal128/comparison.mojo @@ -14,44 +14,44 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # -# Implements comparison operations for the Decimal type +# Implements comparison operations for the Decimal128 type # # ===----------------------------------------------------------------------=== # # # List of functions in this module: # -# compare(x: Decimal, y: Decimal) -> Int8: Compares two Decimals -# compare_absolute(x: Decimal, y: Decimal) -> Int8: Compares absolute values of two Decimals -# greater(a: Decimal, b: Decimal) -> Bool: Returns True if a > b -# less(a: Decimal, b: Decimal) -> Bool: Returns True if a < b -# greater_equal(a: Decimal, b: Decimal) -> Bool: Returns True if a >= b -# less_equal(a: Decimal, b: Decimal) -> Bool: Returns True if a <= b -# equal(a: Decimal, b: Decimal) -> Bool: Returns True if a == b -# not_equal(a: Decimal, b: Decimal) -> Bool: Returns True if a != b +# compare(x: Decimal128, y: Decimal128) -> Int8: Compares two Decimals +# compare_absolute(x: Decimal128, y: Decimal128) -> Int8: Compares absolute values of two Decimals +# greater(a: Decimal128, b: Decimal128) -> Bool: Returns True if a > b +# less(a: Decimal128, b: Decimal128) -> Bool: Returns True if a < b +# greater_equal(a: Decimal128, b: Decimal128) -> Bool: Returns True if a >= b +# less_equal(a: Decimal128, b: Decimal128) -> Bool: Returns True if a <= b +# equal(a: Decimal128, b: Decimal128) -> Bool: Returns True if a == b +# not_equal(a: Decimal128, b: Decimal128) -> Bool: Returns True if a != b # # List of internal functions in this module: # -# _compare_abs(a: Decimal, b: Decimal) -> Int: Compares absolute values of two Decimals +# _compare_abs(a: Decimal128, b: Decimal128) -> Int: Compares absolute values of two Decimals # # ===----------------------------------------------------------------------=== # """ -Implements functions for comparison operations on Decimal objects. +Implements functions for comparison operations on Decimal128 objects. """ import testing -from decimojo.decimal.decimal import Decimal -import decimojo.utility +from decimojo.decimal128.decimal128 import Decimal128 +import decimojo.decimal128.utility -fn compare(x: Decimal, y: Decimal) -> Int8: +fn compare(x: Decimal128, y: Decimal128) -> Int8: """ - Compares the values of two Decimal numbers and returns the result. + Compares the values of two Decimal128 numbers and returns the result. Args: - x: First Decimal value. - y: Second Decimal value. + x: First Decimal128 value. + y: Second Decimal128 value. Returns: Terinary value indicating the comparison result: @@ -84,13 +84,13 @@ fn compare(x: Decimal, y: Decimal) -> Int8: return compare_absolute(x, y) -fn compare_absolute(x: Decimal, y: Decimal) -> Int8: +fn compare_absolute(x: Decimal128, y: Decimal128) -> Int8: """ - Compares the absolute values of two Decimal numbers and returns the result. + Compares the absolute values of two Decimal128 numbers and returns the result. Args: - x: First Decimal value. - y: Second Decimal value. + x: First Decimal128 value. + y: Second Decimal128 value. Returns: Terinary value indicating the comparison result: @@ -117,8 +117,12 @@ fn compare_absolute(x: Decimal, y: Decimal) -> Int8: else: # Early return if integer parts have different lengths # Get number of integer digits - var x_int_digits = decimojo.utility.number_of_digits(x_coef) - x_scale - var y_int_digits = decimojo.utility.number_of_digits(y_coef) - y_scale + var x_int_digits = ( + decimojo.decimal128.utility.number_of_digits(x_coef) - x_scale + ) + var y_int_digits = ( + decimojo.decimal128.utility.number_of_digits(y_coef) - y_scale + ) if x_int_digits > y_int_digits: return 1 if x_int_digits < y_int_digits: @@ -153,13 +157,13 @@ fn compare_absolute(x: Decimal, y: Decimal) -> Int8: return 0 -fn greater(a: Decimal, b: Decimal) -> Bool: +fn greater(a: Decimal128, b: Decimal128) -> Bool: """ Returns True if a > b. Args: - a: First Decimal value. - b: Second Decimal value. + a: First Decimal128 value. + b: Second Decimal128 value. Returns: True if a is greater than b, False otherwise. @@ -168,13 +172,13 @@ fn greater(a: Decimal, b: Decimal) -> Bool: return compare(a, b) == 1 -fn less(a: Decimal, b: Decimal) -> Bool: +fn less(a: Decimal128, b: Decimal128) -> Bool: """ Returns True if a < b. Args: - a: First Decimal value. - b: Second Decimal value. + a: First Decimal128 value. + b: Second Decimal128 value. Returns: True if a is less than b, False otherwise. @@ -183,13 +187,13 @@ fn less(a: Decimal, b: Decimal) -> Bool: return compare(a, b) == -1 -fn greater_equal(a: Decimal, b: Decimal) -> Bool: +fn greater_equal(a: Decimal128, b: Decimal128) -> Bool: """ Returns True if a >= b. Args: - a: First Decimal value. - b: Second Decimal value. + a: First Decimal128 value. + b: Second Decimal128 value. Returns: True if a is greater than or equal to b, False otherwise. @@ -198,13 +202,13 @@ fn greater_equal(a: Decimal, b: Decimal) -> Bool: return compare(a, b) >= 0 -fn less_equal(a: Decimal, b: Decimal) -> Bool: +fn less_equal(a: Decimal128, b: Decimal128) -> Bool: """ Returns True if a <= b. Args: - a: First Decimal value. - b: Second Decimal value. + a: First Decimal128 value. + b: Second Decimal128 value. Returns: True if a is less than or equal to b, False otherwise. @@ -213,13 +217,13 @@ fn less_equal(a: Decimal, b: Decimal) -> Bool: return not greater(a, b) -fn equal(a: Decimal, b: Decimal) -> Bool: +fn equal(a: Decimal128, b: Decimal128) -> Bool: """ Returns True if a == b. Args: - a: First Decimal value. - b: Second Decimal value. + a: First Decimal128 value. + b: Second Decimal128 value. Returns: True if a equals b, False otherwise. @@ -228,13 +232,13 @@ fn equal(a: Decimal, b: Decimal) -> Bool: return compare(a, b) == 0 -fn not_equal(a: Decimal, b: Decimal) -> Bool: +fn not_equal(a: Decimal128, b: Decimal128) -> Bool: """ Returns True if a != b. Args: - a: First Decimal value. - b: Second Decimal value. + a: First Decimal128 value. + b: Second Decimal128 value. Returns: True if a is not equal to b, False otherwise. diff --git a/src/decimojo/decimal128/constants.mojo b/src/decimojo/decimal128/constants.mojo new file mode 100644 index 0000000..b7fa089 --- /dev/null +++ b/src/decimojo/decimal128/constants.mojo @@ -0,0 +1,621 @@ +# ===----------------------------------------------------------------------=== # +# Copyright 2025 Yuhao Zhu +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + +"""Useful constants for Decimal128 type.""" + +from decimojo.decimal128.decimal128 import Decimal128 + +# ===----------------------------------------------------------------------=== # +# +# Integer and decimal constants +# The prefix "M" stands for a decimal (money) value. +# This is a convention in C. +# +# ===----------------------------------------------------------------------=== # + +# Integer constants + + +@always_inline +fn M0() -> Decimal128: + """Returns 0 as a Decimal128.""" + return Decimal128(0x0, 0x0, 0x0, 0x0) + + +@always_inline +fn M1() -> Decimal128: + """Returns 1 as a Decimal128.""" + return Decimal128(0x1, 0x0, 0x0, 0x0) + + +@always_inline +fn M2() -> Decimal128: + """Returns 2 as a Decimal128.""" + return Decimal128(0x2, 0x0, 0x0, 0x0) + + +@always_inline +fn M3() -> Decimal128: + """Returns 3 as a Decimal128.""" + return Decimal128(0x3, 0x0, 0x0, 0x0) + + +@always_inline +fn M4() -> Decimal128: + """Returns 4 as a Decimal128.""" + return Decimal128(0x4, 0x0, 0x0, 0x0) + + +@always_inline +fn M5() -> Decimal128: + """Returns 5 as a Decimal128.""" + return Decimal128(0x5, 0x0, 0x0, 0x0) + + +@always_inline +fn M6() -> Decimal128: + """Returns 6 as a Decimal128.""" + return Decimal128(0x6, 0x0, 0x0, 0x0) + + +@always_inline +fn M7() -> Decimal128: + """Returns 7 as a Decimal128.""" + return Decimal128(0x7, 0x0, 0x0, 0x0) + + +@always_inline +fn M8() -> Decimal128: + """Returns 8 as a Decimal128.""" + return Decimal128(0x8, 0x0, 0x0, 0x0) + + +@always_inline +fn M9() -> Decimal128: + """Returns 9 as a Decimal128.""" + return Decimal128(0x9, 0x0, 0x0, 0x0) + + +@always_inline +fn M10() -> Decimal128: + """Returns 10 as a Decimal128.""" + return Decimal128(0xA, 0x0, 0x0, 0x0) + + +# Decimal128 constants + + +@always_inline +fn M0D5() -> Decimal128: + """Returns 0.5 as a Decimal128.""" + return Decimal128(5, 0, 0, 0x10000) + + +@always_inline +fn M0D25() -> Decimal128: + """Returns 0.25 as a Decimal128.""" + return Decimal128(25, 0, 0, 0x20000) + + +# ===----------------------------------------------------------------------=== # +# +# Inverse constants +# +# ===----------------------------------------------------------------------=== # + + +@always_inline +fn INV2() -> Decimal128: + """Returns 1/2 = 0.5.""" + return Decimal128(0x5, 0x0, 0x0, 0x10000) + + +@always_inline +fn INV10() -> Decimal128: + """Returns 1/10 = 0.1.""" + return Decimal128(0x1, 0x0, 0x0, 0x10000) + + +@always_inline +fn INV0D1() -> Decimal128: + """Returns 1/0.1 = 10.""" + return Decimal128(0xA, 0x0, 0x0, 0x0) + + +@always_inline +fn INV0D2() -> Decimal128: + """Returns 1/0.2 = 5.""" + return Decimal128(0x5, 0x0, 0x0, 0x0) + + +@always_inline +fn INV0D3() -> Decimal128: + """Returns 1/0.3 = 3.33333333333333333333333333333333...""" + return Decimal128(0x35555555, 0xCF2607EE, 0x6BB4AFE4, 0x1C0000) + + +@always_inline +fn INV0D4() -> Decimal128: + """Returns 1/0.4 = 2.5.""" + return Decimal128(0x19, 0x0, 0x0, 0x10000) + + +@always_inline +fn INV0D5() -> Decimal128: + """Returns 1/0.5 = 2.""" + return Decimal128(0x2, 0x0, 0x0, 0x0) + + +@always_inline +fn INV0D6() -> Decimal128: + """Returns 1/0.6 = 1.66666666666666666666666666666667...""" + return Decimal128(0x1AAAAAAB, 0x679303F7, 0x35DA57F2, 0x1C0000) + + +@always_inline +fn INV0D7() -> Decimal128: + """Returns 1/0.7 = 1.42857142857142857142857142857143...""" + return Decimal128(0xCDB6DB6E, 0x3434DED3, 0x2E28DDAB, 0x1C0000) + + +@always_inline +fn INV0D8() -> Decimal128: + """Returns 1/0.8 = 1.25.""" + return Decimal128(0x7D, 0x0, 0x0, 0x20000) + + +@always_inline +fn INV0D9() -> Decimal128: + """Returns 1/0.9 = 1.11111111111111111111111111111111...""" + return Decimal128(0x671C71C7, 0x450CAD4F, 0x23E6E54C, 0x1C0000) + + +@always_inline +fn INV1() -> Decimal128: + """Returns 1/1 = 1.""" + return Decimal128(0x1, 0x0, 0x0, 0x0) + + +@always_inline +fn INV1D1() -> Decimal128: + """Returns 1/1.1 = 0.90909090909090909090909090909091...""" + return Decimal128(0x9A2E8BA3, 0x4FC48DCC, 0x1D5FD2E1, 0x1C0000) + + +@always_inline +fn INV1D2() -> Decimal128: + """Returns 1/1.2 = 0.83333333333333333333333333333333...""" + return Decimal128(0x8D555555, 0x33C981FB, 0x1AED2BF9, 0x1C0000) + + +@always_inline +fn INV1D3() -> Decimal128: + """Returns 1/1.3 = 0.76923076923076923076923076923077...""" + return Decimal128(0xC4EC4EC, 0x9243DA72, 0x18DAED83, 0x1C0000) + + +@always_inline +fn INV1D4() -> Decimal128: + """Returns 1/1.4 = 0.71428571428571428571428571428571...""" + return Decimal128(0xE6DB6DB7, 0x9A1A6F69, 0x17146ED5, 0x1C0000) + + +@always_inline +fn INV1D5() -> Decimal128: + """Returns 1/1.5 = 0.66666666666666666666666666666667...""" + return Decimal128(0xAAAAAAB, 0x296E0196, 0x158A8994, 0x1C0000) + + +@always_inline +fn INV1D6() -> Decimal128: + """Returns 1/1.6 = 0.625.""" + return Decimal128(0x271, 0x0, 0x0, 0x30000) + + +@always_inline +fn INV1D7() -> Decimal128: + """Returns 1/1.7 = 0.58823529411764705882352941176471...""" + return Decimal128(0x45A5A5A6, 0xE8520166, 0x1301C4AF, 0x1C0000) + + +@always_inline +fn INV1D8() -> Decimal128: + """Returns 1/1.8 = 0.55555555555555555555555555555556...""" + return Decimal128(0xB38E38E4, 0x228656A7, 0x11F372A6, 0x1C0000) + + +@always_inline +fn INV1D9() -> Decimal128: + """Returns 1/1.9 = 0.52631578947368421052631578947368...""" + return Decimal128(0xAA1AF287, 0x2E2E6D0A, 0x11019509, 0x1C0000) + + +# ===----------------------------------------------------------------------=== # +# +# N / (N+1) constants +# +# ===----------------------------------------------------------------------=== # + + +@always_inline +fn N_DIVIDE_NEXT(n: Int) raises -> Decimal128: + """ + Returns the pre-calculated value of n/(n+1) for n between 1 and 20. + + Args: + n: An integer between 1 and 20, inclusive. + + Returns: + A Decimal128 representing the value of n/(n+1). + + Raises: + Error: If n is outside the range [1, 20]. + """ + if n == 1: + # 1/2 = 0.5 + return Decimal128(0x5, 0x0, 0x0, 0x10000) + elif n == 2: + # 2/3 = 0.66666666666666666666666666666667... + return Decimal128(0xAAAAAAB, 0x296E0196, 0x158A8994, 0x1C0000) + elif n == 3: + # 3/4 = 0.75 + return Decimal128(0x4B, 0x0, 0x0, 0x20000) + elif n == 4: + # 4/5 = 0.8 + return Decimal128(0x8, 0x0, 0x0, 0x10000) + elif n == 5: + # 5/6 = 0.83333333333333333333333333333333... + return Decimal128(0x8D555555, 0x33C981FB, 0x1AED2BF9, 0x1C0000) + elif n == 6: + # 6/7 = 0.85714285714285714285714285714286... + return Decimal128(0x7B6DB6DB, 0xEC1FB8E5, 0x1BB21E99, 0x1C0000) + elif n == 7: + # 7/8 = 0.875 + return Decimal128(0x36B, 0x0, 0x0, 0x30000) + elif n == 8: + # 8/9 = 0.88888888888888888888888888888889... + return Decimal128(0xB8E38E39, 0x373D5772, 0x1CB8B770, 0x1C0000) + elif n == 9: + # 9/10 = 0.9 + return Decimal128(0x9, 0x0, 0x0, 0x10000) + elif n == 10: + # 10/11 = 0.90909090909090909090909090909091... + return Decimal128(0x9A2E8BA3, 0x4FC48DCC, 0x1D5FD2E1, 0x1C0000) + elif n == 11: + # 11/12 = 0.91666666666666666666666666666667... + return Decimal128(0x4EAAAAAB, 0xB8F7422E, 0x1D9E7D2B, 0x1C0000) + elif n == 12: + # 12/13 = 0.92307692307692307692307692307692... + return Decimal128(0xEC4EC4F, 0xAF849FBC, 0x1DD3836A, 0x1C0000) + elif n == 13: + # 13/14 = 0.92857142857142857142857142857143... + return Decimal128(0x45B6DB6E, 0x15225DA3, 0x1E00F67C, 0x1C0000) + elif n == 14: + # 14/15 = 0.93333333333333333333333333333333... + return Decimal128(0x75555555, 0xD39A0238, 0x1E285A35, 0x1C0000) + elif n == 15: + # 15/16 = 0.9375 + return Decimal128(0x249F, 0x0, 0x0, 0x40000) + elif n == 16: + # 16/17 = 0.94117647058823529411764705882353... + return Decimal128(0x3C3C3C3C, 0xD50023D, 0x1E693AB3, 0x1C0000) + elif n == 17: + # 17/18 = 0.94444444444444444444444444444444... + return Decimal128(0xE471C71C, 0x3AB12CE9, 0x1E8442E7, 0x1C0000) + elif n == 18: + # 18/19 = 0.94736842105263157894736842105263... + return Decimal128(0xCBCA1AF3, 0x1FED2AAC, 0x1E9C72AA, 0x1C0000) + elif n == 19: + # 19/20 = 0.95 + return Decimal128(0x5F, 0x0, 0x0, 0x20000) + elif n == 20: + # 20/21 = 0.95238095238095238095238095238095... + return Decimal128(0x33CF3CF4, 0xCD78948D, 0x1EC5E91C, 0x1C0000) + else: + raise Error("N_DIVIDE_NEXT: n must be between 1 and 20, inclusive") + + +# ===----------------------------------------------------------------------=== # +# +# PI constants +# +# ===----------------------------------------------------------------------=== # + + +@always_inline +fn PI() -> Decimal128: + """Returns the value of pi (π) as a Decimal128.""" + return Decimal128(0x41B65F29, 0xB143885, 0x6582A536, 0x1C0000) + + +# ===----------------------------------------------------------------------=== # +# +# EXP constants +# +# ===----------------------------------------------------------------------=== # + + +@always_inline +fn E() -> Decimal128: + """ + Returns the value of Euler's number (e) as a Decimal128. + + Returns: + A Decimal128 representation of Euler's number with maximum precision. + """ + return Decimal128(0x857AED5A, 0xEBECDE35, 0x57D519AB, 0x1C0000) + + +@always_inline +fn E2() -> Decimal128: + """Returns the value of e^2 as a Decimal128.""" + return Decimal128(0xE4DFDCAE, 0x89F7E295, 0xEEC0D6E9, 0x1C0000) + + +@always_inline +fn E3() -> Decimal128: + """Returns the value of e^3 as a Decimal128.""" + return Decimal128(0x236454F7, 0x62055A80, 0x40E65DE2, 0x1B0000) + + +@always_inline +fn E4() -> Decimal128: + """Returns the value of e^4 as a Decimal128.""" + return Decimal128(0x7121EFD3, 0xFB318FB5, 0xB06A87FB, 0x1B0000) + + +@always_inline +fn E5() -> Decimal128: + """Returns the value of e^5 as a Decimal128.""" + return Decimal128(0xD99BD974, 0x9F4BE5C7, 0x2FF472E3, 0x1A0000) + + +@always_inline +fn E6() -> Decimal128: + """Returns the value of e^6 as a Decimal128.""" + return Decimal128(0xADB57A66, 0xBD7A423F, 0x825AD8FF, 0x1A0000) + + +@always_inline +fn E7() -> Decimal128: + """Returns the value of e^7 as a Decimal128.""" + return Decimal128(0x22313FCF, 0x64D5D12F, 0x236F230A, 0x190000) + + +@always_inline +fn E8() -> Decimal128: + """Returns the value of e^8 as a Decimal128.""" + return Decimal128(0x1E892E63, 0xD1BF8B5C, 0x6051E812, 0x190000) + + +@always_inline +fn E9() -> Decimal128: + """Returns the value of e^9 as a Decimal128.""" + return Decimal128(0x34FAB691, 0xE7CD8DEA, 0x1A2EB6C3, 0x180000) + + +@always_inline +fn E10() -> Decimal128: + """Returns the value of e^10 as a Decimal128.""" + return Decimal128(0xBA7F4F65, 0x58692B62, 0x472BDD8F, 0x180000) + + +@always_inline +fn E11() -> Decimal128: + """Returns the value of e^11 as a Decimal128.""" + return Decimal128(0x8C2C6D20, 0x2A86F9E7, 0xC176BAAE, 0x180000) + + +@always_inline +fn E12() -> Decimal128: + """Returns the value of e^12 as a Decimal128.""" + return Decimal128(0xE924992A, 0x31CDC314, 0x3496C2C4, 0x170000) + + +@always_inline +fn E13() -> Decimal128: + """Returns the value of e^13 as a Decimal128.""" + return Decimal128(0x220130DB, 0xC386029A, 0x8EF393FB, 0x170000) + + +@always_inline +fn E14() -> Decimal128: + """Returns the value of e^14 as a Decimal128.""" + return Decimal128(0x3A24795C, 0xC412DF01, 0x26DBB5A0, 0x160000) + + +@always_inline +fn E15() -> Decimal128: + """Returns the value of e^15 as a Decimal128.""" + return Decimal128(0x6C1248BD, 0x90456557, 0x69A0AD8C, 0x160000) + + +@always_inline +fn E16() -> Decimal128: + """Returns the value of e^16 as a Decimal128.""" + return Decimal128(0xB46A97D, 0x90655BBD, 0x1CB66B18, 0x150000) + + +@always_inline +fn E32() -> Decimal128: + """Returns the value of e^32 as a Decimal128.""" + return Decimal128(0x18420EB, 0xCC2501E6, 0xFF24A138, 0xF0000) + + +@always_inline +fn E0D5() -> Decimal128: + """Returns the value of e^0.5 = e^(1/2) as a Decimal128.""" + return Decimal128(0x8E99DD66, 0xC210E35C, 0x3545E717, 0x1C0000) + + +@always_inline +fn E0D25() -> Decimal128: + """Returns the value of e^0.25 = e^(1/4) as a Decimal128.""" + return Decimal128(0xB43646F1, 0x2654858A, 0x297D3595, 0x1C0000) + + +# ===----------------------------------------------------------------------=== # +# +# LN constants +# +# ===----------------------------------------------------------------------=== # + +# The repr of the magic numbers can be obtained by the following code: +# +# ```mojo +# fn print_repr_words(value: String, ln_value: String) raises: +# """ +# Prints the hex representation of a logarithm value. +# Args: +# value: The original value (for display purposes). +# ln_value: The natural logarithm as a String. +# """ +# var log_decimal = Decimal128(ln_value) +# print("ln(" + value + "): " + log_decimal.repr_words()) +# ``` + + +# Constants for integers + + +@always_inline +fn LN1() -> Decimal128: + """Returns ln(1) = 0.""" + return Decimal128(0x0, 0x0, 0x0, 0x0) + + +@always_inline +fn LN2() -> Decimal128: + """Returns ln(2) = 0.69314718055994530941723212145818...""" + return Decimal128(0xAA7A65BF, 0x81F52F01, 0x1665943F, 0x1C0000) + + +@always_inline +fn LN10() -> Decimal128: + """Returns ln(10) = 2.30258509299404568401799145468436...""" + return Decimal128(0x9FA69733, 0x1414B220, 0x4A668998, 0x1C0000) + + +# Constants for values less than 1 +@always_inline +fn LN0D1() -> Decimal128: + """Returns ln(0.1) = -2.30258509299404568401799145468436...""" + return Decimal128(0x9FA69733, 0x1414B220, 0x4A668998, 0x801C0000) + + +@always_inline +fn LN0D2() -> Decimal128: + """Returns ln(0.2) = -1.60943791243410037460075933322619...""" + return Decimal128(0xF52C3174, 0x921F831E, 0x3400F558, 0x801C0000) + + +@always_inline +fn LN0D3() -> Decimal128: + """Returns ln(0.3) = -1.20397280432593599262274621776184...""" + return Decimal128(0x2B8E6822, 0x8258467, 0x26E70795, 0x801C0000) + + +@always_inline +fn LN0D4() -> Decimal128: + """Returns ln(0.4) = -0.91629073187415506518352721176801...""" + return Decimal128(0x4AB1CBB6, 0x102A541D, 0x1D9B6119, 0x801C0000) + + +@always_inline +fn LN0D5() -> Decimal128: + """Returns ln(0.5) = -0.69314718055994530941723212145818...""" + return Decimal128(0xAA7A65BF, 0x81F52F01, 0x1665943F, 0x801C0000) + + +@always_inline +fn LN0D6() -> Decimal128: + """Returns ln(0.6) = -0.51082562376599068320551409630366...""" + return Decimal128(0x81140263, 0x86305565, 0x10817355, 0x801C0000) + + +@always_inline +fn LN0D7() -> Decimal128: + """Returns ln(0.7) = -0.35667494393873237891263871124118...""" + return Decimal128(0x348BC5A8, 0x8B755D08, 0xB865892, 0x801C0000) + + +@always_inline +fn LN0D8() -> Decimal128: + """Returns ln(0.8) = -0.22314355131420975576629509030983...""" + return Decimal128(0xA03765F7, 0x8E35251B, 0x735CCD9, 0x801C0000) + + +@always_inline +fn LN0D9() -> Decimal128: + """Returns ln(0.9) = -0.10536051565782630122750098083931...""" + return Decimal128(0xB7763910, 0xFC3656AD, 0x3678591, 0x801C0000) + + +# Constants for values greater than 1 + + +@always_inline +fn LN1D1() -> Decimal128: + """Returns ln(1.1) = 0.09531017980432486004395212328077...""" + return Decimal128(0x7212FFD1, 0x7D9A10, 0x3146328, 0x1C0000) + + +@always_inline +fn LN1D2() -> Decimal128: + """Returns ln(1.2) = 0.18232155679395462621171802515451...""" + return Decimal128(0x2966635C, 0xFBC4D99C, 0x5E420E9, 0x1C0000) + + +@always_inline +fn LN1D3() -> Decimal128: + """Returns ln(1.3) = 0.26236426446749105203549598688095...""" + return Decimal128(0xE0BE71FD, 0xC254E078, 0x87A39F0, 0x1C0000) + + +@always_inline +fn LN1D4() -> Decimal128: + """Returns ln(1.4) = 0.33647223662121293050459341021699...""" + return Decimal128(0x75EEA016, 0xF67FD1F9, 0xADF3BAC, 0x1C0000) + + +@always_inline +fn LN1D5() -> Decimal128: + """Returns ln(1.5) = 0.40546510810816438197801311546435...""" + return Decimal128(0xC99DC953, 0x89F9FEB7, 0xD19EDC3, 0x1C0000) + + +@always_inline +fn LN1D6() -> Decimal128: + """Returns ln(1.6) = 0.47000362924573555365093703114834...""" + return Decimal128(0xA42FFC8, 0xF3C009E6, 0xF2FC765, 0x1C0000) + + +@always_inline +fn LN1D7() -> Decimal128: + """Returns ln(1.7) = 0.53062825106217039623154316318876...""" + return Decimal128(0x64BB9ED0, 0x4AB9978F, 0x11254107, 0x1C0000) + + +@always_inline +fn LN1D8() -> Decimal128: + """Returns ln(1.8) = 0.58778666490211900818973114061886...""" + return Decimal128(0xF3042CAE, 0x85BED853, 0x12FE0EAD, 0x1C0000) + + +@always_inline +fn LN1D9() -> Decimal128: + """Returns ln(1.9) = 0.64185388617239477599103597720349...""" + return Decimal128(0x12F992DC, 0xE7374425, 0x14BD4A78, 0x1C0000) diff --git a/src/decimojo/decimal/decimal.mojo b/src/decimojo/decimal128/decimal128.mojo similarity index 70% rename from src/decimojo/decimal/decimal.mojo rename to src/decimojo/decimal128/decimal128.mojo index c426fcd..fdbbaf0 100644 --- a/src/decimojo/decimal/decimal.mojo +++ b/src/decimojo/decimal128/decimal128.mojo @@ -14,9 +14,9 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # -"""Implements basic object methods for the Decimal type. +"""Implements basic object methods for the Decimal128 type. -This module contains the basic object methods for the Decimal type. +This module contains the basic object methods for the Decimal128 type. These methods include constructors, life time methods, output dunders, type-transfer dunders, basic arithmetic operation dunders, comparison operation dunders, and other dunders that implement traits, as well as @@ -26,19 +26,19 @@ mathematical methods that do not implement a trait. from memory import UnsafePointer import testing -import decimojo.decimal.arithmetics -import decimojo.decimal.comparison -import decimojo.decimal.constants -import decimojo.decimal.exponential -import decimojo.decimal.rounding +import decimojo.decimal128.arithmetics +import decimojo.decimal128.comparison +import decimojo.decimal128.constants +import decimojo.decimal128.exponential +import decimojo.decimal128.rounding from decimojo.rounding_mode import RoundingMode -import decimojo.utility +import decimojo.decimal128.utility -alias Dec = Decimal +alias Dec128 = Decimal128 @register_passable("trivial") -struct Decimal( +struct Decimal128( Absable, Comparable, Floatable, @@ -54,7 +54,7 @@ struct Decimal( Internal Representation: - Each decimal uses a 128-bit on memory, where: + Each decimal128 uses a 128-bit on memory, where: - 96 bits for the coefficient (significand), which is 96-bit unsigned integers stored as three 32 bit integer (little-endian). - Bit 0 to 31 are stored in the low field: least significant bits. @@ -130,83 +130,83 @@ struct Decimal( # Special values @always_inline @staticmethod - fn INFINITY() -> Decimal: + fn INFINITY() -> Self: """Returns a Decimal representing positive infinity. Internal representation: `0b0000_0000_0000_0000_0000_0000_0001`. """ - return Decimal(0, 0, 0, 0x00000001) + return Self(0, 0, 0, 0x00000001) @always_inline @staticmethod - fn NEGATIVE_INFINITY() -> Decimal: - """Returns a Decimal representing negative infinity. + fn NEGATIVE_INFINITY() -> Self: + """Returns a Decimal128 representing negative infinity. Internal representation: `0b1000_0000_0000_0000_0000_0000_0001`. """ - return Decimal(0, 0, 0, 0x80000001) + return Self(0, 0, 0, 0x80000001) @always_inline @staticmethod - fn NAN() -> Decimal: - """Returns a Decimal representing Not a Number (NaN). + fn NAN() -> Self: + """Returns a Decimal128 representing Not a Number (NaN). Internal representation: `0b0000_0000_0000_0000_0000_0000_0010`. """ - return Decimal(0, 0, 0, 0x00000010) + return Self(0, 0, 0, 0x00000010) @always_inline @staticmethod - fn NEGATIVE_NAN() -> Decimal: - """Returns a Decimal representing negative Not a Number. + fn NEGATIVE_NAN() -> Self: + """Returns a Decimal128 representing negative Not a Number. Internal representation: `0b1000_0000_0000_0000_0000_0000_0010`. """ - return Decimal(0, 0, 0, 0x80000010) + return Self(0, 0, 0, 0x80000010) @always_inline @staticmethod - fn ZERO() -> Decimal: - """Returns a Decimal representing 0.""" - return Decimal(0, 0, 0, 0) + fn ZERO() -> Decimal128: + """Returns a Decimal128 representing 0.""" + return Self(0, 0, 0, 0) @always_inline @staticmethod - fn ONE() -> Decimal: - """Returns a Decimal representing 1.""" - return Decimal(1, 0, 0, 0) + fn ONE() -> Decimal128: + """Returns a Decimal128 representing 1.""" + return Self(1, 0, 0, 0) @always_inline @staticmethod - fn MAX() -> Decimal: + fn MAX() -> Decimal128: """ - Returns the maximum possible Decimal value. + Returns the maximum possible Decimal128 value. This is equivalent to 79228162514264337593543950335. """ - return Decimal(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0) + return Self(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0) @always_inline @staticmethod - fn MIN() -> Decimal: - """Returns the minimum possible Decimal value (negative of MAX). + fn MIN() -> Decimal128: + """Returns the minimum possible Decimal128 value (negative of MAX). This is equivalent to -79228162514264337593543950335. """ - return Decimal(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, Decimal.SIGN_MASK) + return Self(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, Decimal128.SIGN_MASK) @always_inline @staticmethod - fn PI() -> Decimal: - """Returns the value of pi (π) as a Decimal.""" - return decimojo.decimal.constants.PI() + fn PI() -> Decimal128: + """Returns the value of pi (π) as a Decimal128.""" + return decimojo.decimal128.constants.PI() @always_inline @staticmethod - fn E() -> Decimal: - """Returns the value of Euler's number (e) as a Decimal.""" - return decimojo.decimal.constants.E() + fn E() -> Decimal128: + """Returns the value of Euler's number (e) as a Decimal128.""" + return decimojo.decimal128.constants.E() # ===------------------------------------------------------------------=== # # Constructors and life time dunder methods # ===------------------------------------------------------------------=== # fn __init__(out self): - """Initializes a decimal instance with value 0.""" + """Initializes a decimal128 instance with value 0.""" self.low = 0x00000000 self.mid = 0x00000000 self.high = 0x00000000 @@ -215,9 +215,9 @@ struct Decimal( fn __init__( out self, low: UInt32, mid: UInt32, high: UInt32, flags: UInt32 ): - """Initializes a Decimal with four raw words of internal representation. + """Initializes a Decimal128 with four raw words of internal representation. ***WARNING***: This method does not check the flags. - If you are not sure about the flags, use `Decimal.from_words()` instead. + If you are not sure about the flags, use `Decimal128.from_words()` instead. """ self.low = low @@ -233,48 +233,48 @@ struct Decimal( scale: UInt32, sign: Bool, ) raises: - """Initializes a Decimal with five components. - See `Decimal.from_components()` for more information. + """Initializes a Decimal128 with five components. + See `Decimal128.from_components()` for more information. """ try: - self = Decimal.from_components(low, mid, high, scale, sign) + self = Decimal128.from_components(low, mid, high, scale, sign) except e: raise Error( - "Error in `Decimal.__init__()` with five components: ", e + "Error in `Decimal128.__init__()` with five components: ", e ) fn __init__(out self, value: Int): - """Initializes a Decimal from an integer. + """Initializes a Decimal128 from an integer. See `from_int()` for more information. """ - self = Decimal.from_int(value) + self = Decimal128.from_int(value) fn __init__(out self, value: Int, scale: UInt32) raises: - """Initializes a Decimal from an integer. + """Initializes a Decimal128 from an integer. See `from_int()` for more information. """ try: - self = Decimal.from_int(value, scale) + self = Decimal128.from_int(value, scale) except e: - raise Error("Error in `Decimal.__init__()` with Int: ", e) + raise Error("Error in `Decimal128.__init__()` with Int: ", e) fn __init__(out self, value: String) raises: - """Initializes a Decimal from a string representation. + """Initializes a Decimal128 from a string representation. See `from_string()` for more information. """ try: - self = Decimal.from_string(value) + self = Decimal128.from_string(value) except e: raise Error("Error in `Decimal__init__()` with String: ", e) fn __init__(out self, value: Float64) raises: - """Initializes a Decimal from a floating-point value. + """Initializes a Decimal128 from a floating-point value. See `from_float` for more information. """ try: - self = Decimal.from_float(value) + self = Decimal128.from_float(value) except e: raise Error("Error in `Decimal__init__()` with Float64: ", e) @@ -290,17 +290,17 @@ struct Decimal( scale: UInt32, sign: Bool, ) raises -> Self: - """Initializes a Decimal with five components. + """Initializes a Decimal128 with five components. Args: low: Least significant 32 bits of coefficient. mid: Middle 32 bits of coefficient. high: Most significant 32 bits of coefficient. - scale: Number of decimal places (0-28). + scale: Number of decimal128 places (0-28). sign: True if the number is negative. Returns: - A Decimal instance with the given components. + A Decimal128 instance with the given components. Raises: Error: If the scale is greater than MAX_SCALE. @@ -309,8 +309,8 @@ struct Decimal( if scale > Self.MAX_SCALE: raise Error( String( - "Error in Decimal constructor with five components: Scale" - " must be between 0 and 28, but got {}" + "Error in Decimal128 constructor with five components:" + " Scale must be between 0 and 28, but got {}" ).format(scale) ) @@ -318,13 +318,13 @@ struct Decimal( flags |= (scale << Self.SCALE_SHIFT) & Self.SCALE_MASK flags |= sign << 31 - return Decimal(low, mid, high, flags) + return Self(low, mid, high, flags) @staticmethod fn from_words( low: UInt32, mid: UInt32, high: UInt32, flags: UInt32 ) raises -> Self: - """Initializes a Decimal with four raw words of internal representation. + """Initializes a Decimal128 with four raw words of internal representation. Compared to `__init__()` with four words, this method checks the flags. Args: @@ -334,7 +334,7 @@ struct Decimal( flags: Scale information and the sign. Returns: - A Decimal instance with the given words. + A Decimal128 instance with the given words. Raises: Error: If the `flags` word is invalid. @@ -345,29 +345,29 @@ struct Decimal( testing.assert_true( (flags & 0b0111_1111_0000_0000_1111_1111_1111_1111) == 0, String( - "Error in Decimal constructor with four words: Flags must" + "Error in Decimal128 constructor with four words: Flags must" " have bits 0-15 and 24-30 set to zero, but got {}" ).format(flags), ) testing.assert_true( ((flags & 0x00FF0000) >> Self.SCALE_SHIFT) <= Self.MAX_SCALE, String( - "Error in Decimal constructor with four words: Scale must" + "Error in Decimal128 constructor with four words: Scale must" " be between 0 and 28, but got {}" ).format((flags & 0x00FF0000) >> Self.SCALE_SHIFT), ) - return Decimal(low, mid, high, flags) + return Self(low, mid, high, flags) @staticmethod fn from_int(value: Int) -> Self: - """Initializes a Decimal from an integer. + """Initializes a Decimal128 from an integer. Args: - value: The integer value to convert to Decimal. + value: The integer value to convert to Decimal128. Returns: - The Decimal representation of the integer. + The Decimal128 representation of the integer. Notes: @@ -375,9 +375,9 @@ struct Decimal( Examples: ```mojo - from decimojo import Decimal - var dec1 = Decimal.from_int(-123) # -123 - var dec2 = Decimal.from_int(1) # 1 + from decimojo import Decimal128 + var dec1 = Decimal128.from_int(-123) # -123 + var dec2 = Decimal128.from_int(1) # 1 ``` End of examples. """ @@ -396,18 +396,18 @@ struct Decimal( low = UInt32(abs_value & 0xFFFFFFFF) mid = UInt32((abs_value >> 32) & 0xFFFFFFFF) - return Decimal(low, mid, 0, flags) + return Self(low, mid, 0, flags) @staticmethod fn from_int(value: Int, scale: UInt32) raises -> Self: - """Initializes a Decimal from an integer and a scale. + """Initializes a Decimal128 from an integer and a scale. Args: - value: The integer value to convert to Decimal. - scale: The number of decimal places (0-28). + value: The integer value to convert to Decimal128. + scale: The number of decimal128 places (0-28). Returns: - The Decimal representation of the integer. + The Decimal128 representation of the integer. Raises: Error: If the scale is greater than MAX_SCALE. @@ -418,9 +418,9 @@ struct Decimal( Examples: ```mojo - from decimojo import Decimal - var dec1 = Decimal.from_int(-123, scale=2) # -1.23 - var dec2 = Decimal.from_int(1, scale=5) # 0.00001 + from decimojo import Decimal128 + var dec1 = Decimal128.from_int(-123, scale=2) # -1.23 + var dec2 = Decimal128.from_int(1, scale=5) # 0.00001 ``` End of examples. """ @@ -432,7 +432,7 @@ struct Decimal( if scale > Self.MAX_SCALE: raise Error( String( - "Error in Decimal constructor with Int: Scale must be" + "Error in Decimal128 constructor with Int: Scale must be" " between 0 and 28, but got {}" ).format(scale) ) @@ -450,21 +450,21 @@ struct Decimal( flags |= (scale << Self.SCALE_SHIFT) & Self.SCALE_MASK - return Decimal(low, mid, 0, flags) + return Self(low, mid, 0, flags) @staticmethod fn from_uint128( value: UInt128, scale: UInt32 = 0, sign: Bool = False - ) raises -> Decimal: - """Initializes a Decimal from a UInt128 value. + ) raises -> Decimal128: + """Initializes a Decimal128 from a UInt128 value. Args: - value: The UInt128 value to convert to Decimal. - scale: The number of decimal places (0-28). + value: The UInt128 value to convert to Decimal128. + scale: The number of decimal128 places (0-28). sign: True if the number is negative. Returns: - The Decimal representation of the UInt128 value. + The Decimal128 representation of the UInt128 value. Raises: Error: If the most significant word of the UInt128 is not zero. @@ -474,7 +474,7 @@ struct Decimal( if value >> 96 != 0: raise Error( String( - "Error in Decimal constructor with UInt128: Value must" + "Error in Decimal128 constructor with UInt128: Value must" " fit in 96 bits, but got {}" ).format(value) ) @@ -482,26 +482,26 @@ struct Decimal( if scale > Self.MAX_SCALE: raise Error( String( - "Error in Decimal constructor with five components: Scale" - " must be between 0 and 28, but got {}" + "Error in Decimal128 constructor with five components:" + " Scale must be between 0 and 28, but got {}" ).format(scale) ) - var result = UnsafePointer(to=value).bitcast[Decimal]()[] + var result = UnsafePointer(to=value).bitcast[Decimal128]()[] result.flags |= (scale << Self.SCALE_SHIFT) & Self.SCALE_MASK result.flags |= sign << 31 return result @staticmethod - fn from_string(value: String) raises -> Decimal: - """Initializes a Decimal from a string representation. + fn from_string(value: String) raises -> Decimal128: + """Initializes a Decimal128 from a string representation. Args: - value: The string representation of the Decimal. + value: The string representation of the Decimal128. Returns: - The Decimal representation of the string. + The Decimal128 representation of the string. Raises: Error: If an error occurs during the conversion, forward the error. @@ -526,12 +526,12 @@ struct Decimal( var value_bytes_len = len(value_bytes) if value_bytes_len == 0: - return Decimal.ZERO() + return Decimal128.ZERO() if value_bytes_len != value_string_slice.char_length(): raise Error( String( - "There are invalid characters in decimal string: {}" + "There are invalid characters in decimal128 string: {}" ).format(value) ) @@ -618,7 +618,9 @@ struct Decimal( # Mantissa part else: # Skip the digit if mantissa is too long - if num_mantissa_digits > Decimal.MAX_NUM_DIGITS + 8: # 37 + if ( + num_mantissa_digits > Decimal128.MAX_NUM_DIGITS + 8 + ): # 37 continue mantissa_sign_read = True @@ -639,7 +641,7 @@ struct Decimal( if exponent_notation_read: # Raise an error if the exponent part is too large if (not exponent_sign) and ( - raw_exponent > Decimal.MAX_NUM_DIGITS * 2 + raw_exponent > Decimal128.MAX_NUM_DIGITS * 2 ): raise Error( String("Exponent part is too large: {}").format( @@ -649,7 +651,7 @@ struct Decimal( # Skip the digit if exponent is negatively too large elif (exponent_sign) and ( - raw_exponent > Decimal.MAX_NUM_DIGITS * 2 + raw_exponent > Decimal128.MAX_NUM_DIGITS * 2 ): continue @@ -660,7 +662,9 @@ struct Decimal( # Mantissa part else: # Skip the digit if mantissa is too long - if num_mantissa_digits > Decimal.MAX_NUM_DIGITS + 8: # 37 + if ( + num_mantissa_digits > Decimal128.MAX_NUM_DIGITS + 8 + ): # 37 continue mantissa_significant_start = True @@ -674,13 +678,13 @@ struct Decimal( else: raise Error( - String("Invalid character in decimal string: {}").format( + String("Invalid character in decimal128 string: {}").format( chr(Int(code)) ) ) if unexpected_end_char: - raise Error("Unexpected end character in decimal string.") + raise Error("Unexpected end character in decimal128 string.") # print("DEBUG: coef = ", coef) # print("DEBUG: scale = ", scale) @@ -705,76 +709,80 @@ struct Decimal( # TODO: The following part can be written into a function # because it is used in many cases - if coef <= Decimal.MAX_AS_UINT128: - if scale > Decimal.MAX_SCALE: - coef = decimojo.utility.round_to_keep_first_n_digits( + if coef <= Decimal128.MAX_AS_UINT128: + if scale > Decimal128.MAX_SCALE: + coef = decimojo.decimal128.utility.round_to_keep_first_n_digits( coef, - Int(num_mantissa_digits) - Int(scale - Decimal.MAX_SCALE), + Int(num_mantissa_digits) + - Int(scale - Decimal128.MAX_SCALE), ) - # print("DEBUG: coef = ", coef) - # print( - # "DEBUG: kept digits =", - # Int(num_mantissa_digits) - Int(scale - Decimal.MAX_SCALE), - # ) - scale = Decimal.MAX_SCALE + scale = Decimal128.MAX_SCALE - return Decimal.from_uint128(coef, scale, mantissa_sign) + return Decimal128.from_uint128(coef, scale, mantissa_sign) else: - var ndigits_coef = decimojo.utility.number_of_digits(coef) + var ndigits_coef = decimojo.decimal128.utility.number_of_digits( + coef + ) var ndigits_quot_int_part = ndigits_coef - scale - var truncated_coef = decimojo.utility.round_to_keep_first_n_digits( - coef, Decimal.MAX_NUM_DIGITS + var truncated_coef = ( + decimojo.decimal128.utility.round_to_keep_first_n_digits( + coef, Decimal128.MAX_NUM_DIGITS + ) ) var scale_of_truncated_coef = ( - Decimal.MAX_NUM_DIGITS - ndigits_quot_int_part + Decimal128.MAX_NUM_DIGITS - ndigits_quot_int_part ) - if truncated_coef > Decimal.MAX_AS_UINT128: - truncated_coef = decimojo.utility.round_to_keep_first_n_digits( - coef, Decimal.MAX_NUM_DIGITS - 1 + if truncated_coef > Decimal128.MAX_AS_UINT128: + truncated_coef = ( + decimojo.decimal128.utility.round_to_keep_first_n_digits( + coef, Decimal128.MAX_NUM_DIGITS - 1 + ) ) scale_of_truncated_coef -= 1 - if scale_of_truncated_coef > Decimal.MAX_SCALE: + if scale_of_truncated_coef > Decimal128.MAX_SCALE: var num_digits_truncated_coef = ( - decimojo.utility.number_of_digits(truncated_coef) + decimojo.decimal128.utility.number_of_digits(truncated_coef) ) - truncated_coef = decimojo.utility.round_to_keep_first_n_digits( - truncated_coef, - num_digits_truncated_coef - - Int(scale_of_truncated_coef - Decimal.MAX_SCALE), + truncated_coef = ( + decimojo.decimal128.utility.round_to_keep_first_n_digits( + truncated_coef, + num_digits_truncated_coef + - Int(scale_of_truncated_coef - Decimal128.MAX_SCALE), + ) ) - scale_of_truncated_coef = Decimal.MAX_SCALE + scale_of_truncated_coef = Decimal128.MAX_SCALE - return Decimal.from_uint128( + return Decimal128.from_uint128( truncated_coef, scale_of_truncated_coef, mantissa_sign ) @staticmethod - fn from_float(value: Float64) raises -> Decimal: - """Initializes a Decimal from a floating-point value. + fn from_float(value: Float64) raises -> Decimal128: + """Initializes a Decimal128 from a floating-point value. The reliability of this method is limited by the precision of Float64. Float64 is reliable up to 15 significant digits and marginally reliable up to 16 siginficant digits. Be careful when using this method. Args: - value: The floating-point value to convert to Decimal. + value: The floating-point value to convert to Decimal128. Returns: - The Decimal representation of the floating-point value. + The Decimal128 representation of the floating-point value. Raises: - Error: If the input is too large to be transformed into Decimal. + Error: If the input is too large to be transformed into Decimal128. Error: If the input is infinity or NaN. Example: ```mojo - from decimojo import Decimal - print(Decimal.from_float(Float64(3.1415926535897932383279502))) + from decimojo import Decimal128 + print(Decimal128.from_float(Float64(3.1415926535897932383279502))) # 3.1415926535897932 (17 significant digits) - print(Decimal.from_float(12345678901234567890.12345678901234567890)) + print(Decimal128.from_float(12345678901234567890.12345678901234567890)) # 12345678901234567168 (20 significant digits, but only 15 are reliable) ``` . @@ -782,7 +790,7 @@ struct Decimal( # CASE: Zero if value == Float64(0): - return Decimal.ZERO() + return Decimal128.ZERO() # Get the positive value of the input var abs_value: Float64 @@ -793,11 +801,11 @@ struct Decimal( abs_value = value # Early exit if the value is too large - if UInt128(abs_value) > Decimal.MAX_AS_UINT128: + if UInt128(abs_value) > Decimal128.MAX_AS_UINT128: raise Error( String( "Error in `from_float`: The float value {} is too" - " large (>=2^96) to be transformed into Decimal" + " large (>=2^96) to be transformed into Decimal128" ).format(value) ) @@ -809,22 +817,22 @@ struct Decimal( # CASE: Denormalized number that is very close to zero if biased_exponent == 0: - return Decimal(0, 0, 0, Decimal.MAX_SCALE, is_negative) + return Self(0, 0, 0, Decimal128.MAX_SCALE, is_negative) # CASE: Infinity or NaN if biased_exponent == 0x7FF: - raise Error("Cannot convert infinity or NaN to Decimal") + raise Error("Cannot convert infinity or NaN to Decimal128") # Get unbias exponent var binary_exp: Int = biased_exponent - 1023 # print("DEBUG: binary_exp = ", binary_exp) - # Convert binary exponent to approximate decimal exponent + # Convert binary exponent to approximate decimal128 exponent # log10(2^exp) = exp * log10(2) var decimal_exp: Int = Int(Float64(binary_exp) * 0.301029995663981) # print("DEBUG: decimal_exp = ", decimal_exp) - # Fine-tune decimal exponent + # Fine-tune decimal128 exponent var power_check: Float64 = abs_value / Float64(10) ** decimal_exp if power_check >= 10.0: decimal_exp += 1 @@ -841,12 +849,12 @@ struct Decimal( var scale = 0 var temp_coef: UInt128 var num_trailing_zeros: Int = 0 - while scale < Decimal.MAX_SCALE: + while scale < Decimal128.MAX_SCALE: remainder *= 10 var int_part = UInt128(remainder) remainder = abs(remainder - Float64(int_part)) temp_coef = coefficient * 10 + int_part - if temp_coef > Decimal.MAX_AS_UINT128: + if temp_coef > Decimal128.MAX_AS_UINT128: break coefficient = temp_coef scale += 1 @@ -866,28 +874,28 @@ struct Decimal( var high = UInt32((coefficient >> 64) & 0xFFFFFFFF) # Return both the significant digits and the scale - return Decimal(low, mid, high, scale, is_negative) + return Self(low, mid, high, scale, is_negative) @always_inline fn copy(self) -> Self: - """Returns a copy of the Decimal.""" - return Decimal(self.low, self.mid, self.high, self.flags) + """Returns a copy of the Decimal128.""" + return Self(self.low, self.mid, self.high, self.flags) @always_inline fn clone(self) -> Self: - """Returns a copy of the Decimal.""" - return Decimal(self.low, self.mid, self.high, self.flags) + """Returns a copy of the Decimal128.""" + return Self(self.low, self.mid, self.high, self.flags) # ===------------------------------------------------------------------=== # # Output dunders, type-transfer dunders # ===------------------------------------------------------------------=== # fn __float__(self) -> Float64: - """Converts this Decimal to a floating-point value. - Because Decimal is fixed-point, this may lose precision. + """Converts this Decimal128 to a floating-point value. + Because Decimal128 is fixed-point, this may lose precision. Returns: - The floating-point representation of this Decimal. + The floating-point representation of this Decimal128. """ var result = Float64(self.coefficient()) / (Float64(10) ** self.scale()) @@ -896,37 +904,37 @@ struct Decimal( return result fn __int__(self) raises -> Int: - """Returns the integral part of the Decimal as Int. + """Returns the integral part of the Decimal128 as Int. See `to_int()` for more information. """ return self.to_int() fn __str__(self) -> String: - """Returns string representation of the Decimal. + """Returns string representation of the Decimal128. See `to_str()` for more information. """ return self.to_str() fn __repr__(self) -> String: - """Returns a string representation of the Decimal.""" - return 'Decimal("' + self.__str__() + '")' + """Returns a string representation of the Decimal128.""" + return 'Decimal128("' + self.__str__() + '")' # ===------------------------------------------------------------------=== # # Type-transfer or output methods that are not dunders # ===------------------------------------------------------------------=== # fn write_to[W: Writer](self, mut writer: W): - """Writes the Decimal to a writer. + """Writes the Decimal128 to a writer. This implement the `write` method of the `Writer` trait. """ writer.write(String(self)) fn repr_words(self) -> String: - """Returns a string representation of the Decimal's internal words. - `Decimal.from_words(low, mid, high, flags)`. + """Returns a string representation of the Decimal128's internal words. + `Decimal128.from_words(low, mid, high, flags)`. """ return ( - "Decimal(" + "Decimal128(" + hex(self.low) + ", " + hex(self.mid) @@ -938,13 +946,13 @@ struct Decimal( ) fn repr_components(self) -> String: - """Returns a string representation of the Decimal's five components. - `Decimal.from_components(low, mid, high, scale, sign)`. + """Returns a string representation of the Decimal128's five components. + `Decimal128.from_components(low, mid, high, scale, sign)`. """ var scale = UInt8((self.flags & Self.SCALE_MASK) >> Self.SCALE_SHIFT) var sign = Bool((self.flags & Self.SIGN_MASK) == Self.SIGN_MASK) return ( - "Decimal(low=" + "Decimal128(low=" + hex(self.low) + ", mid=" + hex(self.mid) @@ -958,14 +966,14 @@ struct Decimal( ) fn to_int(self) raises -> Int: - """Returns the integral part of the Decimal as Int. - If the Decimal is too large to fit in Int, an error is raised. + """Returns the integral part of the Decimal128 as Int. + If the Decimal128 is too large to fit in Int, an error is raised. Returns: - The signed integral part of the Decimal. + The signed integral part of the Decimal128. Raises: - Error: If the Decimal is too large to fit in Int. + Error: If the Decimal128 is too large to fit in Int. """ try: return Int(self.to_int64()) @@ -973,34 +981,34 @@ struct Decimal( raise Error("Error in `to_int()`: ", e) fn to_int64(self) raises -> Int64: - """Returns the integral part of the Decimal as Int64. - If the Decimal is too large to fit in Int64, an error is raised. + """Returns the integral part of the Decimal128 as Int64. + If the Decimal128 is too large to fit in Int64, an error is raised. Returns: - The signed integral part of the Decimal. + The signed integral part of the Decimal128. Raises: - Error: If the Decimal is too large to fit in Int64. + Error: If the Decimal128 is too large to fit in Int64. """ var result = self.to_int128() if result > Int128(Int64.MAX): - raise Error("Decimal is too large to fit in Int64") + raise Error("Decimal128 is too large to fit in Int64") if result < Int128(Int64.MIN): - raise Error("Decimal is too small to fit in Int64") + raise Error("Decimal128 is too small to fit in Int64") return Int64(result & 0xFFFF_FFFF_FFFF_FFFF) fn to_int128(self) -> Int128: - """Returns the signed integral part of the Decimal.""" + """Returns the signed integral part of the Decimal128.""" var res = Int128(self.to_uint128()) return -res if self.is_negative() else res fn to_uint128(self) -> UInt128: - """Returns the absolute integral part of the Decimal as UInt128.""" + """Returns the absolute integral part of the Decimal128 as UInt128.""" var res: UInt128 if self.is_zero(): @@ -1022,8 +1030,8 @@ struct Decimal( return res fn to_str(self) -> String: - """Returns string representation of the Decimal. - Preserves trailing zeros after decimal point to match the scale. + """Returns string representation of the Decimal128. + Preserves trailing zeros after decimal128 point to match the scale. """ # Get the coefficient as a string (absolute value) var coef = String(self.coefficient()) @@ -1039,17 +1047,17 @@ struct Decimal( # For non-zero values, format according to scale elif scale == 0: - # No decimal places needed + # No decimal128 places needed result = coef elif scale >= len(coef): - # Need leading zeros after decimal point + # Need leading zeros after decimal128 point result = "0." + "0" * (scale - len(coef)) + coef else: - # Insert decimal point at appropriate position + # Insert decimal128 point at appropriate position var insert_pos = len(coef) - scale result = coef[:insert_pos] + "." + coef[insert_pos:] - # Ensure we have exactly 'scale' digits after decimal point + # Ensure we have exactly 'scale' digits after decimal128 point var decimal_point_pos = result.find(".") var current_decimals = len(result) - decimal_point_pos - 1 @@ -1064,10 +1072,10 @@ struct Decimal( return result fn to_str_scientific(self) raises -> String: - """Returns a string representation of this Decimal in scientific notation. + """Returns a string representation of this Decimal128 in scientific notation. Returns: - A string representation of this Decimal in scientific notation. + A string representation of this Decimal128 in scientific notation. Raises: Error: If significant_digits is not between 1 and 28. @@ -1096,12 +1104,12 @@ struct Decimal( # 0.00100: coef=100, scale=5 # => 0.001: coef=1, scale=3, ndigits_fractional_part=0 # => 1.0e-3: coef=1, exponent=-3 - var ndigits_coef = decimojo.utility.number_of_digits(coef) + var ndigits_coef = decimojo.decimal128.utility.number_of_digits(coef) var ndigits_fractional_part = ndigits_coef - 1 var exponent = ndigits_fractional_part - scale # Format in scientific notation: - # sign, first digit, decimal point, remaining digits + # sign, first digit, decimal128 point, remaining digits var coef_str = String(coef) var result: String = String("-") if self.is_negative() else String("") if len(coef_str) == 1: @@ -1135,17 +1143,17 @@ struct Decimal( @always_inline fn __abs__(self) -> Self: - """Returns the absolute value of this Decimal. + """Returns the absolute value of this Decimal128. See `absolute()` for more information. """ - return decimojo.decimal.arithmetics.absolute(self) + return decimojo.decimal128.arithmetics.absolute(self) @always_inline fn __neg__(self) -> Self: - """Returns the negation of this Decimal. + """Returns the negation of this Decimal128. See `negative()` for more information. """ - return decimojo.decimal.arithmetics.negative(self) + return decimojo.decimal128.arithmetics.negative(self) # ===------------------------------------------------------------------=== # # Basic binary arithmetic operation dunders @@ -1155,65 +1163,65 @@ struct Decimal( @always_inline fn __add__(self, other: Self) raises -> Self: - return decimojo.decimal.arithmetics.add(self, other) + return decimojo.decimal128.arithmetics.add(self, other) @always_inline fn __add__(self, other: Int) raises -> Self: - return decimojo.decimal.arithmetics.add(self, Decimal(other)) + return decimojo.decimal128.arithmetics.add(self, Self(other)) @always_inline fn __sub__(self, other: Self) raises -> Self: - return decimojo.decimal.arithmetics.subtract(self, other) + return decimojo.decimal128.arithmetics.subtract(self, other) @always_inline fn __sub__(self, other: Int) raises -> Self: - return decimojo.decimal.arithmetics.subtract(self, Decimal(other)) + return decimojo.decimal128.arithmetics.subtract(self, Self(other)) @always_inline fn __mul__(self, other: Self) raises -> Self: - return decimojo.decimal.arithmetics.multiply(self, other) + return decimojo.decimal128.arithmetics.multiply(self, other) @always_inline fn __mul__(self, other: Int) raises -> Self: - return decimojo.decimal.arithmetics.multiply(self, Decimal(other)) + return decimojo.decimal128.arithmetics.multiply(self, Self(other)) @always_inline fn __truediv__(self, other: Self) raises -> Self: - return decimojo.decimal.arithmetics.divide(self, other) + return decimojo.decimal128.arithmetics.divide(self, other) @always_inline fn __truediv__(self, other: Int) raises -> Self: - return decimojo.decimal.arithmetics.divide(self, Decimal(other)) + return decimojo.decimal128.arithmetics.divide(self, Self(other)) @always_inline fn __floordiv__(self, other: Self) raises -> Self: """Performs truncate division with // operator.""" - return decimojo.decimal.arithmetics.truncate_divide(self, other) + return decimojo.decimal128.arithmetics.truncate_divide(self, other) @always_inline fn __floordiv__(self, other: Int) raises -> Self: """Performs truncate division with // operator.""" - return decimojo.decimal.arithmetics.truncate_divide( - self, Decimal(other) + return decimojo.decimal128.arithmetics.truncate_divide( + self, Self(other) ) @always_inline fn __mod__(self, other: Self) raises -> Self: """Performs truncate modulo.""" - return decimojo.decimal.arithmetics.modulo(self, other) + return decimojo.decimal128.arithmetics.modulo(self, other) @always_inline fn __mod__(self, other: Int) raises -> Self: """Performs truncate modulo.""" - return decimojo.decimal.arithmetics.modulo(self, Decimal(other)) + return decimojo.decimal128.arithmetics.modulo(self, Self(other)) @always_inline fn __pow__(self, exponent: Self) raises -> Self: - return decimal.power(self, exponent) + return decimojo.decimal128.exponential.power(self, exponent) @always_inline fn __pow__(self, exponent: Int) raises -> Self: - return decimal.power(self, exponent) + return decimojo.decimal128.exponential.power(self, exponent) # ===------------------------------------------------------------------=== # # Basic binary arithmetic operation dunders with reflected operands @@ -1224,31 +1232,31 @@ struct Decimal( @always_inline fn __radd__(self, other: Int) raises -> Self: - return decimojo.decimal.arithmetics.add(Decimal(other), self) + return decimojo.decimal128.arithmetics.add(Self(other), self) @always_inline fn __rsub__(self, other: Int) raises -> Self: - return decimojo.decimal.arithmetics.subtract(Decimal(other), self) + return decimojo.decimal128.arithmetics.subtract(Self(other), self) @always_inline fn __rmul__(self, other: Int) raises -> Self: - return decimojo.decimal.arithmetics.multiply(Decimal(other), self) + return decimojo.decimal128.arithmetics.multiply(Self(other), self) @always_inline fn __rtruediv__(self, other: Int) raises -> Self: - return decimojo.decimal.arithmetics.divide(Decimal(other), self) + return decimojo.decimal128.arithmetics.divide(Self(other), self) @always_inline fn __rfloordiv__(self, other: Int) raises -> Self: """Performs truncate division with // operator.""" - return decimojo.decimal.arithmetics.truncate_divide( - Decimal(other), self + return decimojo.decimal128.arithmetics.truncate_divide( + Self(other), self ) @always_inline fn __rmod__(self, other: Int) raises -> Self: """Performs truncate modulo.""" - return decimojo.decimal.arithmetics.modulo(Decimal(other), self) + return decimojo.decimal128.arithmetics.modulo(Self(other), self) # ===------------------------------------------------------------------=== # # Basic binary augmented arithmetic assignments dunders @@ -1259,52 +1267,52 @@ struct Decimal( @always_inline fn __iadd__(mut self, other: Self) raises: - self = decimojo.decimal.arithmetics.add(self, other) + self = decimojo.decimal128.arithmetics.add(self, other) @always_inline fn __iadd__(mut self, other: Int) raises: - self = decimojo.decimal.arithmetics.add(self, Decimal(other)) + self = decimojo.decimal128.arithmetics.add(self, Self(other)) @always_inline fn __isub__(mut self, other: Self) raises: - self = decimojo.decimal.arithmetics.subtract(self, other) + self = decimojo.decimal128.arithmetics.subtract(self, other) @always_inline fn __isub__(mut self, other: Int) raises: - self = decimojo.decimal.arithmetics.subtract(self, Decimal(other)) + self = decimojo.decimal128.arithmetics.subtract(self, Self(other)) @always_inline fn __imul__(mut self, other: Self) raises: - self = decimojo.decimal.arithmetics.multiply(self, other) + self = decimojo.decimal128.arithmetics.multiply(self, other) @always_inline fn __imul__(mut self, other: Int) raises: - self = decimojo.decimal.arithmetics.multiply(self, Decimal(other)) + self = decimojo.decimal128.arithmetics.multiply(self, Self(other)) @always_inline fn __itruediv__(mut self, other: Self) raises: - self = decimojo.decimal.arithmetics.divide(self, other) + self = decimojo.decimal128.arithmetics.divide(self, other) @always_inline fn __itruediv__(mut self, other: Int) raises: - self = decimojo.decimal.arithmetics.divide(self, Decimal(other)) + self = decimojo.decimal128.arithmetics.divide(self, Self(other)) @always_inline fn __ifloordiv__(mut self, other: Self) raises: """Performs truncate division with // operator.""" - self = decimojo.decimal.arithmetics.truncate_divide(self, other) + self = decimojo.decimal128.arithmetics.truncate_divide(self, other) @always_inline fn __ifloordiv__(mut self, other: Int) raises: """Performs truncate division with // operator.""" - self = decimojo.decimal.arithmetics.truncate_divide( - self, Decimal(other) + self = decimojo.decimal128.arithmetics.truncate_divide( + self, Self(other) ) @always_inline fn __imod__(mut self, other: Self) raises: """Performs truncate modulo.""" - self = decimojo.decimal.arithmetics.modulo(self, other) + self = decimojo.decimal128.arithmetics.modulo(self, other) # ===------------------------------------------------------------------=== # # Basic binary comparison operation dunders @@ -1312,46 +1320,46 @@ struct Decimal( # ===------------------------------------------------------------------=== # @always_inline - fn __gt__(self, other: Decimal) -> Bool: + fn __gt__(self, other: Decimal128) -> Bool: """Greater than comparison operator. See `greater()` for more information. """ - return decimojo.decimal.comparison.greater(self, other) + return decimojo.decimal128.comparison.greater(self, other) @always_inline - fn __lt__(self, other: Decimal) -> Bool: + fn __lt__(self, other: Decimal128) -> Bool: """Less than comparison operator. See `less()` for more information. """ - return decimojo.decimal.comparison.less(self, other) + return decimojo.decimal128.comparison.less(self, other) @always_inline - fn __ge__(self, other: Decimal) -> Bool: + fn __ge__(self, other: Decimal128) -> Bool: """Greater than or equal comparison operator. See `greater_equal()` for more information. """ - return decimojo.decimal.comparison.greater_equal(self, other) + return decimojo.decimal128.comparison.greater_equal(self, other) @always_inline - fn __le__(self, other: Decimal) -> Bool: + fn __le__(self, other: Decimal128) -> Bool: """Less than or equal comparison operator. See `less_equal()` for more information. """ - return decimojo.decimal.comparison.less_equal(self, other) + return decimojo.decimal128.comparison.less_equal(self, other) @always_inline - fn __eq__(self, other: Decimal) -> Bool: + fn __eq__(self, other: Decimal128) -> Bool: """Equality comparison operator. See `equal()` for more information. """ - return decimojo.decimal.comparison.equal(self, other) + return decimojo.decimal128.comparison.equal(self, other) @always_inline - fn __ne__(self, other: Decimal) -> Bool: + fn __ne__(self, other: Decimal128) -> Bool: """Inequality comparison operator. See `not_equal()` for more information. """ - return decimojo.decimal.comparison.not_equal(self, other) + return decimojo.decimal128.comparison.not_equal(self, other) # ===------------------------------------------------------------------=== # # Other dunders that implements traits @@ -1360,15 +1368,15 @@ struct Decimal( @always_inline fn __round__(self, ndigits: Int) -> Self: - """Rounds this Decimal to the specified number of decimal places. - If `ndigits` is not given, rounds to 0 decimal places. + """Rounds this Decimal128 to the specified number of decimal128 places. + If `ndigits` is not given, rounds to 0 decimal128 places. If rounding causes overflow, returns the value itself. raises: Error: Calling `round()` failed. """ try: - return decimojo.decimal.rounding.round( + return decimojo.decimal128.rounding.round( self, ndigits=ndigits, rounding_mode=RoundingMode.ROUND_HALF_EVEN, @@ -1380,7 +1388,7 @@ struct Decimal( fn __round__(self) -> Self: """**OVERLOAD**.""" try: - return decimojo.decimal.rounding.round( + return decimojo.decimal128.rounding.round( self, ndigits=0, rounding_mode=RoundingMode.ROUND_HALF_EVEN ) except e: @@ -1397,74 +1405,74 @@ struct Decimal( ndigits: Int = 0, rounding_mode: RoundingMode = RoundingMode.ROUND_HALF_EVEN, ) raises -> Self: - """Rounds this Decimal to the specified number of decimal places. + """Rounds this Decimal128 to the specified number of decimal128 places. Compared to `__round__`, this method: (1) Allows specifying the rounding mode. (2) Raises an error if the operation would result in overflow. See `round()` for more information. """ - return decimojo.decimal.rounding.round( + return decimojo.decimal128.rounding.round( self, ndigits=ndigits, rounding_mode=rounding_mode ) @always_inline fn quantize( self, - exp: Decimal, + exp: Decimal128, rounding_mode: RoundingMode = RoundingMode.ROUND_HALF_EVEN, ) raises -> Self: - """Quantizes this Decimal to the specified exponent. + """Quantizes this Decimal128 to the specified exponent. See `quantize()` for more information. """ - return decimojo.decimal.rounding.quantize(self, exp, rounding_mode) + return decimojo.decimal128.rounding.quantize(self, exp, rounding_mode) @always_inline fn exp(self) raises -> Self: - """Calculates the exponential of this Decimal. + """Calculates the exponential of this Decimal128. See `exp()` for more information. """ - return decimojo.decimal.exponential.exp(self) + return decimojo.decimal128.exponential.exp(self) @always_inline fn ln(self) raises -> Self: - """Calculates the natural logarithm of this Decimal. + """Calculates the natural logarithm of this Decimal128. See `ln()` for more information. """ - return decimojo.decimal.exponential.ln(self) + return decimojo.decimal128.exponential.ln(self) @always_inline - fn log10(self) raises -> Decimal: - """Computes the base-10 logarithm of this Decimal.""" - return decimojo.decimal.exponential.log10(self) + fn log10(self) raises -> Decimal128: + """Computes the base-10 logarithm of this Decimal128.""" + return decimojo.decimal128.exponential.log10(self) @always_inline - fn log(self, base: Decimal) raises -> Decimal: - """Computes the logarithm of this Decimal with an arbitrary base.""" - return decimojo.decimal.exponential.log(self, base) + fn log(self, base: Decimal128) raises -> Decimal128: + """Computes the logarithm of this Decimal128 with an arbitrary base.""" + return decimojo.decimal128.exponential.log(self, base) @always_inline - fn power(self, exponent: Int) raises -> Decimal: - """Raises this Decimal to the power of an integer.""" - return decimojo.decimal.exponential.power(self, Decimal(exponent)) + fn power(self, exponent: Int) raises -> Decimal128: + """Raises this Decimal128 to the power of an integer.""" + return decimojo.decimal128.exponential.power(self, Self(exponent)) @always_inline - fn power(self, exponent: Decimal) raises -> Decimal: - """Raises this Decimal to the power of another Decimal.""" - return decimojo.decimal.exponential.power(self, exponent) + fn power(self, exponent: Decimal128) raises -> Decimal128: + """Raises this Decimal128 to the power of another Decimal128.""" + return decimojo.decimal128.exponential.power(self, exponent) @always_inline fn root(self, n: Int) raises -> Self: - """Calculates the n-th root of this Decimal. + """Calculates the n-th root of this Decimal128. See `root()` for more information. """ - return decimojo.decimal.exponential.root(self, n) + return decimojo.decimal128.exponential.root(self, n) @always_inline fn sqrt(self) raises -> Self: - """Calculates the square root of this Decimal. + """Calculates the square root of this Decimal128. See `sqrt()` for more information. """ - return decimojo.decimal.exponential.sqrt(self) + return decimojo.decimal128.exponential.sqrt(self) # ===------------------------------------------------------------------=== # # Other methods @@ -1473,7 +1481,7 @@ struct Decimal( @always_inline fn coefficient(self) -> UInt128: """Returns the unscaled integer coefficient as an UInt128 value. - This is the absolute value of the decimal digits without considering + This is the absolute value of the decimal128 digits without considering the scale. The value of the coefficient is: `high * 2**64 + mid * 2**32 + low`. @@ -1484,7 +1492,7 @@ struct Decimal( # Fast implementation using bitcast # Use bitcast to directly convert the three 32-bit parts to a UInt128 # UInt128 must little-endian on memory - return decimojo.utility.bitcast[DType.uint128](self) + return decimojo.decimal128.utility.bitcast[DType.uint128](self) # Alternative implementation using arithmetic # Combine the three 32-bit parts into a single Int128 @@ -1494,30 +1502,30 @@ struct Decimal( # | UInt128(self.low) # ) - fn extend_precision(self, owned precision_diff: Int) raises -> Decimal: - """Returns a number with additional decimal places (trailing zeros). + fn extend_precision(self, owned precision_diff: Int) raises -> Decimal128: + """Returns a number with additional decimal128 places (trailing zeros). This multiplies the coefficient by 10^precision_diff and increases the scale accordingly, preserving the numeric value. Args: - precision_diff: The number of decimal places to add. + precision_diff: The number of decimal128 places to add. Returns: - A new Decimal with increased precision. + A new Decimal128 with increased precision. Raises: Error: If the level is less than 0. Examples: ```mojo - from decimojo import Decimal - var d1 = Decimal("5") # 5 + from decimojo import Decimal128 + var d1 = Decimal128("5") # 5 var d2 = d1.extend_precision(2) # Result: 5.00 (same value, different representation) print(d1) # 5 print(d2) # 5.00 print(d2.scale()) # 2 - var d3 = Decimal("123.456") # 123.456 + var d3 = Decimal128("123.456") # 123.456 var d4 = d3.extend_precision(3) # Result: 123.456000 print(d3) # 123.456 print(d4) # 123.456000 @@ -1540,10 +1548,10 @@ struct Decimal( # TODO: Check if multiplication by 10^level would cause overflow # If yes, then raise an error - if new_scale > Decimal.MAX_SCALE + 1: + if new_scale > Decimal128.MAX_SCALE + 1: # Cannot scale beyond max precision, limit the scaling - precision_diff = Decimal.MAX_SCALE + 1 - self.scale() - new_scale = Decimal.MAX_SCALE + 1 + precision_diff = Decimal128.MAX_SCALE + 1 - self.scale() + new_scale = Decimal128.MAX_SCALE + 1 # With UInt128, we can represent the coefficient as a single value var coefficient = ( @@ -1568,17 +1576,17 @@ struct Decimal( result.high = UInt32((coefficient >> 64) & 0xFFFFFFFF) # Set the new scale - result.flags = (self.flags & ~Decimal.SCALE_MASK) | ( - UInt32(new_scale << Decimal.SCALE_SHIFT) & Decimal.SCALE_MASK + result.flags = (self.flags & ~Decimal128.SCALE_MASK) | ( + UInt32(new_scale << Decimal128.SCALE_SHIFT) & Decimal128.SCALE_MASK ) return result fn print_internal_representation(self): - """Prints the internal representation details of a Decimal.""" + """Prints the internal representation details of a Decimal128.""" print("\nInternal Representation Details:") print("--------------------------------") - print("Decimal: ", self) + print("Decimal128: ", self) print("coefficient: ", self.coefficient()) print("scale: ", self.scale()) print("is negative: ", self.is_negative()) @@ -1594,12 +1602,12 @@ struct Decimal( @always_inline fn is_integer(self) -> Bool: - """Determines whether this Decimal value represents an integer. - A Decimal represents an integer when it has no fractional part - (i.e., all digits after the decimal point are zero). + """Determines whether this Decimal128 value represents an integer. + A Decimal128 represents an integer when it has no fractional part + (i.e., all digits after the decimal128 point are zero). Returns: - True if this Decimal represents an integer value, False otherwise. + True if this Decimal128 represents an integer value, False otherwise. """ # If value is zero, it's an integer regardless of scale @@ -1613,21 +1621,21 @@ struct Decimal( return True # For a value to be an integer, it must be divisible by 10^scale - # If coefficient % 10^scale == 0, then all decimal places are zeros + # If coefficient % 10^scale == 0, then all decimal128 places are zeros # If it divides evenly, it's an integer return ( self.coefficient() - % decimojo.utility.power_of_10[DType.uint128](scale) + % decimojo.decimal128.utility.power_of_10[DType.uint128](scale) ) == 0 @always_inline fn is_negative(self) -> Bool: - """Returns True if this Decimal is negative.""" + """Returns True if this Decimal128 is negative.""" return (self.flags & Self.SIGN_MASK) != 0 @always_inline fn is_one(self) -> Bool: - """Returns True if this Decimal represents the value 1. + """Returns True if this Decimal128 represents the value 1. If 10^scale == coefficient, then it's one. `1` and `1.00` are considered ones. """ @@ -1640,50 +1648,53 @@ struct Decimal( if scale == 0 and coef == 1: return True - if coef == decimojo.utility.power_of_10[DType.uint128](scale): + if coef == decimojo.decimal128.utility.power_of_10[DType.uint128]( + scale + ): return True return False @always_inline fn is_zero(self) -> Bool: - """Returns True if this Decimal represents zero. - A decimal is zero when all coefficient parts (low, mid, high) are zero, + """Returns True if this Decimal128 represents zero. + A decimal128 is zero when all coefficient parts (low, mid, high) are zero, regardless of its sign or scale. """ return self.low == 0 and self.mid == 0 and self.high == 0 @always_inline fn is_infinity(self) -> Bool: - """Returns True if this Decimal is positive or negative infinity.""" + """Returns True if this Decimal128 is positive or negative infinity.""" return (self.flags & Self.INFINITY_MASK) != 0 @always_inline fn is_nan(self) -> Bool: - """Returns True if this Decimal is NaN (Not a Number).""" + """Returns True if this Decimal128 is NaN (Not a Number).""" return (self.flags & Self.NAN_MASK) != 0 @always_inline fn scale(self) -> Int: - """Returns the scale (number of decimal places) of this Decimal.""" + """Returns the scale (number of decimal128 places) of this Decimal128. + """ return Int((self.flags & Self.SCALE_MASK) >> Self.SCALE_SHIFT) @always_inline fn number_of_significant_digits(self) -> Int: - """Returns the number of significant digits in the Decimal. + """Returns the number of significant digits in the Decimal128. The number of significant digits is the total number of digits in the coefficient, excluding leading zeros but including trailing zeros. Returns: - The number of significant digits in the Decimal. + The number of significant digits in the Decimal128. Example: ```mojo - from decimojo import Decimal - print(Decimal("123.4500").number_of_significant_digits()) + from decimojo import Decimal128 + print(Decimal128("123.4500").number_of_significant_digits()) # 7 - print(Decimal("0.0001234500").number_of_significant_digits()) + print(Decimal128("0.0001234500").number_of_significant_digits()) # 7 ``` End of example. @@ -1695,4 +1706,4 @@ struct Decimal( if coef == 0: return 0 # Zero has zero significant digit else: - return decimojo.utility.number_of_digits(coef) + return decimojo.decimal128.utility.number_of_digits(coef) diff --git a/src/decimojo/decimal/exponential.mojo b/src/decimojo/decimal128/exponential.mojo similarity index 66% rename from src/decimojo/decimal/exponential.mojo rename to src/decimojo/decimal128/exponential.mojo index 2ed8abf..dd50cd6 100644 --- a/src/decimojo/decimal/exponential.mojo +++ b/src/decimojo/decimal128/exponential.mojo @@ -14,40 +14,40 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # -"""Implements exponential functions for the Decimal type.""" +"""Implements exponential functions for the Decimal128 type.""" import math as builtin_math import testing import time -import decimojo.decimal.constants -import decimojo.decimal.special -import decimojo.utility +import decimojo.decimal128.constants +import decimojo.decimal128.special +import decimojo.decimal128.utility # ===----------------------------------------------------------------------=== # # Power and root functions # ===----------------------------------------------------------------------=== # -fn power(base: Decimal, exponent: Decimal) raises -> Decimal: - """Raises a Decimal base to an arbitrary Decimal exponent power. +fn power(base: Decimal128, exponent: Decimal128) raises -> Decimal128: + """Raises a Decimal128 base to an arbitrary Decimal128 exponent power. This function handles both integer and non-integer exponents using the identity x^y = e^(y * ln(x)). Args: - base: The base Decimal value (must be positive). - exponent: The exponent Decimal value (can be any value). + base: The base Decimal128 value (must be positive). + exponent: The exponent Decimal128 value (can be any value). Returns: - A new Decimal containing the result of base^exponent. + A new Decimal128 containing the result of base^exponent. Raises: Error: If the base is negative or the exponent is negative and not an integer. Error: If an error occurs in calling the power() function with an integer exponent. - Error: If an error occurs in calling the sqrt() function with a Decimal exponent. - Error: If an error occurs in calling the ln() function with a Decimal base. - Error: If an error occurs in calling the exp() function with a Decimal exponent. + Error: If an error occurs in calling the sqrt() function with a Decimal128 exponent. + Error: If an error occurs in calling the ln() function with a Decimal128 base. + Error: If an error occurs in calling the exp() function with a Decimal128 exponent. """ # CASE: If the exponent is integer @@ -55,7 +55,7 @@ fn power(base: Decimal, exponent: Decimal) raises -> Decimal: try: return power(base, Int(exponent)) except e: - raise Error("Error in `power()` with Decimal exponent: ", e) + raise Error("Error in `power()` with Decimal128 exponent: ", e) # CASE: For negative bases, only integer exponents are supported if base.is_negative(): @@ -66,17 +66,17 @@ fn power(base: Decimal, exponent: Decimal) raises -> Decimal: # CASE: If the exponent is simple fractions # 0.5 - if exponent == decimojo.decimal.constants.M0D5(): + if exponent == decimojo.decimal128.constants.M0D5(): try: return sqrt(base) except e: - raise Error("Error in `power()` with Decimal exponent: ", e) + raise Error("Error in `power()` with Decimal128 exponent: ", e) # -0.5 - if exponent == Decimal(5, 0, 0, 0x80010000): + if exponent == Decimal128(5, 0, 0, 0x80010000): try: - return Decimal.ONE() / sqrt(base) + return Decimal128.ONE() / sqrt(base) except e: - raise Error("Error in `power()` with Decimal exponent: ", e) + raise Error("Error in `power()` with Decimal128 exponent: ", e) # GENERAL CASE # Use the identity x^y = e^(y * ln(x)) @@ -85,24 +85,24 @@ fn power(base: Decimal, exponent: Decimal) raises -> Decimal: var product = exponent * ln_base return exp(product) except e: - raise Error("Error in `power()` with Decimal exponent: ", e) + raise Error("Error in `power()` with Decimal128 exponent: ", e) -fn power(base: Decimal, exponent: Int) raises -> Decimal: - """Raises a Decimal base to an integer power. +fn power(base: Decimal128, exponent: Int) raises -> Decimal128: + """Raises a Decimal128 base to an integer power. Args: base: The base value. exponent: The integer power to raise base to. Returns: - A new Decimal containing the result. + A new Decimal128 containing the result. """ # Special cases if exponent == 0: # x^0 = 1 (including 0^0 = 1 by convention) - return Decimal.ONE() + return Decimal128.ONE() if exponent == 1: # x^1 = x @@ -111,14 +111,14 @@ fn power(base: Decimal, exponent: Int) raises -> Decimal: if base.is_zero(): # 0^n = 0 for n > 0 if exponent > 0: - return Decimal.ZERO() + return Decimal128.ZERO() else: # 0^n is undefined for n < 0 raise Error("Zero cannot be raised to a negative power") if base.coefficient() == 1 and base.scale() == 0: # 1^n = 1 for any n - return Decimal.ONE() + return Decimal128.ONE() # Handle negative exponents: x^(-n) = 1/(x^n) var negative_exponent = exponent < 0 @@ -127,7 +127,7 @@ fn power(base: Decimal, exponent: Int) raises -> Decimal: abs_exp = -exponent # Binary exponentiation for efficiency - var result = Decimal.ONE() + var result = Decimal128.ONE() var current_base = base while abs_exp > 0: @@ -142,20 +142,20 @@ fn power(base: Decimal, exponent: Int) raises -> Decimal: # For negative exponents, take the reciprocal if negative_exponent: # For 1/x, use division - result = Decimal.ONE() / result + result = Decimal128.ONE() / result return result -fn root(x: Decimal, n: Int) raises -> Decimal: - """Calculates the n-th root of a Decimal value using Newton-Raphson method. +fn root(x: Decimal128, n: Int) raises -> Decimal128: + """Calculates the n-th root of a Decimal128 value using Newton-Raphson method. Args: - x: The Decimal value to compute the n-th root of. + x: The Decimal128 value to compute the n-th root of. n: The root to compute (must be positive). Returns: - A new Decimal containing the n-th root of x. + A new Decimal128 containing the n-th root of x. Raises: Error: If x is negative and n is even. @@ -173,9 +173,9 @@ fn root(x: Decimal, n: Int) raises -> Decimal: # Special cases for x if x.is_zero(): - return Decimal.ZERO() + return Decimal128.ZERO() if x.is_one(): - return Decimal.ONE() + return Decimal128.ONE() if x.is_negative(): if n % 2 == 0: raise Error( @@ -191,7 +191,7 @@ fn root(x: Decimal, n: Int) raises -> Decimal: # Use logarithm approach directly with higher precision try: # Direct calculation: x^n = e^(ln(x)/n) - return exp(ln(x) / Decimal(n)) + return exp(ln(x) / Decimal128(n)) except e: raise Error("Error in `root()`: ", e) @@ -199,7 +199,7 @@ fn root(x: Decimal, n: Int) raises -> Decimal: # use floating point approach to quickly find a good guess var x_coef: UInt128 = x.coefficient() var x_scale = x.scale() - var guess: Decimal + var guess: Decimal128 # For numbers with zero scale (true integers) if x_scale == 0: @@ -207,21 +207,21 @@ fn root(x: Decimal, n: Int) raises -> Decimal: var float_root = ( pow(Float64(x_coef), 1 / Float64(n)) * Float64(10) ** 8 ) - guess = Decimal.from_uint128( + guess = Decimal128.from_uint128( UInt128(round(float_root)), scale=8, sign=False ) elif n <= 16: var float_root = ( pow(Float64(x_coef), 1 / Float64(n)) * Float64(10) ** 16 ) - guess = Decimal.from_uint128( + guess = Decimal128.from_uint128( UInt128(round(float_root)), scale=16, sign=False ) else: var float_root = ( pow(Float64(x_coef), 1 / Float64(n)) * Float64(10) ** 26 ) - guess = Decimal.from_uint128( + guess = Decimal128.from_uint128( UInt128(round(float_root)), scale=26, sign=False ) @@ -240,7 +240,7 @@ fn root(x: Decimal, n: Int) raises -> Decimal: var float_root = Float64(x_coef) ** (Float64(1) / Float64(n)) / Float64( 10 ) ** (Float64(remainder) / Float64(n) - 1) - guess = Decimal.from_uint128( + guess = Decimal128.from_uint128( UInt128(float_root), scale=dividend + 1, sign=False ) @@ -248,10 +248,10 @@ fn root(x: Decimal, n: Int) raises -> Decimal: # Newton-Raphson method for n-th root # Formula: x_{k+1} = ((n-1)*x_k + a/x_k^(n-1))/n - var prev_guess = Decimal.ZERO() - var n_decimal = Decimal(n) + var prev_guess = Decimal128.ZERO() + var n_decimal = Decimal128(n) var n_minus_1 = n - 1 - var n_minus_1_decimal = Decimal(n_minus_1) + var n_minus_1_decimal = Decimal128(n_minus_1) var iteration_count = 0 # Newton-Raphson iteration @@ -272,10 +272,12 @@ fn root(x: Decimal, n: Int) raises -> Decimal: # No need to do this if the last digit of the coefficient of guess is not zero if guess_coef % 10 == 0: - var num_digits_x_ceof = decimojo.utility.number_of_digits(x_coef) + var num_digits_x_ceof = decimojo.decimal128.utility.number_of_digits( + x_coef + ) var num_digits_x_root_coef = (num_digits_x_ceof // n) + 1 - var num_digits_guess_coef = decimojo.utility.number_of_digits( - guess_coef + var num_digits_guess_coef = ( + decimojo.decimal128.utility.number_of_digits(guess_coef) ) var num_digits_to_decrease = ( num_digits_guess_coef - num_digits_x_root_coef @@ -293,15 +295,17 @@ fn root(x: Decimal, n: Int) raises -> Decimal: else: var guess_coef_powered = guess_coef**n if guess_coef_powered == x_coef: - return Decimal.from_uint128( + return Decimal128.from_uint128( guess_coef, scale=guess.scale() - num_digits_to_decrease, sign=False, ) - if guess_coef_powered == x_coef * decimojo.utility.power_of_10[ - DType.uint128 - ](n): - return Decimal.from_uint128( + if ( + guess_coef_powered + == x_coef + * decimojo.decimal128.utility.power_of_10[DType.uint128](n) + ): + return Decimal128.from_uint128( guess_coef // 10, scale=guess.scale() - num_digits_to_decrease - 1, sign=False, @@ -316,14 +320,14 @@ fn root(x: Decimal, n: Int) raises -> Decimal: return guess -fn sqrt(x: Decimal) raises -> Decimal: - """Computes the square root of a Decimal value using Newton-Raphson method. +fn sqrt(x: Decimal128) raises -> Decimal128: + """Computes the square root of a Decimal128 value using Newton-Raphson method. Args: - x: The Decimal value to compute the square root of. + x: The Decimal128 value to compute the square root of. Returns: - A new Decimal containing the square root of x. + A new Decimal128 containing the square root of x. Raises: Error: If x is negative. @@ -335,23 +339,23 @@ fn sqrt(x: Decimal) raises -> Decimal: ) if x.is_zero(): - return Decimal.ZERO() + return Decimal128.ZERO() # Initial guess # use floating point approach to quickly find a good guess var x_coef: UInt128 = x.coefficient() var x_scale = x.scale() - var guess: Decimal + var guess: Decimal128 # For numbers with zero scale (true integers) if x_scale == 0: var float_sqrt = builtin_math.sqrt(Float64(x_coef)) - guess = Decimal.from_uint128(UInt128(round(float_sqrt))) + guess = Decimal128.from_uint128(UInt128(round(float_sqrt))) # For numbers with even scale elif x_scale % 2 == 0: var float_sqrt = builtin_math.sqrt(Float64(x_coef)) - guess = Decimal.from_uint128( + guess = Decimal128.from_uint128( UInt128(float_sqrt), scale=x_scale >> 1, sign=False ) # print("DEBUG: scale is even") @@ -359,7 +363,7 @@ fn sqrt(x: Decimal) raises -> Decimal: # For numbers with odd scale else: var float_sqrt = builtin_math.sqrt(Float64(x_coef)) * Float64(3.15625) - guess = Decimal.from_uint128( + guess = Decimal128.from_uint128( UInt128(float_sqrt), scale=(x_scale + 1) >> 1, sign=False ) # print("DEBUG: scale is odd") @@ -369,7 +373,7 @@ fn sqrt(x: Decimal) raises -> Decimal: # Newton-Raphson iterations # x_n+1 = (x_n + S/x_n) / 2 - var prev_guess = Decimal.ZERO() + var prev_guess = Decimal128.ZERO() var iteration_count = 0 # Iterate until guess converges or max iterations reached @@ -379,7 +383,7 @@ fn sqrt(x: Decimal) raises -> Decimal: prev_guess = guess var division_result = x / guess var sum_result = guess + division_result - guess = sum_result / Decimal(2, 0, 0, 0, False) + guess = sum_result / Decimal128(2, 0, 0, 0, False) iteration_count += 1 # print("------------------------------------------------------") @@ -399,10 +403,12 @@ fn sqrt(x: Decimal) raises -> Decimal: # No need to do this if the last digit of the coefficient of guess is not zero if guess_coef % 10 == 0: - var num_digits_x_ceof = decimojo.utility.number_of_digits(x_coef) + var num_digits_x_ceof = decimojo.decimal128.utility.number_of_digits( + x_coef + ) var num_digits_x_sqrt_coef = (num_digits_x_ceof >> 1) + 1 - var num_digits_guess_coef = decimojo.utility.number_of_digits( - guess_coef + var num_digits_guess_coef = ( + decimojo.decimal128.utility.number_of_digits(guess_coef) ) var num_digits_to_decrease = ( num_digits_guess_coef - num_digits_x_sqrt_coef @@ -424,7 +430,7 @@ fn sqrt(x: Decimal) raises -> Decimal: if (guess_coef_squared == x_coef) or ( guess_coef_squared == x_coef * 10 ): - return Decimal.from_uint128( + return Decimal128.from_uint128( guess_coef, scale=guess.scale() - num_digits_to_decrease, sign=False, @@ -438,15 +444,15 @@ fn sqrt(x: Decimal) raises -> Decimal: # ===----------------------------------------------------------------------=== # -fn exp(x: Decimal) raises -> Decimal: - """Calculates e^x for any Decimal value using optimized range reduction. +fn exp(x: Decimal128) raises -> Decimal128: + """Calculates e^x for any Decimal128 value using optimized range reduction. x should be no greater than 66 to avoid overflow. Args: x: The exponent. Returns: - A Decimal approximation of e^x. + A Decimal128 approximation of e^x. Raises: Error: If x is greater than 66.54. @@ -456,7 +462,7 @@ fn exp(x: Decimal) raises -> Decimal: the x value should be no greater than 66 to avoid overflow. """ - if x > Decimal.from_int(value=6654, scale=UInt32(2)): + if x > Decimal128.from_int(value=6654, scale=UInt32(2)): raise Error( "decimal.exponential.exp(): x is too large. It must be no greater" " than 66.54 to avoid overflow. Consider using `BigDecimal` type." @@ -464,10 +470,10 @@ fn exp(x: Decimal) raises -> Decimal: # Handle special cases if x.is_zero(): - return Decimal.ONE() + return Decimal128.ONE() if x.is_negative(): - return Decimal.ONE() / exp(-x) + return Decimal128.ONE() / exp(-x) # For x < 1, use Taylor series expansion # For x > 1, use optimized range reduction with smaller chunks @@ -487,97 +493,97 @@ fn exp(x: Decimal) raises -> Decimal: # The fractional part is then calculated using the series expansion. # Because the fractional part is <1, the series converges quickly. - var exp_chunk: Decimal - var remainder: Decimal + var exp_chunk: Decimal128 + var remainder: Decimal128 var num_chunks: Int = 1 var x_int = Int(x) if x.is_one(): - return decimojo.decimal.constants.E() + return decimojo.decimal128.constants.E() elif x_int < 1: - var M0D5 = decimojo.decimal.constants.M0D5() - var M0D25 = decimojo.decimal.constants.M0D25() + var M0D5 = decimojo.decimal128.constants.M0D5() + var M0D25 = decimojo.decimal128.constants.M0D25() if x < M0D25: # 0 < x < 0.25 return exp_series(x) elif x < M0D5: # 0.25 <= x < 0.5 - exp_chunk = decimojo.decimal.constants.E0D25() + exp_chunk = decimojo.decimal128.constants.E0D25() remainder = x - M0D25 else: # 0.5 <= x < 1 - exp_chunk = decimojo.decimal.constants.E0D5() + exp_chunk = decimojo.decimal128.constants.E0D5() remainder = x - M0D5 elif x_int == 1: # 1 <= x < 2, chunk = 1 - exp_chunk = decimojo.decimal.constants.E() + exp_chunk = decimojo.decimal128.constants.E() remainder = x - x_int elif x_int == 2: # 2 <= x < 3, chunk = 2 - exp_chunk = decimojo.decimal.constants.E2() + exp_chunk = decimojo.decimal128.constants.E2() remainder = x - x_int elif x_int == 3: # 3 <= x < 4, chunk = 3 - exp_chunk = decimojo.decimal.constants.E3() + exp_chunk = decimojo.decimal128.constants.E3() remainder = x - x_int elif x_int == 4: # 4 <= x < 5, chunk = 4 - exp_chunk = decimojo.decimal.constants.E4() + exp_chunk = decimojo.decimal128.constants.E4() remainder = x - x_int elif x_int == 5: # 5 <= x < 6, chunk = 5 - exp_chunk = decimojo.decimal.constants.E5() + exp_chunk = decimojo.decimal128.constants.E5() remainder = x - x_int elif x_int == 6: # 6 <= x < 7, chunk = 6 - exp_chunk = decimojo.decimal.constants.E6() + exp_chunk = decimojo.decimal128.constants.E6() remainder = x - x_int elif x_int == 7: # 7 <= x < 8, chunk = 7 - exp_chunk = decimojo.decimal.constants.E7() + exp_chunk = decimojo.decimal128.constants.E7() remainder = x - x_int elif x_int == 8: # 8 <= x < 9, chunk = 8 - exp_chunk = decimojo.decimal.constants.E8() + exp_chunk = decimojo.decimal128.constants.E8() remainder = x - x_int elif x_int == 9: # 9 <= x < 10, chunk = 9 - exp_chunk = decimojo.decimal.constants.E9() + exp_chunk = decimojo.decimal128.constants.E9() remainder = x - x_int elif x_int == 10: # 10 <= x < 11, chunk = 10 - exp_chunk = decimojo.decimal.constants.E10() + exp_chunk = decimojo.decimal128.constants.E10() remainder = x - x_int elif x_int == 11: # 11 <= x < 12, chunk = 11 - exp_chunk = decimojo.decimal.constants.E11() + exp_chunk = decimojo.decimal128.constants.E11() remainder = x - x_int elif x_int == 12: # 12 <= x < 13, chunk = 12 - exp_chunk = decimojo.decimal.constants.E12() + exp_chunk = decimojo.decimal128.constants.E12() remainder = x - x_int elif x_int == 13: # 13 <= x < 14, chunk = 13 - exp_chunk = decimojo.decimal.constants.E13() + exp_chunk = decimojo.decimal128.constants.E13() remainder = x - x_int elif x_int == 14: # 14 <= x < 15, chunk = 14 - exp_chunk = decimojo.decimal.constants.E14() + exp_chunk = decimojo.decimal128.constants.E14() remainder = x - x_int elif x_int == 15: # 15 <= x < 16, chunk = 15 - exp_chunk = decimojo.decimal.constants.E15() + exp_chunk = decimojo.decimal128.constants.E15() remainder = x - x_int elif x_int < 32: # 16 <= x < 32, chunk = 16 num_chunks = x_int >> 4 - exp_chunk = decimojo.decimal.constants.E16() + exp_chunk = decimojo.decimal128.constants.E16() remainder = x - (num_chunks << 4) else: # chunk = 32 num_chunks = x_int >> 5 - exp_chunk = decimojo.decimal.constants.E32() + exp_chunk = decimojo.decimal128.constants.E32() remainder = x - (num_chunks << 5) # Calculate e^(chunk * num_chunks) = (e^chunk)^num_chunks @@ -591,7 +597,7 @@ fn exp(x: Decimal) raises -> Decimal: return exp_main * exp_remainder -fn exp_series(x: Decimal) raises -> Decimal: +fn exp_series(x: Decimal128) raises -> Decimal128: """Calculates e^x using Taylor series expansion. Do not use this function for values larger than 1, but `exp()` instead. @@ -599,7 +605,7 @@ fn exp_series(x: Decimal) raises -> Decimal: x: The exponent. Returns: - A Decimal approximation of e^x. + A Decimal128 approximation of e^x. Notes: @@ -612,16 +618,16 @@ fn exp_series(x: Decimal) raises -> Decimal: # For x=0, e^0 = 1 if x.is_zero(): - return Decimal.ONE() + return Decimal128.ONE() # For x with very small magnitude, just use 1+x approximation - if abs(x) == Decimal(1, 0, 0, 28 << 16): - return Decimal.ONE() + x + if abs(x) == Decimal128(1, 0, 0, 28 << 16): + return Decimal128.ONE() + x # Initialize result and term - var result = Decimal.ONE() - var term = Decimal.ONE() - var term_add_on: Decimal + var result = Decimal128.ONE() + var term = Decimal128.ONE() + var term_add_on: Decimal128 # Calculate terms iteratively # term[x] = x^i / i! @@ -629,7 +635,7 @@ fn exp_series(x: Decimal) raises -> Decimal: # => term[x] / term[x-1] = x / i for i in range(1, max_terms + 1): - term_add_on = x / Decimal(i) + term_add_on = x / Decimal128(i) term = term * term_add_on # Check for convergence @@ -646,14 +652,14 @@ fn exp_series(x: Decimal) raises -> Decimal: # ===----------------------------------------------------------------------=== # -fn ln(x: Decimal) raises -> Decimal: - """Calculates the natural logarithm (ln) of a Decimal value. +fn ln(x: Decimal128) raises -> Decimal128: + """Calculates the natural logarithm (ln) of a Decimal128 value. Args: - x: The Decimal value to compute the natural logarithm of. + x: The Decimal128 value to compute the natural logarithm of. Returns: - A Decimal approximation of ln(x). + A Decimal128 approximation of ln(x). Raises: Error: If x is less than or equal to zero. @@ -671,170 +677,170 @@ fn ln(x: Decimal) raises -> Decimal: ) if x.is_one(): - return Decimal.ZERO() + return Decimal128.ZERO() # Special cases for common values - if x == decimojo.decimal.constants.E(): - return Decimal.ONE() + if x == decimojo.decimal128.constants.E(): + return Decimal128.ONE() # For values close to 1, use series expansion directly - if Decimal(95, 0, 0, 2 << 16) <= x <= Decimal(105, 0, 0, 2 << 16): - return ln_series(x - Decimal.ONE()) + if Decimal128(95, 0, 0, 2 << 16) <= x <= Decimal128(105, 0, 0, 2 << 16): + return ln_series(x - Decimal128.ONE()) # For all other values, use range reduction # ln(x) = ln(m * 2^p * 10^q) = ln(m) + p*ln(2) + q*ln(10), where 1 <= m < 2 - var m: Decimal = x + var m: Decimal128 = x var p: Int = 0 var q: Int = 0 # Step 1: handle powers of 10 for large values - if x >= decimojo.decimal.constants.M10(): + if x >= decimojo.decimal128.constants.M10(): # Repeatedly divide by 10 until m < 10 - while m >= decimojo.decimal.constants.M10(): - m = m / decimojo.decimal.constants.M10() + while m >= decimojo.decimal128.constants.M10(): + m = m / decimojo.decimal128.constants.M10() q += 1 - elif x < Decimal(1, 0, 0, 1 << 16): + elif x < Decimal128(1, 0, 0, 1 << 16): # Repeatedly multiply by 10 until m >= 0.1 - while m < Decimal(1, 0, 0, 1 << 16): - m = m * decimojo.decimal.constants.M10() + while m < Decimal128(1, 0, 0, 1 << 16): + m = m * decimojo.decimal128.constants.M10() q -= 1 # Now 0.1 <= m < 10 # Step 2: normalize to [0.5, 2) using powers of 2 - if m >= decimojo.decimal.constants.M2(): + if m >= decimojo.decimal128.constants.M2(): # Repeatedly divide by 2 until m < 2 - while m >= decimojo.decimal.constants.M2(): - m = m / decimojo.decimal.constants.M2() + while m >= decimojo.decimal128.constants.M2(): + m = m / decimojo.decimal128.constants.M2() p += 1 - elif m < Decimal(5, 0, 0, 1 << 16): + elif m < Decimal128(5, 0, 0, 1 << 16): # Repeatedly multiply by 2 until m >= 0.5 - while m < Decimal(5, 0, 0, 1 << 16): - m = m * decimojo.decimal.constants.M2() + while m < Decimal128(5, 0, 0, 1 << 16): + m = m * decimojo.decimal128.constants.M2() p -= 1 # Now 0.5 <= m < 2 - var ln_m: Decimal + var ln_m: Decimal128 # Use precomputed values and series expansion for accuracy and performance - if m < Decimal.ONE(): + if m < Decimal128.ONE(): # For 0.5 <= m < 1 - if m >= Decimal(9, 0, 0, 1 << 16): + if m >= Decimal128(9, 0, 0, 1 << 16): ln_m = ( ln_series( - (m - Decimal(9, 0, 0, 1 << 16)) - * decimojo.decimal.constants.INV0D9() + (m - Decimal128(9, 0, 0, 1 << 16)) + * decimojo.decimal128.constants.INV0D9() ) - + decimojo.decimal.constants.LN0D9() + + decimojo.decimal128.constants.LN0D9() ) - elif m >= Decimal(8, 0, 0, 1 << 16): + elif m >= Decimal128(8, 0, 0, 1 << 16): ln_m = ( ln_series( - (m - Decimal(8, 0, 0, 1 << 16)) - * decimojo.decimal.constants.INV0D8() + (m - Decimal128(8, 0, 0, 1 << 16)) + * decimojo.decimal128.constants.INV0D8() ) - + decimojo.decimal.constants.LN0D8() + + decimojo.decimal128.constants.LN0D8() ) - elif m >= Decimal(7, 0, 0, 1 << 16): + elif m >= Decimal128(7, 0, 0, 1 << 16): ln_m = ( ln_series( - (m - Decimal(7, 0, 0, 1 << 16)) - * decimojo.decimal.constants.INV0D7() + (m - Decimal128(7, 0, 0, 1 << 16)) + * decimojo.decimal128.constants.INV0D7() ) - + decimojo.decimal.constants.LN0D7() + + decimojo.decimal128.constants.LN0D7() ) - elif m >= Decimal(6, 0, 0, 1 << 16): + elif m >= Decimal128(6, 0, 0, 1 << 16): ln_m = ( ln_series( - (m - Decimal(6, 0, 0, 1 << 16)) - * decimojo.decimal.constants.INV0D6() + (m - Decimal128(6, 0, 0, 1 << 16)) + * decimojo.decimal128.constants.INV0D6() ) - + decimojo.decimal.constants.LN0D6() + + decimojo.decimal128.constants.LN0D6() ) else: # 0.5 <= m < 0.6 ln_m = ( ln_series( - (m - Decimal(5, 0, 0, 1 << 16)) - * decimojo.decimal.constants.INV0D5() + (m - Decimal128(5, 0, 0, 1 << 16)) + * decimojo.decimal128.constants.INV0D5() ) - + decimojo.decimal.constants.LN0D5() + + decimojo.decimal128.constants.LN0D5() ) else: # For 1 < m < 2 - if m < Decimal(11, 0, 0, 1 << 16): # 1 < m < 1.1 - ln_m = ln_series(m - Decimal.ONE()) - elif m < Decimal(12, 0, 0, 1 << 16): # 1.1 <= m < 1.2 + if m < Decimal128(11, 0, 0, 1 << 16): # 1 < m < 1.1 + ln_m = ln_series(m - Decimal128.ONE()) + elif m < Decimal128(12, 0, 0, 1 << 16): # 1.1 <= m < 1.2 ln_m = ( ln_series( - (m - Decimal(11, 0, 0, 1 << 16)) - * decimojo.decimal.constants.INV1D1() + (m - Decimal128(11, 0, 0, 1 << 16)) + * decimojo.decimal128.constants.INV1D1() ) - + decimojo.decimal.constants.LN1D1() + + decimojo.decimal128.constants.LN1D1() ) - elif m < Decimal(13, 0, 0, 1 << 16): # 1.2 <= m < 1.3 + elif m < Decimal128(13, 0, 0, 1 << 16): # 1.2 <= m < 1.3 ln_m = ( ln_series( - (m - Decimal(12, 0, 0, 1 << 16)) - * decimojo.decimal.constants.INV1D2() + (m - Decimal128(12, 0, 0, 1 << 16)) + * decimojo.decimal128.constants.INV1D2() ) - + decimojo.decimal.constants.LN1D2() + + decimojo.decimal128.constants.LN1D2() ) - elif m < Decimal(14, 0, 0, 1 << 16): # 1.3 <= m < 1.4 + elif m < Decimal128(14, 0, 0, 1 << 16): # 1.3 <= m < 1.4 ln_m = ( ln_series( - (m - Decimal(13, 0, 0, 1 << 16)) - * decimojo.decimal.constants.INV1D3() + (m - Decimal128(13, 0, 0, 1 << 16)) + * decimojo.decimal128.constants.INV1D3() ) - + decimojo.decimal.constants.LN1D3() + + decimojo.decimal128.constants.LN1D3() ) - elif m < Decimal(15, 0, 0, 1 << 16): # 1.4 <= m < 1.5 + elif m < Decimal128(15, 0, 0, 1 << 16): # 1.4 <= m < 1.5 ln_m = ( ln_series( - (m - Decimal(14, 0, 0, 1 << 16)) - * decimojo.decimal.constants.INV1D4() + (m - Decimal128(14, 0, 0, 1 << 16)) + * decimojo.decimal128.constants.INV1D4() ) - + decimojo.decimal.constants.LN1D4() + + decimojo.decimal128.constants.LN1D4() ) - elif m < Decimal(16, 0, 0, 1 << 16): # 1.5 <= m < 1.6 + elif m < Decimal128(16, 0, 0, 1 << 16): # 1.5 <= m < 1.6 ln_m = ( ln_series( - (m - Decimal(15, 0, 0, 1 << 16)) - * decimojo.decimal.constants.INV1D5() + (m - Decimal128(15, 0, 0, 1 << 16)) + * decimojo.decimal128.constants.INV1D5() ) - + decimojo.decimal.constants.LN1D5() + + decimojo.decimal128.constants.LN1D5() ) - elif m < Decimal(17, 0, 0, 1 << 16): # 1.6 <= m < 1.7 + elif m < Decimal128(17, 0, 0, 1 << 16): # 1.6 <= m < 1.7 ln_m = ( ln_series( - (m - Decimal(16, 0, 0, 1 << 16)) - * decimojo.decimal.constants.INV1D6() + (m - Decimal128(16, 0, 0, 1 << 16)) + * decimojo.decimal128.constants.INV1D6() ) - + decimojo.decimal.constants.LN1D6() + + decimojo.decimal128.constants.LN1D6() ) - elif m < Decimal(18, 0, 0, 1 << 16): # 1.7 <= m < 1.8 + elif m < Decimal128(18, 0, 0, 1 << 16): # 1.7 <= m < 1.8 ln_m = ( ln_series( - (m - Decimal(17, 0, 0, 1 << 16)) - * decimojo.decimal.constants.INV1D7() + (m - Decimal128(17, 0, 0, 1 << 16)) + * decimojo.decimal128.constants.INV1D7() ) - + decimojo.decimal.constants.LN1D7() + + decimojo.decimal128.constants.LN1D7() ) - elif m < Decimal(19, 0, 0, 1 << 16): # 1.8 <= m < 1.9 + elif m < Decimal128(19, 0, 0, 1 << 16): # 1.8 <= m < 1.9 ln_m = ( ln_series( - (m - Decimal(18, 0, 0, 1 << 16)) - * decimojo.decimal.constants.INV1D8() + (m - Decimal128(18, 0, 0, 1 << 16)) + * decimojo.decimal128.constants.INV1D8() ) - + decimojo.decimal.constants.LN1D8() + + decimojo.decimal128.constants.LN1D8() ) else: # 1.9 <= m < 2 ln_m = ( ln_series( - (m - Decimal(19, 0, 0, 1 << 16)) - * decimojo.decimal.constants.INV1D9() + (m - Decimal128(19, 0, 0, 1 << 16)) + * decimojo.decimal128.constants.INV1D9() ) - + decimojo.decimal.constants.LN1D9() + + decimojo.decimal128.constants.LN1D9() ) # Combine result: ln(x) = ln(m) + p*ln(2) + q*ln(10) @@ -842,16 +848,16 @@ fn ln(x: Decimal) raises -> Decimal: # Add power of 2 contribution if p != 0: - result = result + Decimal(p) * decimojo.decimal.constants.LN2() + result = result + Decimal128(p) * decimojo.decimal128.constants.LN2() # Add power of 10 contribution if q != 0: - result = result + Decimal(q) * decimojo.decimal.constants.LN10() + result = result + Decimal128(q) * decimojo.decimal128.constants.LN10() return result -fn ln_series(z: Decimal) raises -> Decimal: +fn ln_series(z: Decimal128) raises -> Decimal128: """Calculates ln(1+z) using Taylor series expansion at 1. For best accuracy, |z| should be small (< 0.5). @@ -859,7 +865,7 @@ fn ln_series(z: Decimal) raises -> Decimal: z: The value to compute ln(1+z) for. Returns: - A Decimal approximation of ln(1+z). + A Decimal128 approximation of ln(1+z). Notes: Uses the series: ln(1+z) = z - z²/2 + z³/3 - z⁴/4 + ... @@ -872,14 +878,14 @@ fn ln_series(z: Decimal) raises -> Decimal: # For z=0, ln(1+z) = ln(1) = 0 if z.is_zero(): - return Decimal.ZERO() + return Decimal128.ZERO() # For z with very small magnitude, just use z approximation - if abs(z) == Decimal(1, 0, 0, 28 << 16): + if abs(z) == Decimal128(1, 0, 0, 28 << 16): return z # Initialize result and term - var result = Decimal.ZERO() + var result = Decimal128.ZERO() var term = z var neg: Bool = False @@ -895,9 +901,9 @@ fn ln_series(z: Decimal) raises -> Decimal: neg = not neg # Alternate sign if i <= 20: - term = term * z * decimojo.decimal.constants.N_DIVIDE_NEXT(i) + term = term * z * decimojo.decimal128.constants.N_DIVIDE_NEXT(i) else: - term = term * z * Decimal(i) / Decimal(i + 1) + term = term * z * Decimal128(i) / Decimal128(i + 1) # Check for convergence if term.is_zero(): @@ -909,15 +915,15 @@ fn ln_series(z: Decimal) raises -> Decimal: return result -fn log(x: Decimal, base: Decimal) raises -> Decimal: - """Calculates the logarithm of a Decimal with respect to an arbitrary base. +fn log(x: Decimal128, base: Decimal128) raises -> Decimal128: + """Calculates the logarithm of a Decimal128 with respect to an arbitrary base. Args: - x: The Decimal value to compute the logarithm of. + x: The Decimal128 value to compute the logarithm of. base: The base of the logarithm (must be positive and not equal to 1). Returns: - A Decimal approximation of log_base(x). + A Decimal128 approximation of log_base(x). Raises: Error: If x is less than or equal to zero. @@ -946,15 +952,15 @@ fn log(x: Decimal, base: Decimal) raises -> Decimal: # Special case: x = 1 # log_base(1) = 0 for any valid base if x.is_one(): - return Decimal.ZERO() + return Decimal128.ZERO() # Special case: x = base # log_base(base) = 1 for any valid base if x == base: - return Decimal.ONE() + return Decimal128.ONE() # Special case: base = 10 - if base == Decimal(10, 0, 0, 0): + if base == Decimal128(10, 0, 0, 0): return log10(x) # Use the identity: log_base(x) = ln(x) / ln(base) @@ -964,14 +970,14 @@ fn log(x: Decimal, base: Decimal) raises -> Decimal: return ln_x / ln_base -fn log10(x: Decimal) raises -> Decimal: - """Calculates the base-10 logarithm (log10) of a Decimal value. +fn log10(x: Decimal128) raises -> Decimal128: + """Calculates the base-10 logarithm (log10) of a Decimal128 value. Args: - x: The Decimal value to compute the base-10 logarithm of. + x: The Decimal128 value to compute the base-10 logarithm of. Returns: - A Decimal approximation of log10(x). + A Decimal128 approximation of log10(x). Raises: Error: If x is less than or equal to zero. @@ -993,17 +999,17 @@ fn log10(x: Decimal) raises -> Decimal: if x_coef == 1: # Special case: x = 1 if x_scale == 0: - return Decimal.ZERO() + return Decimal128.ZERO() else: - return Decimal(x_scale, 0, 0, 0x8000_0000) + return Decimal128(x_scale, 0, 0, 0x8000_0000) - var ten_to_power_of_scale = decimojo.utility.power_of_10[DType.uint128]( - x_scale - ) + var ten_to_power_of_scale = decimojo.decimal128.utility.power_of_10[ + DType.uint128 + ](x_scale) # Special case: x = 1.00...0 if x_coef == ten_to_power_of_scale: - return Decimal.ZERO() + return Decimal128.ZERO() # Special case: x = 10^n # First get the integral part of x @@ -1014,9 +1020,9 @@ fn log10(x: Decimal) raises -> Decimal: integeral_part //= 10 exponent += 1 if integeral_part == 1: - return Decimal(exponent, 0, 0, 0) + return Decimal128(exponent, 0, 0, 0) else: pass # Use the identity: log10(x) = ln(x) / ln(10) - return ln(x) / decimojo.decimal.constants.LN10() + return ln(x) / decimojo.decimal128.constants.LN10() diff --git a/src/decimojo/decimal/rounding.mojo b/src/decimojo/decimal128/rounding.mojo similarity index 70% rename from src/decimojo/decimal/rounding.mojo rename to src/decimojo/decimal128/rounding.mojo index e5dbe22..e45a8f2 100644 --- a/src/decimojo/decimal/rounding.mojo +++ b/src/decimojo/decimal128/rounding.mojo @@ -14,26 +14,26 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # -# Implements basic object methods for the Decimal type +# Implements basic object methods for the Decimal128 type # which supports correctly-rounded, fixed-point arithmetic. # # ===----------------------------------------------------------------------=== # # # List of functions in this module: # -# round(x: Decimal, places: Int, mode: RoundingMode): Rounds x to specified decimal places +# round(x: Decimal128, places: Int, mode: RoundingMode): Rounds x to specified decimal places # # ===----------------------------------------------------------------------=== # """ -Implements functions for mathematical operations on Decimal objects. +Implements functions for mathematical operations on Decimal128 objects. """ import testing -from decimojo.decimal.decimal import Decimal +from decimojo.decimal128.decimal128 import Decimal128 from decimojo.rounding_mode import RoundingMode -import decimojo.utility +import decimojo.decimal128.utility # ===------------------------------------------------------------------------===# # Rounding @@ -41,22 +41,22 @@ import decimojo.utility fn round( - number: Decimal, + number: Decimal128, ndigits: Int = 0, rounding_mode: RoundingMode = RoundingMode.ROUND_HALF_EVEN, -) raises -> Decimal: +) raises -> Decimal128: """ - Rounds the Decimal to the specified number of decimal places. + Rounds the Decimal128 to the specified number of decimal places. Args: - number: The Decimal to round. + number: The Decimal128 to round. ndigits: Number of decimal places to round to. Defaults to 0. rounding_mode: Rounding mode to use. Defaults to ROUND_HALF_EVEN (banker's rounding). Returns: - A new Decimal rounded to the specified number of decimal places. + A new Decimal128 rounded to the specified number of decimal places. """ # Number of decimal places of the number is equal to the scale of the number @@ -68,12 +68,12 @@ fn round( # Return a copy directly # 情况一:如果已经在所需的标度上, 直接返回其副本 # - # round(Decimal("123.456"), 3) -> Decimal("123.456") + # round(Decimal128("123.456"), 3) -> Decimal128("123.456") if scale_diff == 0: return number var x_coef = number.coefficient() - var ndigits_of_x = decimojo.utility.number_of_digits(x_coef) + var ndigits_of_x = decimojo.decimal128.utility.number_of_digits(x_coef) # CASE: If ndigits is larger than the current scale # Scale up the coefficient of the number to the desired scale @@ -81,12 +81,12 @@ fn round( # 情况二:如果ndigits大于当前标度, 将係數放大 # # Examples: - # round(Decimal("123.456"), 5) -> Decimal("123.45600") - # round(Decimal("123.456"), 29) -> Error + # round(Decimal128("123.456"), 5) -> Decimal128("123.45600") + # round(Decimal128("123.456"), 29) -> Error if scale_diff > 0: # If the digits of result > 29, directly raise an error - if ndigits_of_x + scale_diff > Decimal.MAX_NUM_DIGITS: + if ndigits_of_x + scale_diff > Decimal128.MAX_NUM_DIGITS: raise Error( String( "Error in `round()`: `ndigits = {}` causes the number of" @@ -95,7 +95,7 @@ fn round( ).format( ndigits, ndigits_of_x + scale_diff, - Decimal.MAX_NUM_DIGITS, + Decimal128.MAX_NUM_DIGITS, ) ) @@ -104,20 +104,20 @@ fn round( var res_coef = x_coef * UInt128(10) ** scale_diff # If the digits of result == 29, but the result >= 2^96, raise an error - if (ndigits_of_x + scale_diff == Decimal.MAX_NUM_DIGITS) and ( - res_coef > Decimal.MAX_AS_UINT128 + if (ndigits_of_x + scale_diff == Decimal128.MAX_NUM_DIGITS) and ( + res_coef > Decimal128.MAX_AS_UINT128 ): raise Error( String( "Error in `round()`: `ndigits = {}` causes the" " significant digits of the result (={}) exceeds the" " maximum capacity (={})." - ).format(ndigits, res_coef, Decimal.MAX_AS_UINT128) + ).format(ndigits, res_coef, Decimal128.MAX_AS_UINT128) ) # In other cases, return the result else: - return Decimal.from_uint128( + return Decimal128.from_uint128( res_coef, scale=ndigits, sign=number.is_negative() ) @@ -128,10 +128,10 @@ fn round( # If `ndigits` is negative, the result need to be scaled up again. # # Examples: - # round(Decimal("987.654321"), 3) -> Decimal("987.654") - # round(Decimal("987.654321"), -2) -> Decimal("1000") - # round(Decimal("987.654321"), -3) -> Decimal("1000") - # round(Decimal("987.654321"), -4) -> Decimal("0") + # round(Decimal128("987.654321"), 3) -> Decimal128("987.654") + # round(Decimal128("987.654321"), -2) -> Decimal128("1000") + # round(Decimal128("987.654321"), -3) -> Decimal128("1000") + # round(Decimal128("987.654321"), -4) -> Decimal128("0") else: # scale_diff < 0 @@ -139,45 +139,45 @@ fn round( var ndigits_to_keep = ndigits_of_x + scale_diff # Keep the first `ndigits_to_keep` digits with specified rounding mode - var res_coef = decimojo.utility.round_to_keep_first_n_digits( + var res_coef = decimojo.decimal128.utility.round_to_keep_first_n_digits( x_coef, ndigits=ndigits_to_keep, rounding_mode=rounding_mode ) if ndigits >= 0: - return Decimal.from_uint128( + return Decimal128.from_uint128( res_coef, scale=ndigits, sign=number.is_negative() ) # if `ndigits` is negative and `ndigits_to_keep` >= 0, scale up the result elif ndigits_to_keep >= 0: res_coef *= UInt128(10) ** (-ndigits) - return Decimal.from_uint128( + return Decimal128.from_uint128( res_coef, scale=0, sign=number.is_negative() ) # if `ndigits` is negative and `ndigits_to_keep` < 0, return 0 else: - return Decimal.ZERO() + return Decimal128.ZERO() fn quantize( - value: Decimal, - exp: Decimal, + value: Decimal128, + exp: Decimal128, rounding_mode: RoundingMode = RoundingMode.ROUND_HALF_EVEN, -) raises -> Decimal: +) raises -> Decimal128: """Rounds the value according to the exponent of the second operand. Unlike `round()`, the scale is determined by the scale of the second operand, not a number of digits. `quantize()` returns the same value as `round()` when the scale of the second operand is non-negative. Args: - value: The Decimal value to quantize. - exp: A Decimal whose scale (exponent) will be used for the result. + value: The Decimal128 value to quantize. + exp: A Decimal128 whose scale (exponent) will be used for the result. rounding_mode: The rounding mode to use. Defaults to ROUND_HALF_EVEN (banker's rounding). Returns: - A new Decimal with the same value as the first operand (except for + A new Decimal128 with the same value as the first operand (except for rounding) and the same scale (exponent) as the second operand. Raises: @@ -186,14 +186,14 @@ fn quantize( Examples: ```mojo - from decimojo import Decimal - _ = Decimal("1.2345").quantize(Decimal("0.001")) # -> Decimal("1.234") - _ = Decimal("1.2345").quantize(Decimal("0.01")) # -> Decimal("1.23") - _ = Decimal("1.2345").quantize(Decimal("0.1")) # -> Decimal("1.2") - _ = Decimal("1.2345").quantize(Decimal("1")) # -> Decimal("1") - _ = Decimal("1.2345").quantize(Decimal("10")) # -> Decimal("1") + from decimojo import Decimal128 + _ = Decimal128("1.2345").quantize(Decimal128("0.001")) # -> Decimal128("1.234") + _ = Decimal128("1.2345").quantize(Decimal128("0.01")) # -> Decimal128("1.23") + _ = Decimal128("1.2345").quantize(Decimal128("0.1")) # -> Decimal128("1.2") + _ = Decimal128("1.2345").quantize(Decimal128("1")) # -> Decimal128("1") + _ = Decimal128("1.2345").quantize(Decimal128("10")) # -> Decimal128("1") # Compare with round() - _ = Decimal("1.2345").round(-1) # -> Decimal("0") + _ = Decimal128("1.2345").round(-1) # -> Decimal128("0") ``` End of examples. """ diff --git a/src/decimojo/decimal128/special.mojo b/src/decimojo/decimal128/special.mojo new file mode 100644 index 0000000..b0d4a6a --- /dev/null +++ b/src/decimojo/decimal128/special.mojo @@ -0,0 +1,280 @@ +# ===----------------------------------------------------------------------=== # +# Copyright 2025 Yuhao Zhu +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# +# Implements special functions for the Decimal128 type +# +# ===----------------------------------------------------------------------=== # + +"""Implements functions for special operations on Decimal128 objects.""" + + +fn factorial(n: Int) raises -> Decimal128: + """Calculates the factorial of a non-negative integer. + + Args: + n: The non-negative integer to calculate the factorial of. + + Returns: + The factorial of n. + + Notes: + + 27! is the largest factorial that can be represented by Decimal128. + An error will be raised if n is greater than 27. + """ + + if n < 0: + raise Error("Factorial is not defined for negative numbers") + + if n > 27: + raise Error( + String("{}! is too large to be represented by Decimal128").format(n) + ) + + # Directly return the factorial for n = 0 to 27 + if n == 0 or n == 1: + return Decimal128.from_words(1, 0, 0, 0) # 1 + elif n == 2: + return Decimal128.from_words(2, 0, 0, 0) # 2 + elif n == 3: + return Decimal128.from_words(6, 0, 0, 0) # 6 + elif n == 4: + return Decimal128.from_words(24, 0, 0, 0) # 24 + elif n == 5: + return Decimal128.from_words(120, 0, 0, 0) # 120 + elif n == 6: + return Decimal128.from_words(720, 0, 0, 0) # 720 + elif n == 7: + return Decimal128.from_words(5040, 0, 0, 0) # 5040 + elif n == 8: + return Decimal128.from_words(40320, 0, 0, 0) # 40320 + elif n == 9: + return Decimal128.from_words(362880, 0, 0, 0) # 362880 + elif n == 10: + return Decimal128.from_words(3628800, 0, 0, 0) # 3628800 + elif n == 11: + return Decimal128.from_words(39916800, 0, 0, 0) # 39916800 + elif n == 12: + return Decimal128.from_words(479001600, 0, 0, 0) # 479001600 + elif n == 13: + return Decimal128.from_words(1932053504, 1, 0, 0) # 6227020800 + elif n == 14: + return Decimal128.from_words(1278945280, 20, 0, 0) # 87178291200 + elif n == 15: + return Decimal128.from_words(2004310016, 304, 0, 0) # 1307674368000 + elif n == 16: + return Decimal128.from_words(2004189184, 4871, 0, 0) # 20922789888000 + elif n == 17: + return Decimal128.from_words(4006445056, 82814, 0, 0) # 355687428096000 + elif n == 18: + return Decimal128.from_words( + 3396534272, 1490668, 0, 0 + ) # 6402373705728000 + elif n == 19: + return Decimal128.from_words( + 109641728, 28322707, 0, 0 + ) # 121645100408832000 + elif n == 20: + return Decimal128.from_words( + 2192834560, 566454140, 0, 0 + ) # 2432902008176640000 + elif n == 21: + return Decimal128.from_words( + 3099852800, 3305602358, 2, 0 + ) # 51090942171709440000 + elif n == 22: + return Decimal128.from_words( + 3772252160, 4003775155, 60, 0 + ) # 1124000727777607680000 + elif n == 23: + return Decimal128.from_words( + 862453760, 1892515369, 1401, 0 + ) # 25852016738884976640000 + elif n == 24: + return Decimal128.from_words( + 3519021056, 2470695900, 33634, 0 + ) # 620448401733239439360000 + elif n == 25: + return Decimal128.from_words( + 2076180480, 1637855376, 840864, 0 + ) # 15511210043330985984000000 + elif n == 26: + return Decimal128.from_words( + 2441084928, 3929534124, 21862473, 0 + ) # 403291461126605650322784000 + else: + return Decimal128.from_words( + 1484783616, 3018206259, 590286795, 0 + ) # 10888869450418352160768000000 + + +fn factorial_reciprocal(n: Int) raises -> Decimal128: + """Calculates the reciprocal of factorial of a non-negative integer (1/n!). + + Args: + n: The non-negative integer to calculate the reciprocal factorial of. + + Returns: + The reciprocal of factorial of n (1/n!). + + Notes: + This function is optimized for Taylor series calculations. + The function uses pre-computed values for speed. + For n > 27, the result is effectively 0 at Decimal128 precision. + """ + + # 1/0! = 1, Decimal128.from_words(0x1, 0x0, 0x0, 0x0) + # 1/1! = 1, Decimal128.from_words(0x1, 0x0, 0x0, 0x0) + # 1/2! = 0.5, Decimal128.from_words(0x5, 0x0, 0x0, 0x10000) + # 1/3! = 0.1666666666666666666666666667, Decimal128.from_words(0x82aaaaab, 0xa5b8065, 0x562a265, 0x1c0000) + # 1/4! = 0.0416666666666666666666666667, Decimal128.from_words(0x60aaaaab, 0x4296e019, 0x158a899, 0x1c0000) + # 1/5! = 0.0083333333333333333333333333, Decimal128.from_words(0x13555555, 0xd516005, 0x44ee85, 0x1c0000) + # 1/6! = 0.0013888888888888888888888889, Decimal128.from_words(0x2de38e39, 0x2ce2e556, 0xb7d16, 0x1c0000) + # 1/7! = 0.0001984126984126984126984127, Decimal128.from_words(0xe1fbefbf, 0xbd44fc30, 0x1a427, 0x1c0000) + # 1/8! = 0.0000248015873015873015873016, Decimal128.from_words(0x1c3f7df8, 0xf7a89f86, 0x3484, 0x1c0000) + # 1/9! = 0.0000027557319223985890652557, Decimal128.from_words(0xca3ff18d, 0xe2a0f547, 0x5d5, 0x1c0000) + # 1/10! = 0.0000002755731922398589065256, Decimal128.from_words(0x94399828, 0x63767eed, 0x95, 0x1c0000) + # 1/11! = 0.0000000250521083854417187751, Decimal128.from_words(0xb06253a7, 0x94adae72, 0xd, 0x1c0000) + # 1/12! = 0.0000000020876756987868098979, Decimal128.from_words(0xe40831a3, 0x21b923de, 0x1, 0x1c0000) + # 1/13! = 0.0000000001605904383682161460, Decimal128.from_words(0x4c9e2b34, 0x16495187, 0x0, 0x1c0000) + # 1/14! = 0.0000000000114707455977297247, Decimal128.from_words(0xce9d955f, 0x19785d2, 0x0, 0x1c0000) + # 1/15! = 0.0000000000007647163731819816, Decimal128.from_words(0xdc63d28, 0x1b2b0e, 0x0, 0x1c0000) + # 1/16! = 0.0000000000000477947733238739, Decimal128.from_words(0xe0dc63d3, 0x1b2b0, 0x0, 0x1c0000) + # 1/17! = 0.0000000000000028114572543455, Decimal128.from_words(0xef1c05df, 0x1991, 0x0, 0x1c0000) + # 1/18! = 0.0000000000000001561920696859, Decimal128.from_words(0xa9ba721b, 0x16b, 0x0, 0x1c0000) + # 1/19! = 0.0000000000000000082206352466, Decimal128.from_words(0x23e16452, 0x13, 0x0, 0x1c0000) + # 1/20! = 0.0000000000000000004110317623, Decimal128.from_words(0xf4fe7837, 0x0, 0x0, 0x1c0000) + # 1/21! = 0.0000000000000000000195729411, Decimal128.from_words(0xbaa9803, 0x0, 0x0, 0x1c0000) + # 1/22! = 0.0000000000000000000008896791, Decimal128.from_words(0x87c117, 0x0, 0x0, 0x1c0000) + # 1/23! = 0.0000000000000000000000386817, Decimal128.from_words(0x5e701, 0x0, 0x0, 0x1c0000) + # 1/24! = 0.0000000000000000000000016117, Decimal128.from_words(0x3ef5, 0x0, 0x0, 0x1c0000) + # 1/25! = 0.0000000000000000000000000645, Decimal128.from_words(0x285, 0x0, 0x0, 0x1c0000) + # 1/26! = 0.0000000000000000000000000025, Decimal128.from_words(0x19, 0x0, 0x0, 0x1c0000) + # 1/27! = 0.0000000000000000000000000001, Decimal128.from_words(0x1, 0x0, 0x0, 0x1c0000) + + if n < 0: + raise Error("Factorial reciprocal is not defined for negative numbers") + + # For n > 27, 1/n! is essentially 0 at Decimal128 precision + # Return 0 with max scale + if n > 27: + return Decimal128.from_words(0, 0, 0, 0x001C0000) + + # Directly return pre-computed reciprocal factorials + if n == 0 or n == 1: + return Decimal128.from_words(0x1, 0x0, 0x0, 0x0) # 1 + elif n == 2: + return Decimal128.from_words(0x5, 0x0, 0x0, 0x10000) # 0.5 + elif n == 3: + return Decimal128.from_words( + 0x82AAAAAB, 0xA5B8065, 0x562A265, 0x1C0000 + ) # 0.1666... + elif n == 4: + return Decimal128.from_words( + 0x60AAAAAB, 0x4296E019, 0x158A899, 0x1C0000 + ) # 0.0416... + elif n == 5: + return Decimal128.from_words( + 0x13555555, 0xD516005, 0x44EE85, 0x1C0000 + ) # 0.0083... + elif n == 6: + return Decimal128.from_words( + 0x2DE38E39, 0x2CE2E556, 0xB7D16, 0x1C0000 + ) # 0.0013... + elif n == 7: + return Decimal128.from_words( + 0xE1FBEFBF, 0xBD44FC30, 0x1A427, 0x1C0000 + ) # 0.0001... + elif n == 8: + return Decimal128.from_words( + 0x1C3F7DF8, 0xF7A89F86, 0x3484, 0x1C0000 + ) # 0.0000248... + elif n == 9: + return Decimal128.from_words( + 0xCA3FF18D, 0xE2A0F547, 0x5D5, 0x1C0000 + ) # 0.0000027... + elif n == 10: + return Decimal128.from_words( + 0x94399828, 0x63767EED, 0x95, 0x1C0000 + ) # 0.00000027... + elif n == 11: + return Decimal128.from_words( + 0xB06253A7, 0x94ADAE72, 0xD, 0x1C0000 + ) # 0.000000025... + elif n == 12: + return Decimal128.from_words( + 0xE40831A3, 0x21B923DE, 0x1, 0x1C0000 + ) # 0.0000000020... + elif n == 13: + return Decimal128.from_words( + 0x4C9E2B34, 0x16495187, 0x0, 0x1C0000 + ) # 0.0000000001... + elif n == 14: + return Decimal128.from_words( + 0xCE9D955F, 0x19785D2, 0x0, 0x1C0000 + ) # 0.00000000001... + elif n == 15: + return Decimal128.from_words( + 0xDC63D28, 0x1B2B0E, 0x0, 0x1C0000 + ) # 0.0000000000007... + elif n == 16: + return Decimal128.from_words( + 0xE0DC63D3, 0x1B2B0, 0x0, 0x1C0000 + ) # 0.00000000000004... + elif n == 17: + return Decimal128.from_words( + 0xEF1C05DF, 0x1991, 0x0, 0x1C0000 + ) # 0.000000000000002... + elif n == 18: + return Decimal128.from_words( + 0xA9BA721B, 0x16B, 0x0, 0x1C0000 + ) # 0.0000000000000001... + elif n == 19: + return Decimal128.from_words( + 0x23E16452, 0x13, 0x0, 0x1C0000 + ) # 0.0000000000000000082... + elif n == 20: + return Decimal128.from_words( + 0xF4FE7837, 0x0, 0x0, 0x1C0000 + ) # 0.0000000000000000004... + elif n == 21: + return Decimal128.from_words( + 0xBAA9803, 0x0, 0x0, 0x1C0000 + ) # 0.0000000000000000000195... + elif n == 22: + return Decimal128.from_words( + 0x87C117, 0x0, 0x0, 0x1C0000 + ) # 0.0000000000000000000008... + elif n == 23: + return Decimal128.from_words( + 0x5E701, 0x0, 0x0, 0x1C0000 + ) # 0.0000000000000000000000386... + elif n == 24: + return Decimal128.from_words( + 0x3EF5, 0x0, 0x0, 0x1C0000 + ) # 0.0000000000000000000000016... + elif n == 25: + return Decimal128.from_words( + 0x285, 0x0, 0x0, 0x1C0000 + ) # 0.0000000000000000000000000645 + elif n == 26: + return Decimal128.from_words( + 0x19, 0x0, 0x0, 0x1C0000 + ) # 0.0000000000000000000000000025 + else: # n == 27 + return Decimal128.from_words( + 0x1, 0x0, 0x0, 0x1C0000 + ) # 0.0000000000000000000000000001 diff --git a/src/decimojo/utility.mojo b/src/decimojo/decimal128/utility.mojo similarity index 96% rename from src/decimojo/utility.mojo rename to src/decimojo/decimal128/utility.mojo index 2946394..73d246a 100644 --- a/src/decimojo/utility.mojo +++ b/src/decimojo/decimal128/utility.mojo @@ -14,7 +14,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # -# Implements internal utility functions for the Decimal type +# Implements internal utility functions for the Decimal128 type # WARNING: These functions are not meant to be used directly by the user. # # ===----------------------------------------------------------------------=== # @@ -23,13 +23,13 @@ from memory import UnsafePointer import sys import time -from decimojo.decimal.decimal import Decimal +from decimojo.decimal128.decimal128 import Decimal128 # UNSAFE -fn bitcast[dtype: DType](dec: Decimal) -> Scalar[dtype]: +fn bitcast[dtype: DType](dec: Decimal128) -> Scalar[dtype]: """ - Direct memory bit copy from Decimal (low, mid, high) to Mojo's Scalar type. + Direct memory bit copy from Decimal128 (low, mid, high) to Mojo's Scalar type. This performs a bitcast/reinterpretation rather than bit manipulation. ***UNSAFE***: This function is unsafe and should be used with caution. @@ -37,13 +37,13 @@ fn bitcast[dtype: DType](dec: Decimal) -> Scalar[dtype]: dtype: The Mojo scalar type to bitcast to. Args: - dec: The Decimal to bitcast. + dec: The Decimal128 to bitcast. Constraints: `dtype` must be `DType.uint128` or `DType.uint256`. Returns: - The bitcasted Decimal (low, mid, high) as a Mojo scalar. + The bitcasted Decimal128 (low, mid, high) as a Mojo scalar. """ @@ -53,7 +53,7 @@ fn bitcast[dtype: DType](dec: Decimal) -> Scalar[dtype]: "must be uint128 or uint256", ]() - # Bitcast the Decimal to the desired Mojo scalar type + # Bitcast the Decimal128 to the desired Mojo scalar type var result = UnsafePointer(to=dec).bitcast[Scalar[dtype]]().load() # Mask out the bits in flags result &= Scalar[dtype](0xFFFFFFFF_FFFFFFFF_FFFFFFFF) @@ -63,7 +63,7 @@ fn bitcast[dtype: DType](dec: Decimal) -> Scalar[dtype]: fn truncate_to_max[dtype: DType, //](value: Scalar[dtype]) -> Scalar[dtype]: """ Truncates a UInt256 or UInt128 value to be as closer to the max value of - Decimal coefficient (`2^96 - 1`) as possible with rounding. + Decimal128 coefficient (`2^96 - 1`) as possible with rounding. Uses banker's rounding (ROUND_HALF_EVEN) for any truncated digits. `792281625142643375935439503356` will be truncated to `7922816251426433759354395034`. @@ -91,20 +91,20 @@ fn truncate_to_max[dtype: DType, //](value: Scalar[dtype]) -> Scalar[dtype]: ]() # If the value is already less than the maximum possible value, return it - if value <= ValueType(Decimal.MAX_AS_UINT128): + if value <= ValueType(Decimal128.MAX_AS_UINT128): return value else: # Calculate how many digits we need to truncate # Calculate how many digits to keep (MAX_NUM_DIGITS = 29) var ndigits = number_of_digits(value) - var digits_to_remove = ndigits - Decimal.MAX_NUM_DIGITS + var digits_to_remove = ndigits - Decimal128.MAX_NUM_DIGITS # Collect digits for rounding decision var divisor = power_of_10[dtype](digits_to_remove) var truncated_value = value // divisor - if truncated_value == ValueType(Decimal.MAX_AS_UINT128): + if truncated_value == ValueType(Decimal128.MAX_AS_UINT128): # Case 1: # Truncated_value == MAX_AS_UINT128 # Rounding may not cause overflow depending on rounding digit @@ -154,7 +154,7 @@ fn truncate_to_max[dtype: DType, //](value: Scalar[dtype]) -> Scalar[dtype]: # Trucated_value < MAX_AS_UINT128 # Rounding will not case overflow - if truncated_value > ValueType(Decimal.MAX_AS_UINT128): + if truncated_value > ValueType(Decimal128.MAX_AS_UINT128): digits_to_remove += 1 # Collect digits for rounding decision @@ -263,7 +263,7 @@ fn round_to_keep_first_n_digits[ - When you want to apply a scale of 31 to the coefficient `997`, it will be `0.0000000000000000000000000000997` with 31 digits. However, we can only - store 28 digits in the coefficient (Decimal.MAX_SCALE = 28). + store 28 digits in the coefficient (Decimal128.MAX_SCALE = 28). Therefore, we need to truncate the coefficient to 0 (`3 - (31 - 28)`) digits and round it to the nearest even number. The truncated ceofficient will be `1`. @@ -273,7 +273,7 @@ fn round_to_keep_first_n_digits[ - When you want to apply a scale of 29 to the coefficient `234567`, it will be `0.00000000000000000000000234567` with 29 digits. However, we can only - store 28 digits in the coefficient (Decimal.MAX_SCALE = 28). + store 28 digits in the coefficient (Decimal128.MAX_SCALE = 28). Therefore, we need to truncate the coefficient to 5 (`6 - (29 - 28)`) digits and round it to the nearest even number. The truncated coefficient will be `23457`. @@ -528,7 +528,7 @@ fn number_of_digits[dtype: DType, //](value: Scalar[dtype]) -> Int: return 57 return 58 - # Digits more than 58 is not possible for Decimal products + # Digits more than 58 is not possible for Decimal128 products return 59 @@ -662,7 +662,7 @@ fn power_of_10[dtype: DType](n: Int) -> Scalar[dtype]: Notes: The powers of 10 is hard-coded up to 10^56 since it is twice the maximum - scale of Decimal (28). For larger values, the function calculates the + scale of Decimal128 (28). For larger values, the function calculates the power of 10 using the built-in `**` operator. """ diff --git a/src/decimojo/errors.mojo b/src/decimojo/errors.mojo new file mode 100644 index 0000000..4513a7b --- /dev/null +++ b/src/decimojo/errors.mojo @@ -0,0 +1,57 @@ +# ===----------------------------------------------------------------------=== # +# Copyright 2025 Yuhao Zhu +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + +""" +Implement error handling for DeciMojo. +""" + + +struct DeciError: + var error_type: String + var message: String + var function: String + var file: String + var line: Int + + fn __init__( + out self, + error_type: String, + message: String, + function: String, + file: String, + line: Int, + ): + self.error_type = error_type + self.message = message + self.function = function + self.file = file + self.line = line + + fn __str__(self) -> String: + return ( + "Traceback (most recent call last):\n" + + ' File "' + + String(self.file) + + '", line ' + + String(self.line) + + " in " + + String(self.function) + + "\n" + + String(self.error_type) + + ": " + + String(self.message) + + '"' + ) diff --git a/src/decimojo/prelude.mojo b/src/decimojo/prelude.mojo index c01eefd..a287ddd 100644 --- a/src/decimojo/prelude.mojo +++ b/src/decimojo/prelude.mojo @@ -26,8 +26,8 @@ from decimojo.prelude import * """ import decimojo as dm -from decimojo.decimal.decimal import Decimal, Dec -from decimojo.bigdecimal.bigdecimal import BigDecimal, BDec, bdec +from decimojo.decimal128.decimal128 import Decimal128, Dec128 +from decimojo.bigdecimal.bigdecimal import BigDecimal, BDec, Decimal from decimojo.biguint.biguint import BigUInt, BUInt from decimojo.bigint.bigint import BigInt, BInt from decimojo.rounding_mode import ( diff --git a/tests/decimal/test_decimal_arithmetics.mojo b/tests/decimal128/test_decimal128_arithmetics.mojo similarity index 74% rename from tests/decimal/test_decimal_arithmetics.mojo rename to tests/decimal128/test_decimal128_arithmetics.mojo index c43937a..3d23d17 100644 --- a/tests/decimal/test_decimal_arithmetics.mojo +++ b/tests/decimal128/test_decimal128_arithmetics.mojo @@ -1,8 +1,8 @@ """ -Test Decimal arithmetic operations including addition, subtraction, and negation. +Test Decimal128 arithmetic operations including addition, subtraction, and negation. """ -from decimojo.prelude import dm, Decimal, RoundingMode +from decimojo.prelude import dm, Decimal128, RoundingMode import testing @@ -11,60 +11,60 @@ fn test_add() raises: print("Testing decimal addition...") # Test case 1: Simple addition with same scale - var a1 = Decimal(12345, scale=2) # 123.45 - var b1 = Decimal(6789, scale=2) # 67.89 + var a1 = Decimal128(12345, scale=2) # 123.45 + var b1 = Decimal128(6789, scale=2) # 67.89 var result1 = a1 + b1 testing.assert_equal( String(result1), "191.34", "Simple addition with same scale" ) # Test case 2: Addition with different scales - var a2 = Decimal(1234, scale=1) - var b2 = Decimal(6789, scale=2) + var a2 = Decimal128(1234, scale=1) + var b2 = Decimal128(6789, scale=2) var result2 = a2 + b2 testing.assert_equal( String(result2), "191.29", "Addition with different scales" ) # Test case 3: Addition with negative numbers - var a3 = Decimal(12345, scale=2) - var b3 = Decimal(-6789, scale=2) + var a3 = Decimal128(12345, scale=2) + var b3 = Decimal128(-6789, scale=2) var result3 = a3 + b3 testing.assert_equal( String(result3), "55.56", "Addition with negative number" ) # Test case 4: Addition resulting in negative - var a4 = Decimal(-12345, scale=2) - var b4 = Decimal(6789, scale=2) + var a4 = Decimal128(-12345, scale=2) + var b4 = Decimal128(6789, scale=2) var result4 = a4 + b4 testing.assert_equal( String(result4), "-55.56", "Addition resulting in negative" ) # Test case 5: Addition with zero - var a5 = Decimal(12345, scale=2) - var b5 = Decimal(0, scale=2) + var a5 = Decimal128(12345, scale=2) + var b5 = Decimal128(0, scale=2) var result5 = a5 + b5 testing.assert_equal(String(result5), "123.45", "Addition with zero") # Test case 6: Addition resulting in zero - var a6 = Decimal(12345, scale=2) - var b6 = Decimal(-12345, scale=2) + var a6 = Decimal128(12345, scale=2) + var b6 = Decimal128(-12345, scale=2) var result6 = a6 + b6 testing.assert_equal(String(result6), "0.00", "Addition resulting in zero") # Test case 7: Addition with large scales - var a7 = Decimal(1, scale=7) - var b7 = Decimal(2, scale=7) + var a7 = Decimal128(1, scale=7) + var b7 = Decimal128(2, scale=7) var result7 = a7 + b7 testing.assert_equal( String(result7), "0.0000003", "Addition with large scales" ) # Test case 8: Addition with different large scales - var a8 = Decimal(1, scale=6) - var b8 = Decimal(2, scale=7) + var a8 = Decimal128(1, scale=6) + var b8 = Decimal128(2, scale=7) var result8 = a8 + b8 testing.assert_equal( String(result8), "0.0000012", "Addition with different large scales" @@ -73,8 +73,8 @@ fn test_add() raises: # Additional edge cases for addition # Test case 9: Addition with many decimal places - var a9 = Decimal.from_uint128(123456789012345678901234567, scale=27) - var b9 = Decimal.from_uint128(987654321098765432109876543, scale=27) + var a9 = Decimal128.from_uint128(123456789012345678901234567, scale=27) + var b9 = Decimal128.from_uint128(987654321098765432109876543, scale=27) var result9 = a9 + b9 testing.assert_equal( String(result9), @@ -83,8 +83,8 @@ fn test_add() raises: ) # Test case 10: Addition with extreme scale difference - var a10 = Decimal(123456789) - var b10 = Decimal(123456789, scale=18) # 0.000000000123456789 + var a10 = Decimal128(123456789) + var b10 = Decimal128(123456789, scale=18) # 0.000000000123456789 var result10 = a10 + b10 testing.assert_equal( String(result10), @@ -93,10 +93,10 @@ fn test_add() raises: ) # Test case 11: Addition near maximum precision - var a11 = Decimal.from_uint128( + var a11 = Decimal128.from_uint128( UInt128(1111111111111111111111111111), scale=28 ) # 0.1111...1 (28 digits) - var b11 = Decimal.from_uint128( + var b11 = Decimal128.from_uint128( UInt128(9999999999999999999999999999), scale=28 ) # 0.9999...9 (28 digits) var result11 = a11 + b11 @@ -107,10 +107,10 @@ fn test_add() raises: ) # Test case 12: Addition causing scale truncation - var a12 = Decimal.from_uint128( + var a12 = Decimal128.from_uint128( UInt128(1111111111111111111111111111), scale=28 ) # 0.1111...1 (28 digits) - var b12 = Decimal.from_uint128( + var b12 = Decimal128.from_uint128( UInt128(999999999999999999999999999), scale=28 ) # 0.09999...9 (28 digits) var result12 = a12 + b12 @@ -121,8 +121,8 @@ fn test_add() raises: ) # Test case 13: Addition of very small numbers - var a13 = Decimal(1, scale=28) # 0.0000...01 (1 at 28th place) - var b13 = Decimal(2, scale=28) # 0.0000...02 (2 at 28th place) + var a13 = Decimal128(1, scale=28) # 0.0000...01 (1 at 28th place) + var b13 = Decimal128(2, scale=28) # 0.0000...02 (2 at 28th place) var result13 = a13 + b13 testing.assert_equal( String(result13), @@ -131,18 +131,18 @@ fn test_add() raises: ) # Test case 14: Addition with alternating signs and scales - var a14 = Decimal(101, 2) - var b14 = Decimal(-101, 3) + var a14 = Decimal128(101, 2) + var b14 = Decimal128(-101, 3) var result14 = a14 + b14 testing.assert_equal( String(result14), "0.909", "Addition with alternating signs and scales" ) # Test case 15: Addition with large numbers (near limits) - var a15 = Decimal.from_uint128( + var a15 = Decimal128.from_uint128( UInt128(79228162514264337593543950334) ) # MAX() - 1 - var b15 = Decimal(1) + var b15 = Decimal128(1) var result15 = a15 + b15 testing.assert_equal( String(result15), @@ -151,14 +151,14 @@ fn test_add() raises: ) # Test case 16: Repeated addition to test cumulative errors - var acc = Decimal(0) + var acc = Decimal128(0) for _ in range(10): - acc = acc + Decimal(1, scale=1) + acc = acc + Decimal128(1, scale=1) testing.assert_equal(String(acc), "1.0", "Repeated addition of 0.1") # Test case 17: Edge case with alternating very large and very small values - var a17 = Decimal.from_uint128(12345678901234567890123456789, scale=10) - var b17 = Decimal(9876543211, scale=28) + var a17 = Decimal128.from_uint128(12345678901234567890123456789, scale=10) + var b17 = Decimal128(9876543211, scale=28) var result17 = a17 + b17 # 1234567890123456789.0123456789000000009876543211 testing.assert_equal( @@ -167,13 +167,13 @@ fn test_add() raises: "Addition with large and small values", ) - print("Decimal addition tests passed!") + print("Decimal128 addition tests passed!") # Test case 18: Edge case with one equals 0 - var a18 = Decimal.from_uint128( + var a18 = Decimal128.from_uint128( UInt128(45631171710880163026696499898), scale=13 ) - var b18 = Decimal(0, scale=28) + var b18 = Decimal128(0, scale=28) var result18 = a18 + b18 # 1234567890123456789.0123456789 testing.assert_equal( @@ -182,7 +182,7 @@ fn test_add() raises: "Addition with zeros", ) - print("Decimal addition tests passed!") + print("Decimal128 addition tests passed!") fn test_negation() raises: @@ -190,43 +190,43 @@ fn test_negation() raises: print("Testing decimal negation...") # Test case 1: Negate positive number - var a1 = Decimal(12345, 2) + var a1 = Decimal128(12345, 2) var result1 = -a1 testing.assert_equal(String(result1), "-123.45", "Negating positive number") # Test case 2: Negate negative number - var a2 = Decimal(-6789, 2) + var a2 = Decimal128(-6789, 2) var result2 = -a2 testing.assert_equal(String(result2), "67.89", "Negating negative number") # Test case 3: Negate zero - var a3 = Decimal(0) + var a3 = Decimal128(0) var result3 = -a3 testing.assert_equal(String(result3), "0", "Negating zero") # Test case 4: Negate number with trailing zeros - var a4 = Decimal(1234500, 4) + var a4 = Decimal128(1234500, 4) var result4 = -a4 testing.assert_equal( String(result4), "-123.4500", "Negating with trailing zeros" ) # Test case 5: Double negation - var a5 = Decimal(12345, 2) + var a5 = Decimal128(12345, 2) var result5 = -(-a5) testing.assert_equal(String(result5), "123.45", "Double negation") # Additional edge cases for negation # Test case 6: Negate very small number - var a6 = Decimal(1, scale=28) # 0.0000...01 (1 at 28th place) + var a6 = Decimal128(1, scale=28) # 0.0000...01 (1 at 28th place) var result6 = -a6 testing.assert_equal( String(result6), "-0." + "0" * 27 + "1", "Negating very small number" ) # Test case 7: Negate very large number - var a7 = Decimal.from_uint128( + var a7 = Decimal128.from_uint128( UInt128(79228162514264337593543950335) ) # MAX() var result7 = -a7 @@ -237,12 +237,12 @@ fn test_negation() raises: ) # Test case 8: Triple negation - var a8 = Decimal(12345, 2) + var a8 = Decimal128(12345, 2) var result8 = -(-(-a8)) testing.assert_equal(String(result8), "-123.45", "Triple negation") # Test case 9: Negate number with scientific notation - var a9 = Decimal("1.23e5") # 123000 + var a9 = Decimal128("1.23e5") # 123000 var result9 = -a9 testing.assert_equal( String(result9), @@ -251,7 +251,7 @@ fn test_negation() raises: ) # Test case 10: Negate number with maximum precision - var a10 = Decimal("0." + "1" * 28) # 0.1111...1 (28 digits) + var a10 = Decimal128("0." + "1" * 28) # 0.1111...1 (28 digits) var result10 = -a10 testing.assert_equal( String(result10), @@ -259,7 +259,7 @@ fn test_negation() raises: "Negating number with maximum precision", ) - print("Decimal negation tests passed!") + print("Decimal128 negation tests passed!") fn test_abs() raises: @@ -267,33 +267,33 @@ fn test_abs() raises: print("Testing decimal absolute value...") # Test case 1: Absolute value of positive number - var a1 = Decimal(12345, 2) + var a1 = Decimal128(12345, 2) var result1 = abs(a1) testing.assert_equal( String(result1), "123.45", "Absolute value of positive number" ) # Test case 2: Absolute value of negative number - var a2 = Decimal(-6789, 2) + var a2 = Decimal128(-6789, 2) var result2 = abs(a2) testing.assert_equal( String(result2), "67.89", "Absolute value of negative number" ) # Test case 3: Absolute value of zero - var a3 = Decimal(0) + var a3 = Decimal128(0) var result3 = abs(a3) testing.assert_equal(String(result3), "0", "Absolute value of zero") # Test case 4: Absolute value of negative zero (if supported) - var a4 = Decimal(-0, 2) + var a4 = Decimal128(-0, 2) var result4 = abs(a4) testing.assert_equal( String(result4), "0.00", "Absolute value of negative zero" ) # Test case 5: Absolute value with large number of decimal places - var a5 = Decimal(-1, 10) + var a5 = Decimal128(-1, 10) var result5 = abs(a5) testing.assert_equal( String(result5), @@ -302,7 +302,7 @@ fn test_abs() raises: ) # Test case 6: Absolute value of very large number - var a6 = Decimal("-9999999999.9999999999") + var a6 = Decimal128("-9999999999.9999999999") var result6 = abs(a6) testing.assert_equal( String(result6), @@ -311,7 +311,7 @@ fn test_abs() raises: ) # Test case 7: Absolute value of number with many significant digits - var a7 = Decimal("-0.123456789012345678901234567") + var a7 = Decimal128("-0.123456789012345678901234567") var result7 = abs(a7) testing.assert_equal( String(result7), @@ -321,7 +321,7 @@ fn test_abs() raises: # Test case 8: Absolute value of maximum representable number try: - var a8 = Decimal.from_uint128( + var a8 = Decimal128.from_uint128( UInt128(79228162514264337593543950335) ) # Maximum value var result8 = abs(a8) @@ -331,7 +331,7 @@ fn test_abs() raises: "Absolute value of maximum value", ) - var a9 = Decimal( + var a9 = Decimal128( "-79228162514264337593543950335" ) # Negative maximum value var result9 = abs(a9) @@ -343,7 +343,7 @@ fn test_abs() raises: except: print("Maximum value test not applicable") - print("Decimal absolute value tests passed!") + print("Decimal128 absolute value tests passed!") fn test_subtract() raises: @@ -351,78 +351,78 @@ fn test_subtract() raises: print("Testing decimal subtraction...") # Test case 1: Simple subtraction with same scale - var a1 = Decimal(12345, 2) - var b1 = Decimal(6789, 2) + var a1 = Decimal128(12345, 2) + var b1 = Decimal128(6789, 2) var result1 = a1 - b1 testing.assert_equal( String(result1), "55.56", "Simple subtraction with same scale" ) # Test case 2: Subtraction with different scales - var a2 = Decimal(1234, 1) - var b2 = Decimal(6789, 2) + var a2 = Decimal128(1234, 1) + var b2 = Decimal128(6789, 2) var result2 = a2 - b2 testing.assert_equal( String(result2), "55.51", "Subtraction with different scales" ) # Test case 3: Subtraction resulting in negative - var a3 = Decimal(6789, 2) - var b3 = Decimal(12345, 2) + var a3 = Decimal128(6789, 2) + var b3 = Decimal128(12345, 2) var result3 = a3 - b3 testing.assert_equal( String(result3), "-55.56", "Subtraction resulting in negative" ) # Test case 4: Subtraction of negative numbers - var a4 = Decimal(12345, 2) - var b4 = Decimal(-6789, 2) + var a4 = Decimal128(12345, 2) + var b4 = Decimal128(-6789, 2) var result4 = a4 - b4 testing.assert_equal( String(result4), "191.34", "Subtraction of negative number" ) # Test case 5: Subtraction with zero - var a5 = Decimal(12345, 2) - var b5 = Decimal(0, 2) + var a5 = Decimal128(12345, 2) + var b5 = Decimal128(0, 2) var result5 = a5 - b5 testing.assert_equal(String(result5), "123.45", "Subtraction with zero") # Test case 6: Subtraction resulting in zero - var a6 = Decimal(12345, 2) - var b6 = Decimal(12345, 2) + var a6 = Decimal128(12345, 2) + var b6 = Decimal128(12345, 2) var result6 = a6 - b6 testing.assert_equal( String(result6), "0.00", "Subtraction resulting in zero" ) # Test case 7: Subtraction with large scales - var a7 = Decimal(3, 7) - var b7 = Decimal(2, 7) + var a7 = Decimal128(3, 7) + var b7 = Decimal128(2, 7) var result7 = a7 - b7 testing.assert_equal( String(result7), "0.0000001", "Subtraction with large scales" ) # Test case 8: Subtraction with different large scales - var a8 = Decimal(5, 6) - var b8 = Decimal(2, 7) + var a8 = Decimal128(5, 6) + var b8 = Decimal128(2, 7) var result8 = a8 - b8 testing.assert_equal( String(result8), "0.0000048", "Subtraction with different large scales" ) # Test case 9: Subtraction with small difference - var a9 = Decimal(10000001, 7) - var b9 = Decimal(10000000, 7) + var a9 = Decimal128(10000001, 7) + var b9 = Decimal128(10000000, 7) var result9 = a9 - b9 testing.assert_equal( String(result9), "0.0000001", "Subtraction with small difference" ) # Test case 10: Subtraction of very small from very large - var a10 = Decimal("9999999999.9999999") - var b10 = Decimal("0.0000001") + var a10 = Decimal128("9999999999.9999999") + var b10 = Decimal128("0.0000001") var result10 = a10 - b10 testing.assert_equal( String(result10), @@ -432,51 +432,51 @@ fn test_subtract() raises: # Test case 11: Self subtraction for various values (expanded from list) # Individual test cases instead of iterating over a list - var value1 = Decimal(0) + var value1 = Decimal128(0) testing.assert_equal( String(value1 - value1), - String(round(Decimal(0), value1.scale())), + String(round(Decimal128(0), value1.scale())), "Self subtraction should yield zero (0)", ) - var value2 = Decimal(12345, 2) + var value2 = Decimal128(12345, 2) testing.assert_equal( String(value2 - value2), - String(round(Decimal(0), value2.scale())), + String(round(Decimal128(0), value2.scale())), "Self subtraction should yield zero (123.45)", ) - var value3 = Decimal(-987654, 3) + var value3 = Decimal128(-987654, 3) testing.assert_equal( String(value3 - value3), - String(round(Decimal(0), value3.scale())), + String(round(Decimal128(0), value3.scale())), "Self subtraction should yield zero (-987.654)", ) - var value4 = Decimal(1, 4) + var value4 = Decimal128(1, 4) testing.assert_equal( String(value4 - value4), - String(round(Decimal(0), value4.scale())), + String(round(Decimal128(0), value4.scale())), "Self subtraction should yield zero (0.0001)", ) - var value5 = Decimal("-99999.99999") + var value5 = Decimal128("-99999.99999") testing.assert_equal( String(value5 - value5), - String(round(Decimal(0), value5.scale())), + String(round(Decimal128(0), value5.scale())), "Self subtraction should yield zero (-99999.99999)", ) # Test case 12: Verify that a - b = -(b - a) - var a12a = Decimal(123456, 3) - var b12a = Decimal(789012, 3) + var a12a = Decimal128(123456, 3) + var b12a = Decimal128(789012, 3) var result12a = a12a - b12a var result12b = -(b12a - a12a) testing.assert_equal( String(result12a), String(result12b), "a - b should equal -(b - a)" ) - print("Decimal subtraction tests passed!") + print("Decimal128 subtraction tests passed!") fn test_extreme_cases() raises: @@ -484,8 +484,8 @@ fn test_extreme_cases() raises: print("Testing extreme cases...") # Test case 1: Addition that results in exactly zero with high precision - var a1 = Decimal("0." + "1" * 28) # 0.1111...1 (28 digits) - var b1 = Decimal("-0." + "1" * 28) # -0.1111...1 (28 digits) + var a1 = Decimal128("0." + "1" * 28) # 0.1111...1 (28 digits) + var b1 = Decimal128("-0." + "1" * 28) # -0.1111...1 (28 digits) var result1 = a1 + b1 testing.assert_equal( String(result1), @@ -495,32 +495,32 @@ fn test_extreme_cases() raises: # Test case 2: Addition that should trigger overflow handling try: - var a2 = Decimal("79228162514264337593543950335") # MAX() - var b2 = Decimal(1) + var a2 = Decimal128("79228162514264337593543950335") # MAX() + var b2 = Decimal128(1) var _result2 = a2 + b2 print("WARNING: Addition beyond MAX() didn't raise an error") except: print("Addition overflow correctly detected") # Test case 3: Addition with mixed precision zeros - var a3 = Decimal("0.00") - var b3 = Decimal("0.000000") + var a3 = Decimal128("0.00") + var b3 = Decimal128("0.000000") var result3 = a3 + b3 testing.assert_equal( String(result3), "0.000000", "Addition of different precision zeros" ) # Test case 4: Addition with boundary values involving zeros - var a4 = Decimal("0.0") - var b4 = Decimal("-0.00") + var a4 = Decimal128("0.0") + var b4 = Decimal128("-0.00") var result4 = a4 + b4 testing.assert_equal( String(result4), "0.00", "Addition of positive and negative zero" ) # Test case 5: Adding numbers that require carry propagation through many places - var a5 = Decimal("9" * 20 + "." + "9" * 28) # 99...9.99...9 - var b5 = Decimal("0." + "0" * 27 + "1") # 0.00...01 + var a5 = Decimal128("9" * 20 + "." + "9" * 28) # 99...9.99...9 + var b5 = Decimal128("0." + "0" * 27 + "1") # 0.00...01 var result5 = a5 + b5 # The result should be 10^20 exactly, since all 9s carry over testing.assert_equal( diff --git a/tests/decimal/test_decimal_comparison.mojo b/tests/decimal128/test_decimal128_comparison.mojo similarity index 78% rename from tests/decimal/test_decimal_comparison.mojo rename to tests/decimal128/test_decimal128_comparison.mojo index 38393e5..f51c484 100644 --- a/tests/decimal/test_decimal_comparison.mojo +++ b/tests/decimal128/test_decimal128_comparison.mojo @@ -1,9 +1,9 @@ """ -Test Decimal logic operations for comparison, including basic comparisons, +Test Decimal128 logic operations for comparison, including basic comparisons, edge cases, special handling for zero values, and operator overloads. """ -from decimojo.prelude import dm, Decimal, RoundingMode -from decimojo.decimal.comparison import ( +from decimojo.prelude import dm, Decimal128, RoundingMode +from decimojo.decimal128.comparison import ( greater, greater_equal, less, @@ -19,39 +19,39 @@ fn test_equality() raises: print("Testing decimal equality...") # Test case 1: Equal decimals - var a1 = Decimal(12345, 2) - var b1 = Decimal(12345, 2) + var a1 = Decimal128(12345, 2) + var b1 = Decimal128(12345, 2) testing.assert_true(equal(a1, b1), "Equal decimals should be equal") # Test case 2: Equal with different scales - var a2 = Decimal("123.450") - var b2 = Decimal(12345, 2) + var a2 = Decimal128("123.450") + var b2 = Decimal128(12345, 2) testing.assert_true( equal(a2, b2), "Equal decimals with different scales should be equal" ) # Test case 3: Different values - var a3 = Decimal(12345, 2) - var b3 = Decimal("123.46") + var a3 = Decimal128(12345, 2) + var b3 = Decimal128("123.46") testing.assert_false( equal(a3, b3), "Different decimals should not be equal" ) # Test case 4: Zeros with different scales - var a4 = Decimal(0) - var b4 = Decimal("0.00") + var a4 = Decimal128(0) + var b4 = Decimal128("0.00") testing.assert_true( equal(a4, b4), "Zeros with different scales should be equal" ) # Test case 5: Zero and negative zero - var a5 = Decimal(0) - var b5 = Decimal("-0") + var a5 = Decimal128(0) + var b5 = Decimal128("-0") testing.assert_true(equal(a5, b5), "Zero and negative zero should be equal") # Test case 6: Same absolute value but different signs - var a6 = Decimal(12345, 2) - var b6 = Decimal("-123.45") + var a6 = Decimal128(12345, 2) + var b6 = Decimal128("-123.45") testing.assert_false( equal(a6, b6), "Same absolute value but different signs should not be equal", @@ -65,30 +65,30 @@ fn test_inequality() raises: print("Testing decimal inequality...") # Test case 1: Equal decimals - var a1 = Decimal(12345, 2) - var b1 = Decimal(12345, 2) + var a1 = Decimal128(12345, 2) + var b1 = Decimal128(12345, 2) testing.assert_false( not_equal(a1, b1), "Equal decimals should not be unequal" ) # Test case 2: Equal with different scales - var a2 = Decimal(123450, 3) - var b2 = Decimal(12345, 2) + var a2 = Decimal128(123450, 3) + var b2 = Decimal128(12345, 2) testing.assert_false( not_equal(a2, b2), "Equal decimals with different scales should not be unequal", ) # Test case 3: Different values - var a3 = Decimal(12345, 2) - var b3 = Decimal(12346, 2) + var a3 = Decimal128(12345, 2) + var b3 = Decimal128(12346, 2) testing.assert_true( not_equal(a3, b3), "Different decimals should be unequal" ) # Test case 4: Same absolute value but different signs - var a4 = Decimal(12345, 2) - var b4 = Decimal(-12345, 2) + var a4 = Decimal128(12345, 2) + var b4 = Decimal128(-12345, 2) testing.assert_true( not_equal(a4, b4), "Same absolute value but different signs should be unequal", @@ -101,23 +101,23 @@ fn test_greater() raises: print("Testing greater than comparison...") # Test case 1: Larger decimal - var a1 = Decimal(12346, 2) - var b1 = Decimal(12345, 2) + var a1 = Decimal128(12346, 2) + var b1 = Decimal128(12345, 2) testing.assert_true(greater(a1, b1), "123.46 should be greater than 123.45") testing.assert_false( greater(b1, a1), "123.45 should not be greater than 123.46" ) # Test case 2: Equal decimals - var a2 = Decimal(12345, 2) - var b2 = Decimal(12345, 2) + var a2 = Decimal128(12345, 2) + var b2 = Decimal128(12345, 2) testing.assert_false( greater(a2, b2), "Equal decimals should not be greater" ) # Test case 3: Positive vs. negative - var a3 = Decimal(12345, 2) - var b3 = Decimal(-12345, 2) + var a3 = Decimal128(12345, 2) + var b3 = Decimal128(-12345, 2) testing.assert_true( greater(a3, b3), "Positive should be greater than negative" ) @@ -126,27 +126,27 @@ fn test_greater() raises: ) # Test case 4: Negative with smaller absolute value - var a4 = Decimal("-123.45") - var b4 = Decimal("-123.46") + var a4 = Decimal128("-123.45") + var b4 = Decimal128("-123.46") testing.assert_true( greater(a4, b4), "-123.45 should be greater than -123.46" ) # Test case 5: Zero vs. positive - var a5 = Decimal(0) - var b5 = Decimal(12345, 2) + var a5 = Decimal128(0) + var b5 = Decimal128(12345, 2) testing.assert_false( greater(a5, b5), "Zero should not be greater than positive" ) # Test case 6: Zero vs. negative - var a6 = Decimal(0) - var b6 = Decimal("-123.45") + var a6 = Decimal128(0) + var b6 = Decimal128("-123.45") testing.assert_true(greater(a6, b6), "Zero should be greater than negative") # Test case 7: Different scales - var a7 = Decimal("123.5") - var b7 = Decimal(12345, 2) + var a7 = Decimal128("123.5") + var b7 = Decimal128(12345, 2) testing.assert_true(greater(a7, b7), "123.5 should be greater than 123.45") print("Greater than tests passed!") @@ -156,39 +156,39 @@ fn test_greater_equal() raises: print("Testing greater than or equal comparison...") # Test case 1: Larger decimal - var a1 = Decimal("123.46") - var b1 = Decimal(12345, 2) + var a1 = Decimal128("123.46") + var b1 = Decimal128(12345, 2) testing.assert_true( greater_equal(a1, b1), "123.46 should be greater than or equal to 123.45", ) # Test case 2: Equal decimals - var a2 = Decimal(12345, 2) - var b2 = Decimal(12345, 2) + var a2 = Decimal128(12345, 2) + var b2 = Decimal128(12345, 2) testing.assert_true( greater_equal(a2, b2), "Equal decimals should be greater than or equal" ) # Test case 3: Positive vs. negative - var a3 = Decimal(12345, 2) - var b3 = Decimal("-123.45") + var a3 = Decimal128(12345, 2) + var b3 = Decimal128("-123.45") testing.assert_true( greater_equal(a3, b3), "Positive should be greater than or equal to negative", ) # Test case 4: Equal values with different scales - var a4 = Decimal("123.450") - var b4 = Decimal(12345, 2) + var a4 = Decimal128("123.450") + var b4 = Decimal128(12345, 2) testing.assert_true( greater_equal(a4, b4), "Equal values with different scales should be greater than or equal", ) # Test case 5: Smaller decimal - var a5 = Decimal(12345, 2) - var b5 = Decimal("123.46") + var a5 = Decimal128(12345, 2) + var b5 = Decimal128("123.46") testing.assert_false( greater_equal(a5, b5), "123.45 should not be greater than or equal to 123.46", @@ -201,28 +201,28 @@ fn test_less() raises: print("Testing less than comparison...") # Test case 1: Smaller decimal - var a1 = Decimal(12345, 2) - var b1 = Decimal("123.46") + var a1 = Decimal128(12345, 2) + var b1 = Decimal128("123.46") testing.assert_true(less(a1, b1), "123.45 should be less than 123.46") # Test case 2: Equal decimals - var a2 = Decimal(12345, 2) - var b2 = Decimal(12345, 2) + var a2 = Decimal128(12345, 2) + var b2 = Decimal128(12345, 2) testing.assert_false(less(a2, b2), "Equal decimals should not be less") # Test case 3: Negative vs. positive - var a3 = Decimal("-123.45") - var b3 = Decimal(12345, 2) + var a3 = Decimal128("-123.45") + var b3 = Decimal128(12345, 2) testing.assert_true(less(a3, b3), "Negative should be less than positive") # Test case 4: Negative with larger absolute value - var a4 = Decimal("-123.46") - var b4 = Decimal("-123.45") + var a4 = Decimal128("-123.46") + var b4 = Decimal128("-123.45") testing.assert_true(less(a4, b4), "-123.46 should be less than -123.45") # Test case 5: Zero vs. positive - var a5 = Decimal(0) - var b5 = Decimal(12345, 2) + var a5 = Decimal128(0) + var b5 = Decimal128(12345, 2) testing.assert_true(less(a5, b5), "Zero should be less than positive") print("Less than tests passed!") @@ -232,37 +232,37 @@ fn test_less_equal() raises: print("Testing less than or equal comparison...") # Test case 1: Smaller decimal - var a1 = Decimal(12345, 2) - var b1 = Decimal("123.46") + var a1 = Decimal128(12345, 2) + var b1 = Decimal128("123.46") testing.assert_true( less_equal(a1, b1), "123.45 should be less than or equal to 123.46" ) # Test case 2: Equal decimals - var a2 = Decimal(12345, 2) - var b2 = Decimal(12345, 2) + var a2 = Decimal128(12345, 2) + var b2 = Decimal128(12345, 2) testing.assert_true( less_equal(a2, b2), "Equal decimals should be less than or equal" ) # Test case 3: Negative vs. positive - var a3 = Decimal("-123.45") - var b3 = Decimal(12345, 2) + var a3 = Decimal128("-123.45") + var b3 = Decimal128(12345, 2) testing.assert_true( less_equal(a3, b3), "Negative should be less than or equal to positive" ) # Test case 4: Equal values with different scales - var a4 = Decimal("123.450") - var b4 = Decimal(12345, 2) + var a4 = Decimal128("123.450") + var b4 = Decimal128(12345, 2) testing.assert_true( less_equal(a4, b4), "Equal values with different scales should be less than or equal", ) # Test case 5: Larger decimal - var a5 = Decimal("123.46") - var b5 = Decimal(12345, 2) + var a5 = Decimal128("123.46") + var b5 = Decimal128(12345, 2) testing.assert_false( less_equal(a5, b5), "123.46 should not be less than or equal to 123.45" ) @@ -273,10 +273,10 @@ fn test_less_equal() raises: fn test_zero_comparison() raises: print("Testing zero comparison cases...") - var zero = Decimal(0) - var pos = Decimal("0.0000000000000000001") # Very small positive - var neg = Decimal("-0.0000000000000000001") # Very small negative - var zero_scale = Decimal("0.00000") # Zero with different scale + var zero = Decimal128(0) + var pos = Decimal128("0.0000000000000000001") # Very small positive + var neg = Decimal128("-0.0000000000000000001") # Very small negative + var zero_scale = Decimal128("0.00000") # Zero with different scale # Zero compared to small positive testing.assert_false(greater(zero, pos), "Zero should not be > positive") @@ -329,7 +329,7 @@ fn test_zero_comparison() raises: ) # Negative zero - var neg_zero = Decimal("-0") + var neg_zero = Decimal128("-0") testing.assert_true( equal(zero, neg_zero), "Zero should be == negative zero" ) @@ -353,30 +353,32 @@ fn test_edge_cases() raises: print("Testing comparison edge cases...") # Test case 1: Very close values - var a1 = Decimal("1.000000000000000000000000001") - var b1 = Decimal("1.000000000000000000000000000") + var a1 = Decimal128("1.000000000000000000000000001") + var b1 = Decimal128("1.000000000000000000000000000") testing.assert_true( greater(a1, b1), "1.000...001 should be greater than 1.000...000" ) # Test case 2: Very large values - var a2 = Decimal("79228162514264337593543950335") # MAX value - var b2 = Decimal("79228162514264337593543950334") # MAX - 1 + var a2 = Decimal128("79228162514264337593543950335") # MAX value + var b2 = Decimal128("79228162514264337593543950334") # MAX - 1 testing.assert_true(greater(a2, b2), "MAX should be greater than MAX-1") # Test case 3: Very small negatives vs very small positives - var a3 = Decimal("-0." + "0" * 27 + "1") # -0.0000...01 (1 at 28th place) - var b3 = Decimal("0." + "0" * 27 + "1") # 0.0000...01 (1 at 28th place) + var a3 = Decimal128( + "-0." + "0" * 27 + "1" + ) # -0.0000...01 (1 at 28th place) + var b3 = Decimal128("0." + "0" * 27 + "1") # 0.0000...01 (1 at 28th place) testing.assert_true( less(a3, b3), "Very small negative should be less than very small positive", ) # Test case 4: Transitivity checks - var neg_large = Decimal("-1000") - var neg_small = Decimal("-0.001") - var pos_small = Decimal("0.001") - var pos_large = Decimal(1000) + var neg_large = Decimal128("-1000") + var neg_small = Decimal128("-0.001") + var pos_small = Decimal128("0.001") + var pos_large = Decimal128(1000) # Transitivity: if a > b and b > c then a > c testing.assert_true(greater(pos_large, pos_small), "1000 > 0.001") @@ -393,26 +395,26 @@ fn test_exact_comparison() raises: print("Testing exact comparison with precision handling...") # Test case 1: Scale handling with zeros - var zero1 = Decimal(0) - var zero2 = Decimal("0.0") - var zero3 = Decimal("0.00000") + var zero1 = Decimal128(0) + var zero2 = Decimal128("0.0") + var zero3 = Decimal128("0.00000") testing.assert_true(equal(zero1, zero2), "0 == 0.0") testing.assert_true(equal(zero1, zero3), "0 == 0.00000") testing.assert_true(equal(zero2, zero3), "0.0 == 0.00000") # Test case 2: Equal values with different number of trailing zeros - var d1 = Decimal("123.400") - var d2 = Decimal("123.4") - var d3 = Decimal("123.40000") + var d1 = Decimal128("123.400") + var d2 = Decimal128("123.4") + var d3 = Decimal128("123.40000") testing.assert_true(equal(d1, d2), "123.400 == 123.4") testing.assert_true(equal(d2, d3), "123.4 == 123.40000") testing.assert_true(equal(d1, d3), "123.400 == 123.40000") # Test case 3: Numbers that appear close but are different - var e1 = Decimal("1.2") - var e2 = Decimal("1.20000001") + var e1 = Decimal128("1.2") + var e2 = Decimal128("1.20000001") testing.assert_false(equal(e1, e2), "1.2 != 1.20000001") testing.assert_true(less(e1, e2), "1.2 < 1.20000001") @@ -424,13 +426,13 @@ fn test_comparison_operators() raises: print("Testing comparison operators...") # Create test values - var a = Decimal(12345, 2) - var b = Decimal("67.89") - var c = Decimal(12345, 2) # Equal to a - var d = Decimal("123.450") # Equal to a with different scale - var e = Decimal("-50.0") # Negative number - var f = Decimal(0) # Zero - var g = Decimal("-0.0") # Negative zero (equal to zero) + var a = Decimal128(12345, 2) + var b = Decimal128("67.89") + var c = Decimal128(12345, 2) # Equal to a + var d = Decimal128("123.450") # Equal to a with different scale + var e = Decimal128("-50.0") # Negative number + var f = Decimal128(0) # Zero + var g = Decimal128("-0.0") # Negative zero (equal to zero) # Greater than testing.assert_true(a > b, "a > b: 123.45 should be > 67.89") diff --git a/tests/decimal/test_decimal_divide.mojo b/tests/decimal128/test_decimal128_divide.mojo similarity index 68% rename from tests/decimal/test_decimal_divide.mojo rename to tests/decimal128/test_decimal128_divide.mojo index 20e9ea9..1b2c571 100644 --- a/tests/decimal/test_decimal_divide.mojo +++ b/tests/decimal128/test_decimal128_divide.mojo @@ -1,9 +1,9 @@ """ -Comprehensive test suite for Decimal division operations. +Comprehensive test suite for Decimal128 division operations. Includes 100 test cases covering edge cases, precision limits, and various scenarios. """ -from decimojo.prelude import dm, Decimal, RoundingMode +from decimojo.prelude import dm, Decimal128, RoundingMode import testing @@ -13,64 +13,66 @@ fn test_basic_division() raises: # 1. Simple integer division testing.assert_equal( - String(Decimal(10) / Decimal(2)), "5", "Simple integer division" + String(Decimal128(10) / Decimal128(2)), "5", "Simple integer division" ) # 2. Division with no remainder testing.assert_equal( - String(Decimal(100) / Decimal(4)), + String(Decimal128(100) / Decimal128(4)), "25", "Division with no remainder", ) # 3. Division resulting in non-integer testing.assert_equal( - String(Decimal(10) / Decimal(4)), + String(Decimal128(10) / Decimal128(4)), "2.5", "Division resulting in non-integer", ) # 4. Division by one testing.assert_equal( - String(Decimal("123.45") / Decimal(1)), "123.45", "Division by one" + String(Decimal128("123.45") / Decimal128(1)), + "123.45", + "Division by one", ) # 5. Division of zero testing.assert_equal( - String(Decimal(0) / Decimal(42)), "0", "Division of zero" + String(Decimal128(0) / Decimal128(42)), "0", "Division of zero" ) # 6. Division with both negative numbers testing.assert_equal( - String(Decimal("-10") / Decimal(-5)), + String(Decimal128("-10") / Decimal128(-5)), "2", "Division with both negative numbers", ) # 7. Division with negative dividend testing.assert_equal( - String(Decimal("-10") / Decimal(5)), + String(Decimal128("-10") / Decimal128(5)), "-2", "Division with negative dividend", ) # 8. Division with negative divisor testing.assert_equal( - String(Decimal(10) / Decimal(-5)), + String(Decimal128(10) / Decimal128(-5)), "-2", "Division with negative divisor", ) # 9. Division with decimals, same scale testing.assert_equal( - String(Decimal("10.5") / Decimal("2.1")), + String(Decimal128("10.5") / Decimal128("2.1")), "5", "Division with decimals, same scale", ) # 10. Division with decimals, different scales testing.assert_equal( - String(Decimal("10.5") / Decimal("0.5")), + String(Decimal128("10.5") / Decimal128("0.5")), "21", "Division with decimals, different scales", ) @@ -83,70 +85,70 @@ fn test_repeating_decimals() raises: print("Testing division with repeating decimals...") # 11. Division resulting in 1/3 - var third = Decimal(1) / Decimal(3) + var third = Decimal128(1) / Decimal128(3) testing.assert_true( String(third).startswith("0.33333333333333"), "Case 11: Division resulting in 1/3 failed", ) # 12. Division resulting in 1/6 - var sixth = Decimal(1) / Decimal(6) + var sixth = Decimal128(1) / Decimal128(6) testing.assert_true( String(sixth).startswith("0.16666666666666"), "Case 12: Division resulting in 1/6 failed", ) # 13. Division resulting in 1/7 - var seventh = Decimal(1) / Decimal(7) + var seventh = Decimal128(1) / Decimal128(7) testing.assert_true( String(seventh).startswith("0.142857142857142857"), "Case 13: Division resulting in 1/7 failed", ) # 14. Division resulting in 2/3 - var two_thirds = Decimal(2) / Decimal(3) + var two_thirds = Decimal128(2) / Decimal128(3) testing.assert_true( String(two_thirds).startswith("0.66666666666666"), "Case 14: Division resulting in 2/3 failed", ) # 15. Division resulting in 5/6 - var five_sixths = Decimal(5) / Decimal(6) + var five_sixths = Decimal128(5) / Decimal128(6) testing.assert_true( String(five_sixths).startswith("0.83333333333333"), "Case 15: Division resulting in 5/6 failed", ) # 16. Division of 1 by 9 - var one_ninth = Decimal(1) / Decimal(9) + var one_ninth = Decimal128(1) / Decimal128(9) testing.assert_true( String(one_ninth).startswith("0.11111111111111"), "Case 16: Division of 1 by 9 failed", ) # 17. Division of 1 by 11 - var one_eleventh = Decimal(1) / Decimal(11) + var one_eleventh = Decimal128(1) / Decimal128(11) testing.assert_true( String(one_eleventh).startswith("0.0909090909090"), "Case 17: Division of 1 by 11 failed", ) # 18. Division of 1 by 12 - var one_twelfth = Decimal(1) / Decimal(12) + var one_twelfth = Decimal128(1) / Decimal128(12) testing.assert_true( String(one_twelfth).startswith("0.08333333333333"), "Case 18: Division of 1 by 12 failed", ) # 19. Division of 5 by 11 - var five_elevenths = Decimal(5) / Decimal(11) + var five_elevenths = Decimal128(5) / Decimal128(11) testing.assert_true( String(five_elevenths).startswith("0.4545454545454"), "Case 19: Division of 5 by 11 failed", ) # 20. Division of 10 by 3 - var ten_thirds = Decimal(10) / Decimal(3) + var ten_thirds = Decimal128(10) / Decimal128(3) testing.assert_true( String(ten_thirds).startswith("3.33333333333333"), "Case 20: Division of 10 by 3 failed", @@ -160,29 +162,29 @@ fn test_precision_rounding() raises: print("Testing division precision and rounding...") # 21. Rounding half even (banker's rounding) at precision limit - var a21 = Decimal(2) / Decimal(3) # Should be ~0.6666...67 - var b21 = Decimal("0." + "6" * 27 + "7") # 0.6666...67 + var a21 = Decimal128(2) / Decimal128(3) # Should be ~0.6666...67 + var b21 = Decimal128("0." + "6" * 27 + "7") # 0.6666...67 testing.assert_equal( String(a21), String(b21), "Rounding half even at precision limit" ) # 22. Another case of rounding half even - var a22 = Decimal(1) / Decimal(9) # Should be ~0.1111...11 - var b22 = Decimal("0." + "1" * 28) # 0.1111...11 + var a22 = Decimal128(1) / Decimal128(9) # Should be ~0.1111...11 + var b22 = Decimal128("0." + "1" * 28) # 0.1111...11 testing.assert_equal( String(a22), String(b22), "Another case of rounding half even" ) # 23. Rounding up at precision limit - var a23 = Decimal(10) / Decimal(3) # Should be ~3.3333...33 - var b23 = Decimal("3." + "3" * 28) # 3.3333...33 + var a23 = Decimal128(10) / Decimal128(3) # Should be ~3.3333...33 + var b23 = Decimal128("3." + "3" * 28) # 3.3333...33 testing.assert_equal( String(a23), String(b23), "Rounding up at precision limit" ) # 24. Division requiring rounding to precision limit - var a24 = Decimal(1) / Decimal(7) # ~0.142857... - var manually_calculated = Decimal("0.1428571428571428571428571429") + var a24 = Decimal128(1) / Decimal128(7) # ~0.142857... + var manually_calculated = Decimal128("0.1428571428571428571428571429") testing.assert_equal( String(a24), String(manually_calculated), @@ -190,44 +192,44 @@ fn test_precision_rounding() raises: ) # 25. Precision limit with repeating 9s - var a25 = Decimal(1) / Decimal(81) # ~0.01234... - var precision_reached = a25.scale() <= Decimal.MAX_SCALE + var a25 = Decimal128(1) / Decimal128(81) # ~0.01234... + var precision_reached = a25.scale() <= Decimal128.MAX_SCALE testing.assert_true(precision_reached, "Scale should not exceed MAX_SCALE") # 26. Test precision with negative numbers - var a26 = Decimal(-1) / Decimal(3) - var b26 = Decimal("-0." + "3" * 28) # -0.3333...33 + var a26 = Decimal128(-1) / Decimal128(3) + var b26 = Decimal128("-0." + "3" * 28) # -0.3333...33 testing.assert_equal( String(a26), String(b26), "Test precision with negative numbers" ) # 27. Division with result at exactly precision limit - var a27 = Decimal(1) / Decimal(String("1" + "0" * 28)) # 1/10^28 + var a27 = Decimal128(1) / Decimal128(String("1" + "0" * 28)) # 1/10^28 testing.assert_equal( String(a27), - String(Decimal("0." + "0" * 27 + "1")), + String(Decimal128("0." + "0" * 27 + "1")), "Division with result at exactly precision limit", ) # 28. Division with result needing one more than precision limit - var a28 = Decimal(1) / Decimal(String("1" + "0" * 28)) # 1/10^29 + var a28 = Decimal128(1) / Decimal128(String("1" + "0" * 28)) # 1/10^29 testing.assert_equal( String(a28), - String(Decimal("0." + "0" * 27 + "1")), + String(Decimal128("0." + "0" * 27 + "1")), "Division with result needing one more than precision limit", ) # 29. Division where quotient has more digits than precision allows - var a29 = Decimal("12345678901234567890123456789") / Decimal(7) + var a29 = Decimal128("12345678901234567890123456789") / Decimal128(7) testing.assert_true( - a29.scale() <= Decimal.MAX_SCALE, + a29.scale() <= Decimal128.MAX_SCALE, "Scale should not exceed MAX_SCALE", ) # 30. Division where both operands have maximum precision - var a30 = Decimal("0." + "1" * 28) / Decimal("0." + "9" * 28) + var a30 = Decimal128("0." + "1" * 28) / Decimal128("0." + "9" * 28) testing.assert_true( - a30.scale() <= Decimal.MAX_SCALE, + a30.scale() <= Decimal128.MAX_SCALE, "Scale should not exceed MAX_SCALE", ) @@ -240,70 +242,70 @@ fn test_scale_handling() raises: # 31. Division by power of 10 testing.assert_equal( - String(Decimal("123.456") / Decimal(10)), + String(Decimal128("123.456") / Decimal128(10)), "12.3456", "Division by power of 10", ) # 32. Division by 0.1 (multiply by 10) testing.assert_equal( - String(Decimal("123.456") / Decimal("0.1")), + String(Decimal128("123.456") / Decimal128("0.1")), "1234.56", "Division by 0.1", ) # 33. Division by 0.01 (multiply by 100) testing.assert_equal( - String(Decimal("123.456") / Decimal("0.01")), + String(Decimal128("123.456") / Decimal128("0.01")), "12345.6", "Division by 0.01", ) # 34. Division by 100 (divide by 100) testing.assert_equal( - String(Decimal("123.456") / Decimal(100)), + String(Decimal128("123.456") / Decimal128(100)), "1.23456", "Division by 100", ) # 35. Division resulting in loss of trailing zeros testing.assert_equal( - String(Decimal("10.000") / Decimal(2)), + String(Decimal128("10.000") / Decimal128(2)), "5.000", "Division resulting in loss of trailing zeros", ) # 36. Division where quotient needs more decimal places testing.assert_equal( - String(Decimal(1) / Decimal(8)), + String(Decimal128(1) / Decimal128(8)), "0.125", "Division where quotient needs more decimal places", ) # 37. Division where dividend has more scale than divisor testing.assert_equal( - String(Decimal("0.01") / Decimal(2)), + String(Decimal128("0.01") / Decimal128(2)), "0.005", "Division where dividend has more scale than divisor", ) # 38. Division where divisor has more scale than dividend testing.assert_equal( - String(Decimal(2) / Decimal("0.01")), + String(Decimal128(2) / Decimal128("0.01")), "200", "Division where divisor has more scale than dividend", ) # 39. Division where both have high scale and result needs less testing.assert_equal( - String(Decimal("0.0001") / Decimal("0.0001")), + String(Decimal128("0.0001") / Decimal128("0.0001")), "1", "Division where both have high scale and result needs less", ) # 40. Division where both have high scale and result needs more testing.assert_equal( - String(Decimal("0.0001") / Decimal("0.0003")), + String(Decimal128("0.0001") / Decimal128("0.0003")), "0.3333333333333333333333333333", "Division where both have high scale and result needs more", ) @@ -316,23 +318,27 @@ fn test_edge_cases() raises: print("Testing division edge cases...") # 41. Division by very small number close to zero - var a41 = Decimal(1) / Decimal("0." + "0" * 27 + "1") # Dividing by 10^-28 + var a41 = Decimal128(1) / Decimal128( + "0." + "0" * 27 + "1" + ) # Dividing by 10^-28 testing.assert_true( - a41 > Decimal(String("1" + "0" * 27)), + a41 > Decimal128(String("1" + "0" * 27)), "Case 41: Division by very small number failed", ) # 42. Division resulting in a number close to zero - var a42 = Decimal("0." + "0" * 27 + "1") / Decimal(10) # Very small / 10 + var a42 = Decimal128("0." + "0" * 27 + "1") / Decimal128( + 10 + ) # Very small / 10 testing.assert_equal( a42, - Decimal("0." + "0" * 28), + Decimal128("0." + "0" * 28), "Case 42: Division resulting in number close to zero failed", ) # 43. Division of very large number by very small number - var max_decimal = Decimal.MAX() - var small_divisor = Decimal("0.0001") + var max_decimal = Decimal128.MAX() + var small_divisor = Decimal128("0.0001") try: var _a43 = max_decimal / small_divisor except: @@ -342,47 +348,47 @@ fn test_edge_cases() raises: ) # 44. Division of minimum representable positive number - var min_positive = Decimal( + var min_positive = Decimal128( "0." + "0" * 27 + "1" ) # Smallest positive decimal - var a44 = min_positive / Decimal(2) - testing.assert_true(a44.scale() <= Decimal.MAX_SCALE) + var a44 = min_positive / Decimal128(2) + testing.assert_true(a44.scale() <= Decimal128.MAX_SCALE) # 45. Division by power of 2 (binary divisions) testing.assert_equal( - String(Decimal(1) / Decimal(4)), "0.25", "Division by power of 2" + String(Decimal128(1) / Decimal128(4)), "0.25", "Division by power of 2" ) # 46. Division by 9's testing.assert_equal( - String(Decimal(100) / Decimal("9.9")), + String(Decimal128(100) / Decimal128("9.9")), "10.101010101010101010101010101", "Division by 9's", ) # 47. Division resulting in exactly MAX_SCALE digits - var a47 = Decimal(1) / Decimal(3) + var a47 = Decimal128(1) / Decimal128(3) testing.assert_true( - a47.scale() == Decimal.MAX_SCALE, + a47.scale() == Decimal128.MAX_SCALE, "Case 47: Division resulting in exactly MAX_SCALE digits failed", ) # 48. Division of large integers resulting in max precision testing.assert_equal( - String(Decimal(9876543210) / Decimal(123456789)), + String(Decimal128(9876543210) / Decimal128(123456789)), "80.00000072900000663390006037", "Division of large integers resulting in max precision", ) # 49. Division of zero by one (edge case) testing.assert_equal( - String(Decimal(0) / Decimal(1)), "0", "Division of zero by one" + String(Decimal128(0) / Decimal128(1)), "0", "Division of zero by one" ) # 50. Division with value at maximum supported scale - var a50 = Decimal("0." + "0" * 27 + "5") / Decimal(1) + var a50 = Decimal128("0." + "0" * 27 + "5") / Decimal128(1) testing.assert_true( - a50.scale() <= Decimal.MAX_SCALE, + a50.scale() <= Decimal128.MAX_SCALE, "Case 50: Division with value at maximum supported scale failed", ) @@ -395,14 +401,14 @@ fn test_large_numbers() raises: # 51. Division of large number that results in small number testing.assert_equal( - String(Decimal("1" + "0" * 20) / Decimal("1" + "0" * 20)), + String(Decimal128("1" + "0" * 20) / Decimal128("1" + "0" * 20)), "1", "Division of large number that results in small number", ) # 52. Division where dividend is at max capacity - var max_value = Decimal.MAX() - var a52 = max_value / Decimal(1) + var max_value = Decimal128.MAX() + var a52 = max_value / Decimal128(1) testing.assert_equal( a52, max_value, @@ -410,61 +416,61 @@ fn test_large_numbers() raises: ) # 53. Division where dividend is slightly below max - var near_max = Decimal.MAX() - Decimal(1) - var a53 = near_max / Decimal(10) - testing.assert_equal(a53, Decimal("7922816251426433759354395033.4")) + var near_max = Decimal128.MAX() - Decimal128(1) + var a53 = near_max / Decimal128(10) + testing.assert_equal(a53, Decimal128("7922816251426433759354395033.4")) # 54. Division where result approaches max - var large_num = Decimal.MAX() / Decimal(3) - var a54 = large_num * Decimal(3) + var large_num = Decimal128.MAX() / Decimal128(3) + var a54 = large_num * Decimal128(3) testing.assert_true( - a54 <= Decimal.MAX(), + a54 <= Decimal128.MAX(), "Case 54: Division where result approaches max failed", ) # 55. Large negative divided by large positive - var large_neg = -Decimal(String("1" + "0" * 15)) - var a55 = large_neg / Decimal(10000) + var large_neg = -Decimal128(String("1" + "0" * 15)) + var a55 = large_neg / Decimal128(10000) testing.assert_equal( a55, - -Decimal(String("1" + "0" * 11)), + -Decimal128(String("1" + "0" * 11)), "Case 55: Large negative divided by large positive failed", ) # 56. Large integer division with remainder testing.assert_equal( - String(Decimal("12345678901234567890") / Decimal(9876543210)), + String(Decimal128("12345678901234567890") / Decimal128(9876543210)), "1249999988.7343749990033203125", "Large integer division with many digits", ) # 57. Large numbers with exact division testing.assert_equal( - String(Decimal("9" * 28) / Decimal("9" * 14)), - String(Decimal("100000000000001")), + String(Decimal128("9" * 28) / Decimal128("9" * 14)), + String(Decimal128("100000000000001")), "Large numbers with exact division", ) # 58. Division of large numbers with same leading digits - var a58 = Decimal("123" + "0" * 25) / Decimal("123" + "0" * 15) + var a58 = Decimal128("123" + "0" * 25) / Decimal128("123" + "0" * 15) testing.assert_equal( a58, - Decimal("1" + "0" * 10), + Decimal128("1" + "0" * 10), "Case 58: Division of large numbers with same leading digits failed", ) # 59. Large numbers with different signs - var a59 = Decimal("9" * 28) / Decimal("-" + "9" * 14) + var a59 = Decimal128("9" * 28) / Decimal128("-" + "9" * 14) testing.assert_equal( a59, - -Decimal("100000000000001"), + -Decimal128("100000000000001"), "Case 59: Large numbers with different signs failed", ) # 60. Division near maximum representable value try: - var a60 = Decimal.MAX() / Decimal("0.5") - testing.assert_true(a60 <= Decimal.MAX()) + var a60 = Decimal128.MAX() / Decimal128("0.5") + testing.assert_true(a60 <= Decimal128.MAX()) except: print("Division overflows") @@ -476,74 +482,74 @@ fn test_special_cases() raises: # 61. Identical numbers should give 1 testing.assert_equal( - String(Decimal("123.456") / Decimal("123.456")), + String(Decimal128("123.456") / Decimal128("123.456")), "1", "Identical numbers should give 1", ) # 62. Division by 0.1 power for decimal shift testing.assert_equal( - String(Decimal("1.234") / Decimal("0.001")), + String(Decimal128("1.234") / Decimal128("0.001")), "1234", "Division by 0.1 power for decimal shift", ) # 63. Division that normalizes out trailing zeros testing.assert_equal( - Decimal("1.000") / Decimal("1.000"), - Decimal(1), + Decimal128("1.000") / Decimal128("1.000"), + Decimal128(1), "Case 63: Division that normalizes out trailing zeros failed", ) # 64. Division by 1 should leave number unchanged - var special_value = Decimal("123.456789012345678901234567") + var special_value = Decimal128("123.456789012345678901234567") testing.assert_equal( - special_value / Decimal(1), + special_value / Decimal128(1), special_value, "Case 64: Division by 1 should leave number unchanged failed", ) # 65. Division by self should be 1 for non-zero testing.assert_equal( - Decimal("0.000123") / Decimal("0.000123"), - Decimal(1), + Decimal128("0.000123") / Decimal128("0.000123"), + Decimal128(1), "Case 65: Division by self should be 1 for non-zero failed", ) # 66. Division of 1 by numbers close to 1 testing.assert_equal( - Decimal(1) / Decimal("0.999999"), - Decimal("1.000001000001000001000001000"), + Decimal128(1) / Decimal128("0.999999"), + Decimal128("1.000001000001000001000001000"), "Case 66: Division of 1 by numbers close to 1 failed", ) # 67. Series of divisions that should cancel out - var value = Decimal("123.456") - var divided = value / Decimal(7) - var result = divided * Decimal(7) + var value = Decimal128("123.456") + var divided = value / Decimal128(7) + var result = divided * Decimal128(7) testing.assert_true( - abs(value - result) / value < Decimal("0.0001"), + abs(value - result) / value < Decimal128("0.0001"), "Case 67: Series of divisions that should cancel out failed", ) # 68. Division by fractional power of 10 testing.assert_equal( - String(Decimal("5.5") / Decimal("0.055")), + String(Decimal128("5.5") / Decimal128("0.055")), "100", "Division by fractional power of 10", ) # 69. Division causing exact shift in magnitude testing.assert_equal( - String(Decimal(1) / Decimal(1000)), + String(Decimal128(1) / Decimal128(1000)), "0.001", "Division causing exact shift in magnitude", ) # 70. Dividing number very close to zero by one - var very_small = Decimal("0." + "0" * 27 + "1") + var very_small = Decimal128("0." + "0" * 27 + "1") testing.assert_equal( - very_small / Decimal(1), + very_small / Decimal128(1), very_small, "Case 70: Dividing number very close to zero by one failed", ) @@ -556,38 +562,38 @@ fn test_mixed_precision() raises: # 71. High precision / low precision testing.assert_equal( - String(Decimal("123.456789012345678901234567") / Decimal(2)), + String(Decimal128("123.456789012345678901234567") / Decimal128(2)), "61.7283945061728394506172835", "High precision / low precision", ) # 72. Low precision / high precision - var a72 = Decimal(1234) / Decimal("0.0000000000000000000000011") + var a72 = Decimal128(1234) / Decimal128("0.0000000000000000000000011") testing.assert_equal( a72, - Decimal("1121818181818181818181818181.8"), + Decimal128("1121818181818181818181818181.8"), "Low precision / high precision", ) # 73. Mixing high precision with power of 10 - var a73 = Decimal("0.123456789012345678901234567") / Decimal("0.1") + var a73 = Decimal128("0.123456789012345678901234567") / Decimal128("0.1") testing.assert_equal( a73, - Decimal("1.23456789012345678901234567"), + Decimal128("1.23456789012345678901234567"), "Case 73: Mixing high precision with power of 10 failed", ) # 74. Precision of result higher than either operand - var a74 = Decimal("0.1") / Decimal(3) + var a74 = Decimal128("0.1") / Decimal128(3) testing.assert_true( String(a74).startswith("0.0333333333333333"), "Case 74: Precision of result higher than either operand failed", ) # 75. Division where divisor has higher precision than dividend - var a75 = Decimal(1) / Decimal("0.0001234567890123456789") + var a75 = Decimal128(1) / Decimal128("0.0001234567890123456789") testing.assert_true( - a75 > Decimal(8000), + a75 > Decimal128(8000), ( "Case 75: Division where divisor has higher precision than dividend" " failed" @@ -595,37 +601,39 @@ fn test_mixed_precision() raises: ) # 76. Division where precision shifts dramatically - var a76 = Decimal("0.000000001") / Decimal("0.000000000001") + var a76 = Decimal128("0.000000001") / Decimal128("0.000000000001") testing.assert_equal( a76, - Decimal(1000), + Decimal128(1000), "Case 76: Division where precision shifts dramatically failed", ) # 77. Mixing different but high precision values - var a77 = Decimal("0.12345678901234567") / Decimal("0.98765432109876543") + var a77 = Decimal128("0.12345678901234567") / Decimal128( + "0.98765432109876543" + ) testing.assert_true( - a77 < Decimal("0.13"), + a77 < Decimal128("0.13"), "Case 77: Mixing different but high precision values failed", ) # 78. Very different scales that result in exact division testing.assert_equal( - String(Decimal("0.0000004") / Decimal("0.0002")), + String(Decimal128("0.0000004") / Decimal128("0.0002")), "0.002", "Very different scales that result in exact division", ) # 79. Maximum precision divided by maximum precision testing.assert_equal( - String(Decimal("0." + "9" * 28) / Decimal("0." + "3" * 28)), + String(Decimal128("0." + "9" * 28) / Decimal128("0." + "3" * 28)), "3", "Maximum precision divided by maximum precision", ) # 80. Many trailing zeros in result testing.assert_equal( - String(Decimal("2.000") / Decimal("0.001")), + String(Decimal128("2.000") / Decimal128("0.001")), "2000", "Many trailing zeros in result", ) @@ -638,22 +646,24 @@ fn test_rounding_behavior() raises: print("Testing division rounding behavior...") # 81. Banker's rounding at boundary (round to even) - var a81 = Decimal(1) / Decimal(String("3" + "0" * (Decimal.MAX_SCALE - 1))) - var expected = "0." + "0" * (Decimal.MAX_SCALE - 1) + "3" + var a81 = Decimal128(1) / Decimal128( + String("3" + "0" * (Decimal128.MAX_SCALE - 1)) + ) + var expected = "0." + "0" * (Decimal128.MAX_SCALE - 1) + "3" testing.assert_equal( String(a81), expected, "Case 81: Banker's rounding at boundary failed" ) # 82. Banker's rounding up at precision limit - var a82 = Decimal(5) / Decimal(9) # ~0.55555... - var b82 = Decimal("0." + "5" * 27 + "6") # 0.5555...6 + var a82 = Decimal128(5) / Decimal128(9) # ~0.55555... + var b82 = Decimal128("0." + "5" * 27 + "6") # 0.5555...6 testing.assert_equal( a82, b82, "Case 82: Banker's rounding up at precision limit failed" ) # 83. Rounding that requires carry propagation - var a83 = Decimal(1) / Decimal("1.9999999999999999999999999") - var expected83 = Decimal("0.5000000000000000000000000250") + var a83 = Decimal128(1) / Decimal128("1.9999999999999999999999999") + var expected83 = Decimal128("0.5000000000000000000000000250") testing.assert_equal( a83, expected83, @@ -661,8 +671,8 @@ fn test_rounding_behavior() raises: ) # 84. Division that results in exactly half a unit in last place - var a84 = Decimal(1) / Decimal("4" + "0" * Decimal.MAX_SCALE) - var expected84 = Decimal("0." + "0" * (Decimal.MAX_SCALE)) + var a84 = Decimal128(1) / Decimal128("4" + "0" * Decimal128.MAX_SCALE) + var expected84 = Decimal128("0." + "0" * (Decimal128.MAX_SCALE)) testing.assert_equal( a84, expected84, @@ -673,8 +683,8 @@ fn test_rounding_behavior() raises: ) # 85. Rounding stress test: 1/7 at different precisions - var a85 = Decimal(1) / Decimal(7) - var expected85 = Decimal( + var a85 = Decimal128(1) / Decimal128(7) + var expected85 = Decimal128( "0.1428571428571428571428571429" ) # 28 decimal places testing.assert_equal( @@ -685,35 +695,35 @@ fn test_rounding_behavior() raises: # 86. Division at the edge testing.assert_equal( - String(Decimal("9.999999999999999999999999999") / Decimal(10)), + String(Decimal128("9.999999999999999999999999999") / Decimal128(10)), "0.9999999999999999999999999999", "Division at the edge", ) # 87. Division requiring rounding to even at last digit testing.assert_equal( - String(Decimal("1.25") / Decimal("0.5")), + String(Decimal128("1.25") / Decimal128("0.5")), "2.5", "Division requiring rounding to even at last digit", ) # 88. Half-even rounding with even digit before testing.assert_equal( - String(Decimal("24.5") / Decimal(10)), + String(Decimal128("24.5") / Decimal128(10)), "2.45", "Testing half-even rounding with even digit before", ) # 89. Half-even rounding with odd digit before testing.assert_equal( - String(Decimal("25.5") / Decimal(10)), + String(Decimal128("25.5") / Decimal128(10)), "2.55", "Testing half-even rounding with odd digit before", ) # 90. Division with MAX_SCALE-3 digits # 1 / 300000000000000000000000000 (26 zeros) - var a90 = Decimal(1) / Decimal(String("300000000000000000000000000")) + var a90 = Decimal128(1) / Decimal128(String("300000000000000000000000000")) testing.assert_equal( String(a90), "0.0000000000000000000000000033", @@ -729,7 +739,7 @@ fn test_error_cases() raises: # 91. Division by zero try: - var _result = Decimal(123) / Decimal(0) + var _result = Decimal128(123) / Decimal128(0) testing.assert_true( False, "Case 91: Expected division by zero to raise exception" ) @@ -741,8 +751,8 @@ fn test_error_cases() raises: # 92. Division with overflow potential # This is intended to test if the implementation can avoid overflow # by handling the operation algebraically before doing actual division - var large1 = Decimal.MAX() - var large2 = Decimal("0.5") + var large1 = Decimal128.MAX() + var large2 = Decimal128("0.5") try: var result92 = large1 / large2 testing.assert_true(result92 > large1) @@ -751,21 +761,21 @@ fn test_error_cases() raises: # 93. Division of maximum possible value try: - var result93 = Decimal.MAX() / Decimal("0.1") - testing.assert_true(result93 > Decimal.MAX()) + var result93 = Decimal128.MAX() / Decimal128("0.1") + testing.assert_true(result93 > Decimal128.MAX()) except: print("Overflow detected (acceptable)") # 94. Division of minimum possible value - var result94 = Decimal.MIN() / Decimal("10.12345") + var result94 = Decimal128.MIN() / Decimal128("10.12345") testing.assert_equal( result94, - Decimal("-7826201790324873199704048554.1"), + Decimal128("-7826201790324873199704048554.1"), "Case 94: Division of minimum possible value failed", ) # 95. Division of very small by very large (approaching underflow) - var result95 = Decimal("0." + "0" * 27 + "1") / Decimal.MAX() + var result95 = Decimal128("0." + "0" * 27 + "1") / Decimal128.MAX() testing.assert_equal( String(result95), "0.0000000000000000000000000000", @@ -774,14 +784,14 @@ fn test_error_cases() raises: # 96. Division of maximum by minimum value testing.assert_equal( - String(Decimal.MAX() / Decimal.MIN()), + String(Decimal128.MAX() / Decimal128.MIN()), "-1", "Division of maximum by minimum value", ) # 97. Division with potential for intermediate overflow testing.assert_equal( - String(Decimal("1" + "0" * 20) / Decimal("1" + "0" * 20)), + String(Decimal128("1" + "0" * 20) / Decimal128("1" + "0" * 20)), "1", "Division with potential for intermediate overflow", ) @@ -789,13 +799,13 @@ fn test_error_cases() raises: # 98. Division resulting in value greater than representable max try: # This may either return MAX or raise an error depending on implementation - var result = Decimal.MAX() / Decimal("0.00001") - testing.assert_true(result >= Decimal.MAX()) + var result = Decimal128.MAX() / Decimal128("0.00001") + testing.assert_true(result >= Decimal128.MAX()) except: testing.assert_true(True, "Overflow detected (acceptable)") # 99. Multiple operations that could cause cumulative error - var calc = (Decimal(1) / Decimal(3)) * Decimal(3) + var calc = (Decimal128(1) / Decimal128(3)) * Decimal128(3) testing.assert_equal( String(calc), "0.9999999999999999999999999999", @@ -804,7 +814,9 @@ fn test_error_cases() raises: # 100. Division at the exact boundary of precision limit # 1 / 70000000000000000000000000000 (28 zeros) - var a100 = Decimal(1) / Decimal(String("7" + "0" * Decimal.MAX_SCALE)) + var a100 = Decimal128(1) / Decimal128( + String("7" + "0" * Decimal128.MAX_SCALE) + ) testing.assert_equal( String(a100), "0.0000000000000000000000000000", @@ -815,7 +827,7 @@ fn test_error_cases() raises: fn main() raises: - print("\n=== Running Comprehensive Decimal Division Tests ===\n") + print("\n=== Running Comprehensive Decimal128 Division Tests ===\n") # Run all test groups test_basic_division() diff --git a/tests/decimal/test_decimal_exp.mojo b/tests/decimal128/test_decimal128_exp.mojo similarity index 88% rename from tests/decimal/test_decimal_exp.mojo rename to tests/decimal128/test_decimal128_exp.mojo index 0a8bb2a..3e65ec3 100644 --- a/tests/decimal/test_decimal_exp.mojo +++ b/tests/decimal128/test_decimal128_exp.mojo @@ -5,8 +5,8 @@ and edge cases to ensure proper calculation of e^x. """ import testing -from decimojo.prelude import dm, Decimal, RoundingMode -from decimojo.decimal.exponential import exp +from decimojo.prelude import dm, Decimal128, RoundingMode +from decimojo.decimal128.exponential import exp fn test_basic_exp_values() raises: @@ -14,14 +14,14 @@ fn test_basic_exp_values() raises: print("Testing basic exponential values...") # Test case 1: e^0 = 1 - var zero = Decimal(String("0")) + var zero = Decimal128(String("0")) var result0 = exp(zero) testing.assert_equal( String(result0), String("1"), "e^0 should be 1, got " + String(result0) ) # Test case 2: e^1 should be close to Euler's number - var one = Decimal(String("1")) + var one = Decimal128(String("1")) var result1 = exp(one) var expected1 = String( "2.718281828459045235360287471" @@ -35,7 +35,7 @@ fn test_basic_exp_values() raises: ) # Test case 3: e^2 - var two = Decimal(String("2")) + var two = Decimal128(String("2")) var result2 = exp(two) var expected2 = String( "7.389056098930650227230427461" @@ -49,7 +49,7 @@ fn test_basic_exp_values() raises: ) # Test case 4: e^3 - var three = Decimal(String("3")) + var three = Decimal128(String("3")) var result3 = exp(three) var expected3 = String( "20.08553692318766774092852965" @@ -63,7 +63,7 @@ fn test_basic_exp_values() raises: ) # Test case 5: e^5 - var five = Decimal(String("5")) + var five = Decimal128(String("5")) var result5 = exp(five) var expected5 = String( "148.41315910257660342111558004055" @@ -84,7 +84,7 @@ fn test_negative_exponents() raises: print("Testing exponential function with negative exponents...") # Test case 1: e^(-1) = 1/e - var neg_one = Decimal(String("-1")) + var neg_one = Decimal128(String("-1")) var result1 = exp(neg_one) var expected1 = String( "0.3678794411714423215955237702" @@ -98,7 +98,7 @@ fn test_negative_exponents() raises: ) # Test case 2: e^(-2) = 1/e^2 - var neg_two = Decimal(String("-2")) + var neg_two = Decimal128(String("-2")) var result2 = exp(neg_two) var expected2 = String( "0.1353352832366126918939994950" @@ -112,7 +112,7 @@ fn test_negative_exponents() raises: ) # Test case 3: e^(-5) - var neg_five = Decimal(String("-5")) + var neg_five = Decimal128(String("-5")) var result3 = exp(neg_five) var expected3 = String( "0.006737946999085467096636048777" @@ -133,7 +133,7 @@ fn test_fractional_exponents() raises: print("Testing exponential function with fractional exponents...") # Test case 1: e^0.5 - var half = Decimal(String("0.5")) + var half = Decimal128(String("0.5")) var result1 = exp(half) var expected1 = String( "1.648721270700128146848650787" @@ -147,7 +147,7 @@ fn test_fractional_exponents() raises: ) # Test case 2: e^0.1 - var tenth = Decimal(String("0.1")) + var tenth = Decimal128(String("0.1")) var result2 = exp(tenth) var expected2 = String( "1.105170918075647624811707826" @@ -161,7 +161,7 @@ fn test_fractional_exponents() raises: ) # Test case 3: e^(-0.5) - var neg_half = Decimal(String("-0.5")) + var neg_half = Decimal128(String("-0.5")) var result3 = exp(neg_half) var expected3 = String( "0.6065306597126334236037995349" @@ -175,7 +175,7 @@ fn test_fractional_exponents() raises: ) # Test case 4: e^1.5 - var one_half = Decimal(String("1.5")) + var one_half = Decimal128(String("1.5")) var result4 = exp(one_half) var expected4 = String( "4.481689070338064822602055460" @@ -196,7 +196,7 @@ fn test_high_precision_exponents() raises: print("Testing exponential function with high precision inputs...") # Test case 1: e^π (approximate) - var pi = Decimal(String("3.14159265358979323846264338327950288")) + var pi = Decimal128(String("3.14159265358979323846264338327950288")) var result1 = exp(pi) var expected1 = String( "23.14069263277926900572908636794" @@ -210,7 +210,7 @@ fn test_high_precision_exponents() raises: ) # Test case 2: e^2.71828 (approximate e) - var approx_e = Decimal(String("2.71828")) + var approx_e = Decimal128(String("2.71828")) var result2 = exp(approx_e) var expected2 = String( "15.154234532556727211057207398340" @@ -231,8 +231,8 @@ fn test_mathematical_identities() raises: print("Testing mathematical identities for exponential function...") # Test case 1: e^(a+b) = e^a * e^b - var a = Decimal(String("2")) - var b = Decimal(String("3")) + var a = Decimal128(String("2")) + var b = Decimal128(String("3")) var exp_a_plus_b = exp(a + b) var exp_a_times_exp_b = exp(a) * exp(b) @@ -240,27 +240,27 @@ fn test_mathematical_identities() raises: var diff1 = abs(exp_a_plus_b - exp_a_times_exp_b) var rel_diff1 = diff1 / exp_a_plus_b testing.assert_true( - rel_diff1 < Decimal(String("0.0000001")), + rel_diff1 < Decimal128(String("0.0000001")), "e^(a+b) should equal e^a * e^b within tolerance, difference: " + String(rel_diff1), ) # Test case 2: e^(-x) = 1/e^x - var x = Decimal(String("1.5")) + var x = Decimal128(String("1.5")) var exp_neg_x = exp(-x) - var one_over_exp_x = Decimal(String("1")) / exp(x) + var one_over_exp_x = Decimal128(String("1")) / exp(x) # Compare with some level of precision var diff2 = abs(exp_neg_x - one_over_exp_x) var rel_diff2 = diff2 / exp_neg_x testing.assert_true( - rel_diff2 < Decimal(String("0.0000001")), + rel_diff2 < Decimal128(String("0.0000001")), "e^(-x) should equal 1/e^x within tolerance, difference: " + String(rel_diff2), ) # Test case 3: e^0 = 1 (Already tested in basic values, but included here for completeness) - var zero = Decimal(String("0")) + var zero = Decimal128(String("0")) var exp_zero = exp(zero) testing.assert_equal(String(exp_zero), String("1"), "e^0 should equal 1") @@ -272,7 +272,7 @@ fn test_extreme_values() raises: print("Testing exponential function with extreme values...") # Test case 1: Very small positive input - var small_input = Decimal(String("0.0000001")) + var small_input = Decimal128(String("0.0000001")) var result1 = exp(small_input) testing.assert_true( String(result1).startswith(String("1.0000001")), @@ -280,7 +280,7 @@ fn test_extreme_values() raises: ) # Test case 2: Very small negative input - var small_neg_input = Decimal(String("-0.0000001")) + var small_neg_input = Decimal128(String("-0.0000001")) var result2 = exp(small_neg_input) testing.assert_true( String(result2).startswith(String("0.9999999")), @@ -291,10 +291,10 @@ fn test_extreme_values() raises: # Test case 3: Large positive value # This should not overflow but should produce a very large result # Note: The implementation may have specific limits - var large_input = Decimal(String("20")) + var large_input = Decimal128(String("20")) var result3 = exp(large_input) testing.assert_true( - result3 > Decimal(String("100000000")), + result3 > Decimal128(String("100000000")), "e^20 should be a very large number > 100,000,000, got " + String(result3), ) @@ -307,7 +307,7 @@ fn test_edge_cases() raises: print("Testing edge cases for exponential function...") # Test with very high precision input - var high_precision = Decimal(String("1.23456789012345678901234567")) + var high_precision = Decimal128(String("1.23456789012345678901234567")) var result_high = exp(high_precision) testing.assert_true( len(String(result_high)) > 15, diff --git a/tests/decimal/test_decimal_factorial.mojo b/tests/decimal128/test_decimal128_factorial.mojo similarity index 95% rename from tests/decimal/test_decimal_factorial.mojo rename to tests/decimal128/test_decimal128_factorial.mojo index aae3c55..eba5bec 100644 --- a/tests/decimal/test_decimal_factorial.mojo +++ b/tests/decimal128/test_decimal128_factorial.mojo @@ -2,12 +2,12 @@ Comprehensive tests for the `factorial()` and the `factorial_reciprocal()` functions in the DeciMojo library. Tests various cases including edge cases and error handling for factorials -in the range 0 to 27, which is the maximum range supported by Decimal. +in the range 0 to 27, which is the maximum range supported by Decimal128. """ import testing -from decimojo.prelude import dm, Decimal, RoundingMode -from decimojo.decimal.special import factorial, factorial_reciprocal +from decimojo.prelude import dm, Decimal128, RoundingMode +from decimojo.decimal128.special import factorial, factorial_reciprocal fn test_basic_factorials() raises: @@ -152,7 +152,7 @@ fn test_factorial_properties() raises: for n in range(0, 26): var n_fact = factorial(n) var n_plus_1_fact = factorial(n + 1) - var calculated = n_fact * Decimal(String(n + 1)) + var calculated = n_fact * Decimal128(String(n + 1)) testing.assert_equal( String(n_plus_1_fact), String(calculated), @@ -231,7 +231,7 @@ fn test_factorial_reciprocal() raises: # Test for all values in the supported range (0-27) var all_equal = True for i in range(28): - var a = Decimal(1) / factorial(i) + var a = Decimal128(1) / factorial(i) var b = factorial_reciprocal(i) var equal = a == b @@ -244,8 +244,8 @@ fn test_factorial_reciprocal() raises: testing.assert_true( all_equal, ( - "factorial_reciprocal(n) should equal Decimal(1)/factorial(n) for" - " all n" + "factorial_reciprocal(n) should equal Decimal128(1)/factorial(n)" + " for all n" ), ) diff --git a/tests/decimal/test_decimal_from_components.mojo b/tests/decimal128/test_decimal128_from_components.mojo similarity index 73% rename from tests/decimal/test_decimal_from_components.mojo rename to tests/decimal128/test_decimal128_from_components.mojo index 873d90a..91bb04a 100644 --- a/tests/decimal/test_decimal_from_components.mojo +++ b/tests/decimal128/test_decimal128_from_components.mojo @@ -1,50 +1,50 @@ """ -Test Decimal creation from integer, float, or string values. +Test Decimal128 creation from integer, float, or string values. """ # TODO: Split into separate test files for each type of constructor -from decimojo.prelude import dm, Decimal, RoundingMode +from decimojo.prelude import dm, Decimal128, RoundingMode import testing fn test_decimal_from_components() raises: print("------------------------------------------------------") - print("Testing Decimal Creation from Components") + print("Testing Decimal128 Creation from Components") # Test case 1: Zero with zero scale - var zero = Decimal(0, 0, 0, 0, False) + var zero = Decimal128(0, 0, 0, 0, False) testing.assert_equal(String(zero), "0", "Zero with scale 0") # Test case 2: One with zero scale - var one = Decimal(1, 0, 0, 0, False) + var one = Decimal128(1, 0, 0, 0, False) testing.assert_equal(String(one), "1", "One with scale 0") # Test case 3: Negative one - var neg_one = Decimal(1, 0, 0, 0, True) + var neg_one = Decimal128(1, 0, 0, 0, True) testing.assert_equal(String(neg_one), "-1", "Negative one") # Test case 4: Simple number with scale - var with_scale = Decimal(12345, 0, 0, 2, False) + var with_scale = Decimal128(12345, 0, 0, 2, False) testing.assert_equal( String(with_scale), "123.45", "Simple number with scale 2" ) # Test case 5: Negative number with scale - var neg_with_scale = Decimal(12345, 0, 0, 2, True) + var neg_with_scale = Decimal128(12345, 0, 0, 2, True) testing.assert_equal( String(neg_with_scale), "-123.45", "Negative number with scale 2" ) # Test case 6: Larger number using mid - var large = Decimal(0xFFFFFFFF, 5, 0, 0, False) - var expected_large = Decimal(String(0xFFFFFFFF + 5 * 4294967296)) + var large = Decimal128(0xFFFFFFFF, 5, 0, 0, False) + var expected_large = Decimal128(String(0xFFFFFFFF + 5 * 4294967296)) testing.assert_equal( String(large), String(expected_large), "Large number using mid field" ) # Test case 7: Verify scale is correctly stored - var high_scale = Decimal(123, 0, 0, 10, False) + var high_scale = Decimal128(123, 0, 0, 10, False) testing.assert_equal( high_scale.scale(), 10, "Scale should be correctly stored" ) @@ -53,7 +53,7 @@ fn test_decimal_from_components() raises: ) # Test case 8: Test large scale with negative number - var neg_high_scale = Decimal(123, 0, 0, 10, True) + var neg_high_scale = Decimal128(123, 0, 0, 10, True) testing.assert_equal( String(neg_high_scale), "-0.0000000123", @@ -70,7 +70,7 @@ fn test_decimal_from_components() raises: ) # Test case 10: With high component - var with_high = Decimal(0, 0, 3, 0, False) + var with_high = Decimal128(0, 0, 3, 0, False) testing.assert_equal( String(with_high), "55340232221128654848", @@ -78,12 +78,12 @@ fn test_decimal_from_components() raises: ) # Test case 11: Maximum possible scale - var max_scale = Decimal(123, 0, 0, 28, False) + var max_scale = Decimal128(123, 0, 0, 28, False) testing.assert_equal(max_scale.scale(), 28, "Maximum scale should be 28") # Test case 12: Overflow scale protection try: - var _overflow_scale = Decimal(123, 0, 0, 100, False) + var _overflow_scale = Decimal128(123, 0, 0, 100, False) except: print("Successfully caught overflow scale error") diff --git a/tests/decimal/test_decimal_from_float.mojo b/tests/decimal128/test_decimal128_from_float.mojo similarity index 70% rename from tests/decimal/test_decimal_from_float.mojo rename to tests/decimal128/test_decimal128_from_float.mojo index 9bec2f7..4e93398 100644 --- a/tests/decimal/test_decimal_from_float.mojo +++ b/tests/decimal128/test_decimal128_from_float.mojo @@ -1,5 +1,5 @@ """ -Comprehensive tests for the Decimal.from_float() constructor method. +Comprehensive tests for the Decimal128.from_float() constructor method. Tests 50 different cases to ensure proper conversion from Float64 values. Note: Comparisons are based on the expected precision of the input value rather than expecting exact decimal representation for all digits. @@ -9,7 +9,7 @@ import testing from math import nan, inf from python import Python -from decimojo.prelude import dm, Decimal, RoundingMode +from decimojo.prelude import dm, Decimal128, RoundingMode fn test_simple_integers() raises: @@ -17,33 +17,35 @@ fn test_simple_integers() raises: print("Testing simple integer float conversions...") # Test case 1: Zero - var zero = Decimal.from_float(0.0) + var zero = Decimal128.from_float(0.0) testing.assert_equal( - String(zero), "0", "Float 0.0 should convert to Decimal 0" + String(zero), "0", "Float 0.0 should convert to Decimal128 0" ) # Test case 2: One - var one = Decimal.from_float(1.0) + var one = Decimal128.from_float(1.0) testing.assert_equal( - String(one), "1", "Float 1.0 should convert to Decimal 1" + String(one), "1", "Float 1.0 should convert to Decimal128 1" ) # Test case 3: Ten - var ten = Decimal.from_float(10.0) + var ten = Decimal128.from_float(10.0) testing.assert_equal( - String(ten), "10", "Float 10.0 should convert to Decimal 10" + String(ten), "10", "Float 10.0 should convert to Decimal128 10" ) # Test case 4: Hundred - var hundred = Decimal.from_float(100.0) + var hundred = Decimal128.from_float(100.0) testing.assert_equal( - String(hundred), "100", "Float 100.0 should convert to Decimal 100" + String(hundred), "100", "Float 100.0 should convert to Decimal128 100" ) # Test case 5: Thousand - var thousand = Decimal.from_float(1000.0) + var thousand = Decimal128.from_float(1000.0) testing.assert_equal( - String(thousand), "1000", "Float 1000.0 should convert to Decimal 1000" + String(thousand), + "1000", + "Float 1000.0 should convert to Decimal128 1000", ) print("✓ Simple integer tests passed") @@ -54,35 +56,35 @@ fn test_simple_decimals() raises: print("Testing simple decimal float conversions...") # Test case 6: 0.5 (exact representation) - var half = Decimal.from_float(0.5) + var half = Decimal128.from_float(0.5) testing.assert_equal( - String(half), "0.5", "Float 0.5 should convert to Decimal 0.5" + String(half), "0.5", "Float 0.5 should convert to Decimal128 0.5" ) # Test case 7: 0.25 (exact representation) - var quarter = Decimal.from_float(0.25) + var quarter = Decimal128.from_float(0.25) testing.assert_equal( - String(quarter), "0.25", "Float 0.25 should convert to Decimal 0.25" + String(quarter), "0.25", "Float 0.25 should convert to Decimal128 0.25" ) # Test case 8: 1.5 (exact representation) - var one_half = Decimal.from_float(1.5) + var one_half = Decimal128.from_float(1.5) testing.assert_equal( - String(one_half), "1.5", "Float 1.5 should convert to Decimal 1.5" + String(one_half), "1.5", "Float 1.5 should convert to Decimal128 1.5" ) # Test case 9: 3.14 (check first 3 chars) - var pi_approx = Decimal.from_float(3.14) + var pi_approx = Decimal128.from_float(3.14) testing.assert_true( String(pi_approx).startswith("3.14"), - "Float 3.14 should convert to a Decimal starting with 3.14", + "Float 3.14 should convert to a Decimal128 starting with 3.14", ) # Test case 10: 2.71828 (check first 6 chars) - var e_approx = Decimal.from_float(2.71828) + var e_approx = Decimal128.from_float(2.71828) testing.assert_true( String(e_approx).startswith("2.7182"), - "Float 2.71828 should convert to a Decimal starting with 2.7182", + "Float 2.71828 should convert to a Decimal128 starting with 2.7182", ) print("✓ Simple decimal tests passed") @@ -93,35 +95,35 @@ fn test_negative_numbers() raises: print("Testing negative float conversions...") # Test case 11: -1.0 - var neg_one = Decimal.from_float(-1.0) + var neg_one = Decimal128.from_float(-1.0) testing.assert_equal( - String(neg_one), "-1", "Float -1.0 should convert to Decimal -1" + String(neg_one), "-1", "Float -1.0 should convert to Decimal128 -1" ) # Test case 12: -0.5 - var neg_half = Decimal.from_float(-0.5) + var neg_half = Decimal128.from_float(-0.5) testing.assert_equal( - String(neg_half), "-0.5", "Float -0.5 should convert to Decimal -0.5" + String(neg_half), "-0.5", "Float -0.5 should convert to Decimal128 -0.5" ) # Test case 13: -123.456 (check first 7 chars) - var neg_decimal = Decimal.from_float(-123.456) + var neg_decimal = Decimal128.from_float(-123.456) testing.assert_true( String(neg_decimal).startswith("-123.45"), - "Float -123.456 should convert to a Decimal starting with -123.45", + "Float -123.456 should convert to a Decimal128 starting with -123.45", ) # Test case 14: -0.0 (negative zero) - var neg_zero = Decimal.from_float(-0.0) + var neg_zero = Decimal128.from_float(-0.0) testing.assert_equal( - String(neg_zero), "0", "Float -0.0 should convert to Decimal 0" + String(neg_zero), "0", "Float -0.0 should convert to Decimal128 0" ) # Test case 15: -999.999 (check first 7 chars) - var neg_nines = Decimal.from_float(-999.999) + var neg_nines = Decimal128.from_float(-999.999) testing.assert_true( String(neg_nines).startswith("-999.99"), - "Float -999.999 should convert to a Decimal starting with -999.99", + "Float -999.999 should convert to a Decimal128 starting with -999.99", ) print("✓ Negative number tests passed") @@ -132,39 +134,39 @@ fn test_very_large_numbers() raises: print("Testing very large float conversions...") # Test case 16: 1e10 - var ten_billion = Decimal.from_float(1e10) + var ten_billion = Decimal128.from_float(1e10) testing.assert_equal( String(ten_billion), "10000000000", - "Float 1e10 should convert to Decimal 10000000000", + "Float 1e10 should convert to Decimal128 10000000000", ) # Test case 17: 1e15 - var quadrillion = Decimal.from_float(1e15) + var quadrillion = Decimal128.from_float(1e15) testing.assert_equal( String(quadrillion), "1000000000000000", - "Float 1e15 should convert to Decimal 1000000000000000", + "Float 1e15 should convert to Decimal128 1000000000000000", ) # Test case 18: Max safe integer in JavaScript (2^53 - 1) - var max_safe_int = Decimal.from_float(9007199254740991.0) + var max_safe_int = Decimal128.from_float(9007199254740991.0) testing.assert_equal( String(max_safe_int), "9007199254740991", - "Float 2^53-1 should convert to exact Decimal 9007199254740991", + "Float 2^53-1 should convert to exact Decimal128 9007199254740991", ) # Test case 19: 1e20 - var hundred_quintillion = Decimal.from_float(1e20) + var hundred_quintillion = Decimal128.from_float(1e20) testing.assert_equal( String(hundred_quintillion), "100000000000000000000", - "Float 1e20 should convert to Decimal 100000000000000000000", + "Float 1e20 should convert to Decimal128 100000000000000000000", ) # Test case 20: Large number with limited precision - var large_number = Decimal.from_float(1.23456789e15) + var large_number = Decimal128.from_float(1.23456789e15) testing.assert_true( String(large_number).startswith("1234567890000000"), "Large float should convert with appropriate precision", @@ -178,21 +180,21 @@ fn test_very_small_numbers() raises: print("Testing very small float conversions...") # Test case 21: 1e-10 - var tiny = Decimal.from_float(1e-10) + var tiny = Decimal128.from_float(1e-10) testing.assert_true( String(tiny).startswith("0.00000000"), - "Float 1e-10 should convert to a Decimal with appropriate zeros", + "Float 1e-10 should convert to a Decimal128 with appropriate zeros", ) # Test case 22: 1e-15 - var tinier = Decimal.from_float(1e-15) + var tinier = Decimal128.from_float(1e-15) testing.assert_true( String(tinier).startswith("0.000000000000001"), - "Float 1e-15 should convert to a Decimal with appropriate zeros", + "Float 1e-15 should convert to a Decimal128 with appropriate zeros", ) # Test case 23: Small number with precision - var small_with_precision = Decimal.from_float(1.234e-10) + var small_with_precision = Decimal128.from_float(1.234e-10) var expected_prefix = "0.0000000001" testing.assert_true( String(small_with_precision).startswith(expected_prefix), @@ -200,17 +202,17 @@ fn test_very_small_numbers() raises: ) # Test case 24: Very small but non-zero - var very_small = Decimal.from_float(1e-20) + var very_small = Decimal128.from_float(1e-20) testing.assert_true( String(very_small).startswith("0.00000000000000000001"), - "Very small float should convert to appropriate Decimal", + "Very small float should convert to appropriate Decimal128", ) # Test case 25: Denormalized float - var denorm = Decimal.from_float(1e-310) + var denorm = Decimal128.from_float(1e-310) testing.assert_true( String(denorm).startswith("0."), - "Denormalized float should convert to small Decimal", + "Denormalized float should convert to small Decimal128", ) print("✓ Very small number tests passed") @@ -222,35 +224,35 @@ fn test_binary_to_decimal_conversion() raises: print("Testing binary to decimal conversion edge cases...") # Test case 26: 0.1 (known inexact in binary) - var point_one = Decimal.from_float(0.1) + var point_one = Decimal128.from_float(0.1) testing.assert_true( String(point_one).startswith("0.1"), - "Float 0.1 should convert to a Decimal starting with 0.1", + "Float 0.1 should convert to a Decimal128 starting with 0.1", ) # Test case 27: 0.2 (known inexact in binary) - var point_two = Decimal.from_float(0.2) + var point_two = Decimal128.from_float(0.2) testing.assert_true( String(point_two).startswith("0.2"), - "Float 0.2 should convert to a Decimal starting with 0.2", + "Float 0.2 should convert to a Decimal128 starting with 0.2", ) # Test case 28: 0.3 (known inexact in binary) - var point_three = Decimal.from_float(0.3) + var point_three = Decimal128.from_float(0.3) testing.assert_true( String(point_three).startswith("0.3"), - "Float 0.3 should convert to a Decimal that starts with 0.3", + "Float 0.3 should convert to a Decimal128 that starts with 0.3", ) # Test case 29: 0.1 + 0.2 (famously != 0.3 in binary) - var point_one_plus_two = Decimal.from_float(0.1 + 0.2) + var point_one_plus_two = Decimal128.from_float(0.1 + 0.2) testing.assert_true( String(point_one_plus_two).startswith("0.3"), - "Float 0.1+0.2 should convert to a Decimal starting with 0.3", + "Float 0.1+0.2 should convert to a Decimal128 starting with 0.3", ) # Test case 30: Repeating binary fraction - var repeating = Decimal.from_float(0.1) + var repeating = Decimal128.from_float(0.1) testing.assert_true( String(repeating).startswith("0.1"), "Float with repeating binary fraction should convert properly", @@ -260,40 +262,40 @@ fn test_binary_to_decimal_conversion() raises: fn test_rounding_behavior() raises: - """Test rounding behavior during float to Decimal conversion.""" - print("Testing rounding behavior in float to Decimal conversion...") + """Test rounding behavior during float to Decimal128 conversion.""" + print("Testing rounding behavior in float to Decimal128 conversion...") # Test case 31: Pi with limited precision - var pi = Decimal.from_float(3.141592653589793) + var pi = Decimal128.from_float(3.141592653589793) testing.assert_true( String(pi).startswith("3.14159265358979"), - "Float Pi should maintain appropriate precision in Decimal", + "Float Pi should maintain appropriate precision in Decimal128", ) # Test case 32: 1/3 (repeating decimal in base 10) - var one_third = Decimal.from_float(1.0 / 3.0) + var one_third = Decimal128.from_float(1.0 / 3.0) testing.assert_true( String(one_third).startswith("0.33333333"), - "Float 1/3 should maintain appropriate precision in Decimal", + "Float 1/3 should maintain appropriate precision in Decimal128", ) # Test case 33: 2/3 (repeating decimal in base 10) - var two_thirds = Decimal.from_float(2.0 / 3.0) + var two_thirds = Decimal128.from_float(2.0 / 3.0) testing.assert_true( String(two_thirds).startswith("0.66666666"), - "Float 2/3 should maintain appropriate precision in Decimal", + "Float 2/3 should maintain appropriate precision in Decimal128", ) # Test case 34: Round trip conversion var x = 123.456 - var decimal_x = Decimal.from_float(x) + var decimal_x = Decimal128.from_float(x) testing.assert_true( String(decimal_x).startswith("123.456"), - "Float-to-Decimal conversion should preserve input precision", + "Float-to-Decimal128 conversion should preserve input precision", ) # Test case 35: Number at float precision boundary - var precision_boundary = Decimal.from_float(9.9999999999999999) + var precision_boundary = Decimal128.from_float(9.9999999999999999) testing.assert_true( String(precision_boundary).startswith("10"), "Float near precision boundary should convert appropriately", @@ -307,32 +309,32 @@ fn test_special_values() raises: print("Testing special float values...") # Test case 36: 0.0 (already covered but included for completeness) - var zero = Decimal.from_float(0.0) + var zero = Decimal128.from_float(0.0) testing.assert_equal( - String(zero), "0", "Float 0.0 should convert to Decimal 0" + String(zero), "0", "Float 0.0 should convert to Decimal128 0" ) # Test case 37: Epsilon (smallest Float64 increment from 1.0) - var epsilon = Decimal.from_float(2.220446049250313e-16) + var epsilon = Decimal128.from_float(2.220446049250313e-16) testing.assert_true( String(epsilon).startswith("0.000000000000000"), "Float64 epsilon should convert with appropriate precision", ) # Test case 38: power of 2 (exact in binary) - var pow2 = Decimal.from_float(1024.0) + var pow2 = Decimal128.from_float(1024.0) testing.assert_equal( String(pow2), "1024", "Powers of 2 should convert exactly" ) # Test case 39: Small power of 2 - var small_pow2 = Decimal.from_float(0.125) # 2^-3 + var small_pow2 = Decimal128.from_float(0.125) # 2^-3 testing.assert_equal( String(small_pow2), "0.125", "Small powers of 2 should convert exactly" ) # Test case 40: Float with many 9s - var many_nines = Decimal.from_float(9.9999) + var many_nines = Decimal128.from_float(9.9999) testing.assert_true( String(many_nines).startswith("9.9999"), "Float with many 9s should preserve precision appropriately", @@ -346,7 +348,7 @@ fn test_scientific_notation() raises: print("Testing scientific notation float values...") # Test case 41: Simple scientific notation - var sci1 = Decimal.from_float(1.23e5) + var sci1 = Decimal128.from_float(1.23e5) testing.assert_equal( String(sci1), "123000", @@ -354,7 +356,7 @@ fn test_scientific_notation() raises: ) # Test case 42: Negative exponent - var sci2 = Decimal.from_float(4.56e-3) + var sci2 = Decimal128.from_float(4.56e-3) testing.assert_true( String(sci2).startswith("0.00456"), ( @@ -364,7 +366,7 @@ fn test_scientific_notation() raises: ) # Test case 43: Extreme positive exponent - var sci3 = Decimal.from_float(1.0e20) + var sci3 = Decimal128.from_float(1.0e20) testing.assert_equal( String(sci3), "100000000000000000000", @@ -372,14 +374,14 @@ fn test_scientific_notation() raises: ) # Test case 44: Extreme negative exponent - var sci4 = Decimal.from_float(1.0e-10) + var sci4 = Decimal128.from_float(1.0e-10) testing.assert_true( String(sci4).startswith("0.00000000"), "Float with negative exponent should have appropriate zeros", ) # Test case 45: Low precision, high exponent - var sci5 = Decimal.from_float(5e20) + var sci5 = Decimal128.from_float(5e20) testing.assert_true( String(sci5).startswith("5"), "Float with low precision but high exponent should convert properly", @@ -389,17 +391,17 @@ fn test_scientific_notation() raises: fn test_boundary_cases() raises: - """Test boundary cases for float to Decimal conversion.""" + """Test boundary cases for float to Decimal128 conversion.""" print("Testing boundary cases...") # Test case 46: Exact power of 10 - var pow10 = Decimal.from_float(1000.0) + var pow10 = Decimal128.from_float(1000.0) testing.assert_equal( String(pow10), "1000", "Powers of 10 should convert exactly" ) # Test case 47: Max safe integer precision - var safe_int = Decimal.from_float(9007199254740990.0) + var safe_int = Decimal128.from_float(9007199254740990.0) testing.assert_equal( String(safe_int), "9007199254740990", @@ -407,21 +409,21 @@ fn test_boundary_cases() raises: ) # Test case 48: Just beyond safe integer precision - var beyond_safe = Decimal.from_float(9007199254740994.0) + var beyond_safe = Decimal128.from_float(9007199254740994.0) testing.assert_true( String(beyond_safe).startswith("9007199254740"), "Beyond safe integer values should maintain appropriate precision", ) # Test case 49: Float with many trailing zeros - var trailing_zeros = Decimal.from_float(123.000000) + var trailing_zeros = Decimal128.from_float(123.000000) testing.assert_true( String(trailing_zeros).startswith("123"), "Floats with trailing zeros should convert properly", ) # Test case 50: Simple fraction - var fraction = Decimal.from_float(0.125) # 1/8, exact in binary + var fraction = Decimal128.from_float(0.125) # 1/8, exact in binary testing.assert_equal( String(fraction), "0.125", @@ -453,7 +455,7 @@ fn run_test_with_error_handling( fn main() raises: print("=========================================") - print("Running 50 tests for Decimal.from_float()") + print("Running 50 tests for Decimal128.from_float()") print("=========================================") run_test_with_error_handling(test_simple_integers, "Simple integers test") @@ -477,4 +479,4 @@ fn main() raises: ) run_test_with_error_handling(test_boundary_cases, "Boundary cases test") - print("All 50 Decimal.from_float() tests passed!") + print("All 50 Decimal128.from_float() tests passed!") diff --git a/tests/decimal/test_decimal_from_int.mojo b/tests/decimal128/test_decimal128_from_int.mojo similarity index 76% rename from tests/decimal/test_decimal_from_int.mojo rename to tests/decimal128/test_decimal128_from_int.mojo index 4a6879c..c35c27d 100644 --- a/tests/decimal/test_decimal_from_int.mojo +++ b/tests/decimal128/test_decimal128_from_int.mojo @@ -1,10 +1,10 @@ """ -Comprehensive tests for the Decimal.from_int() method. -Tests various scenarios to ensure proper conversion from integer values to Decimal. +Comprehensive tests for the Decimal128.from_int() method. +Tests various scenarios to ensure proper conversion from integer values to Decimal128. """ import testing -from decimojo.prelude import dm, Decimal, RoundingMode +from decimojo.prelude import dm, Decimal128, RoundingMode fn test_basic_integers() raises: @@ -12,7 +12,7 @@ fn test_basic_integers() raises: print("Testing basic integer conversions...") # Test case 1: Zero - var result1 = Decimal.from_int(0) + var result1 = Decimal128.from_int(0) testing.assert_equal( String(result1), "0", @@ -20,7 +20,7 @@ fn test_basic_integers() raises: ) # Test case 2: Positive integer - var result2 = Decimal.from_int(123) + var result2 = Decimal128.from_int(123) testing.assert_equal( String(result2), "123", @@ -28,7 +28,7 @@ fn test_basic_integers() raises: ) # Test case 3: Negative integer - var result3 = Decimal.from_int(-456) + var result3 = Decimal128.from_int(-456) testing.assert_equal( String(result3), "-456", @@ -36,8 +36,8 @@ fn test_basic_integers() raises: ) # Test case 4: Simple arithmetic with from_int results - var a = Decimal.from_int(10) - var b = Decimal.from_int(5) + var a = Decimal128.from_int(10) + var b = Decimal128.from_int(5) var sum_result = a + b testing.assert_equal( String(sum_result), @@ -53,7 +53,7 @@ fn test_large_integers() raises: print("Testing large integer conversions...") # Test case 1: Large positive integer - var large_pos = Decimal.from_int(1000000000) # 1 billion + var large_pos = Decimal128.from_int(1000000000) # 1 billion testing.assert_equal( String(large_pos), "1000000000", @@ -61,7 +61,7 @@ fn test_large_integers() raises: ) # Test case 2: Large negative integer - var large_neg = Decimal.from_int(-2000000000) # -2 billion + var large_neg = Decimal128.from_int(-2000000000) # -2 billion testing.assert_equal( String(large_neg), "-2000000000", @@ -69,7 +69,7 @@ fn test_large_integers() raises: ) # Test case 3: INT32_MAX - var int32_max = Decimal.from_int(2147483647) # 2^31 - 1 + var int32_max = Decimal128.from_int(2147483647) # 2^31 - 1 testing.assert_equal( String(int32_max), "2147483647", @@ -77,7 +77,7 @@ fn test_large_integers() raises: ) # Test case 4: INT32_MIN - var int32_min = Decimal.from_int(-2147483648) # -2^31 + var int32_min = Decimal128.from_int(-2147483648) # -2^31 testing.assert_equal( String(int32_min), "-2147483648", @@ -85,7 +85,7 @@ fn test_large_integers() raises: ) # Test case 5: VERY large integer (close to INT64_MAX) - var very_large = Decimal.from_int(9223372036854775807) # 2^63 - 1 + var very_large = Decimal128.from_int(9223372036854775807) # 2^63 - 1 testing.assert_equal( String(very_large), "9223372036854775807", @@ -100,40 +100,40 @@ fn test_operations_with_from_int() raises: print("Testing operations with from_int results...") # Test case 1: Addition - var a1 = Decimal.from_int(100) - var b1 = Decimal.from_int(50) + var a1 = Decimal128.from_int(100) + var b1 = Decimal128.from_int(50) var result1 = a1 + b1 testing.assert_equal( String(result1), "150", "100 + 50 should be 150, got " + String(result1) ) # Test case 2: Subtraction - var a2 = Decimal.from_int(100) - var b2 = Decimal.from_int(30) + var a2 = Decimal128.from_int(100) + var b2 = Decimal128.from_int(30) var result2 = a2 - b2 testing.assert_equal( String(result2), "70", "100 - 30 should be 70, got " + String(result2) ) # Test case 3: Multiplication - var a3 = Decimal.from_int(25) - var b3 = Decimal.from_int(4) + var a3 = Decimal128.from_int(25) + var b3 = Decimal128.from_int(4) var result3 = a3 * b3 testing.assert_equal( String(result3), "100", "25 * 4 should be 100, got " + String(result3) ) # Test case 4: Division - var a4 = Decimal.from_int(100) - var b4 = Decimal.from_int(5) + var a4 = Decimal128.from_int(100) + var b4 = Decimal128.from_int(5) var result4 = a4 / b4 testing.assert_equal( String(result4), "20", "100 / 5 should be 20, got " + String(result4) ) # Test case 5: Operation with mixed types - var a5 = Decimal.from_int(10) - var b5 = Decimal("3.5") # String constructor + var a5 = Decimal128.from_int(10) + var b5 = Decimal128("3.5") # String constructor var result5 = a5 * b5 testing.assert_equal( String(result5), @@ -149,33 +149,37 @@ fn test_comparison_with_from_int() raises: print("Testing comparisons with from_int results...") # Test case 1: Equality with same value - var a1 = Decimal.from_int(100) - var b1 = Decimal.from_int(100) + var a1 = Decimal128.from_int(100) + var b1 = Decimal128.from_int(100) testing.assert_true(a1 == b1, "from_int(100) should equal from_int(100)") # Test case 2: Equality with string constructor - var a2 = Decimal.from_int(123) - var b2 = Decimal("123") - testing.assert_true(a2 == b2, "from_int(123) should equal Decimal('123')") + var a2 = Decimal128.from_int(123) + var b2 = Decimal128("123") + testing.assert_true( + a2 == b2, "from_int(123) should equal Decimal128('123')" + ) # Test case 3: Less than - var a3 = Decimal.from_int(50) - var b3 = Decimal.from_int(100) + var a3 = Decimal128.from_int(50) + var b3 = Decimal128.from_int(100) testing.assert_true( a3 < b3, "from_int(50) should be less than from_int(100)" ) # Test case 4: Greater than - var a4 = Decimal.from_int(200) - var b4 = Decimal.from_int(100) + var a4 = Decimal128.from_int(200) + var b4 = Decimal128.from_int(100) testing.assert_true( a4 > b4, "from_int(200) should be greater than from_int(100)" ) # Test case 5: Equality with negative values - var a5 = Decimal.from_int(-500) - var b5 = Decimal("-500") - testing.assert_true(a5 == b5, "from_int(-500) should equal Decimal('-500')") + var a5 = Decimal128.from_int(-500) + var b5 = Decimal128("-500") + testing.assert_true( + a5 == b5, "from_int(-500) should equal Decimal128('-500')" + ) print("✓ Comparison with from_int results tests passed!") @@ -185,17 +189,17 @@ fn test_properties() raises: print("Testing properties of from_int results...") # Test case 1: Sign of positive - var pos = Decimal.from_int(100) + var pos = Decimal128.from_int(100) testing.assert_false( pos.is_negative(), "from_int(100) should not be negative" ) # Test case 2: Sign of negative - var neg = Decimal.from_int(-100) + var neg = Decimal128.from_int(-100) testing.assert_true(neg.is_negative(), "from_int(-100) should be negative") # Test case 3: Scale of integer (should be 0) - var integer = Decimal.from_int(123) + var integer = Decimal128.from_int(123) testing.assert_equal( integer.scale(), 0, @@ -203,13 +207,13 @@ fn test_properties() raises: ) # Test case 4: Is_integer test - var int_test = Decimal.from_int(42) + var int_test = Decimal128.from_int(42) testing.assert_true( int_test.is_integer(), "from_int result should satisfy is_integer()" ) # Test case 5: Coefficient correctness - var coef_test = Decimal.from_int(9876) + var coef_test = Decimal128.from_int(9876) testing.assert_equal( coef_test.coefficient(), UInt128(9876), @@ -224,7 +228,7 @@ fn test_edge_cases() raises: print("Testing edge cases for from_int...") # Test case 1: Zero remains zero - var zero = Decimal.from_int(0) + var zero = Decimal128.from_int(0) testing.assert_equal( String(zero), "0", "from_int(0) should be '0', got " + String(zero) ) @@ -232,17 +236,17 @@ fn test_edge_cases() raises: # Test case 2: Result should preserve sign for negative zero # Note: In most contexts, -0 becomes 0, but this tests the handling of negative zero var neg_zero = -0 - var dec_neg_zero = Decimal.from_int(neg_zero) + var dec_neg_zero = Decimal128.from_int(neg_zero) var is_neg_zero = dec_neg_zero.is_negative() and dec_neg_zero.is_zero() testing.assert_equal( is_neg_zero, False, - "Negative zero should not preserve negative sign in Decimal", + "Negative zero should not preserve negative sign in Decimal128", ) # Test case 3: INT64_MIN # Note: Most extreme negative value representable in Int - var int64_min = Decimal.from_int(-9223372036854775807 - 1) + var int64_min = Decimal128.from_int(-9223372036854775807 - 1) testing.assert_equal( String(int64_min), "-9223372036854775808", @@ -250,15 +254,15 @@ fn test_edge_cases() raises: ) # Test case 4: Alternative ways to create same value - var from_int_val = Decimal.from_int(12345) - var from_string_val = Decimal("12345") + var from_int_val = Decimal128.from_int(12345) + var from_string_val = Decimal128("12345") testing.assert_true( from_int_val == from_string_val, "from_int and from_string should create equal Decimals for same value", ) # Test case 5: Powers of 10 - var power10 = Decimal.from_int(10**9) # 1 billion + var power10 = Decimal128.from_int(10**9) # 1 billion testing.assert_equal(String(power10), "1000000000", "from_int(10^9) failed") print("✓ Edge cases for from_int tests passed!") @@ -269,7 +273,7 @@ fn test_from_int_with_scale() raises: print("Testing from_int with scale argument...") # Test case 1: Positive integer with positive scale - var result1 = Decimal.from_int(123, 2) + var result1 = Decimal128.from_int(123, 2) testing.assert_equal( String(result1), "1.23", @@ -278,7 +282,7 @@ fn test_from_int_with_scale() raises: testing.assert_equal(result1.scale(), 2, "Scale should be set to 2") # Test case 2: Negative integer with positive scale - var result2 = Decimal.from_int(-456, 3) + var result2 = Decimal128.from_int(-456, 3) testing.assert_equal( String(result2), "-0.456", @@ -287,7 +291,7 @@ fn test_from_int_with_scale() raises: testing.assert_equal(result2.scale(), 3, "Scale should be set to 3") # Test case 3: Zero with scale - var result3 = Decimal.from_int(0, 4) + var result3 = Decimal128.from_int(0, 4) testing.assert_equal( String(result3), "0.0000", @@ -296,7 +300,7 @@ fn test_from_int_with_scale() raises: testing.assert_equal(result3.scale(), 4, "Scale should be set to 4") # Test case 4: Positive integer - var result4 = Decimal.from_int(123, 2) + var result4 = Decimal128.from_int(123, 2) testing.assert_equal( String(result4), "1.23", @@ -304,7 +308,7 @@ fn test_from_int_with_scale() raises: ) # Test case 5: Large scale (close to maximum) - var result5 = Decimal.from_int(1, 25) + var result5 = Decimal128.from_int(1, 25) testing.assert_equal( String(result5), "0.0000000000000000000000001", @@ -313,16 +317,16 @@ fn test_from_int_with_scale() raises: testing.assert_equal(result5.scale(), 25, "Scale should be set to 25") # Test case 6: Max scale - var result6 = Decimal.from_int(1, Decimal.MAX_SCALE) + var result6 = Decimal128.from_int(1, Decimal128.MAX_SCALE) testing.assert_equal( result6.scale(), - Decimal.MAX_SCALE, - "Scale should be set to MAX_SCALE = " + String(Decimal.MAX_SCALE), + Decimal128.MAX_SCALE, + "Scale should be set to MAX_SCALE = " + String(Decimal128.MAX_SCALE), ) # Test case 7: Arithmetic with scaled value - var a7 = Decimal.from_int(10, 1) # 1.0 - var b7 = Decimal.from_int(3, 2) # 0.03 + var a7 = Decimal128.from_int(10, 1) # 1.0 + var b7 = Decimal128.from_int(3, 2) # 0.03 var result7 = a7 / b7 testing.assert_equal( String(result7), @@ -331,8 +335,8 @@ fn test_from_int_with_scale() raises: ) # Test case 8: Comparison with different scales but same value - var a8 = Decimal.from_int(123, 0) # 123 - var b8 = Decimal.from_int(123, 2) # 1.23 + var a8 = Decimal128.from_int(123, 0) # 123 + var b8 = Decimal128.from_int(123, 2) # 1.23 testing.assert_true( a8 != b8, "from_int(123, 0) should not equal from_int(123, 2)" ) @@ -359,7 +363,7 @@ fn run_test_with_error_handling( fn main() raises: print("=========================================") - print("Running Decimal.from_int() Tests") + print("Running Decimal128.from_int() Tests") print("=========================================") run_test_with_error_handling( @@ -382,4 +386,4 @@ fn main() raises: test_from_int_with_scale, "from_int with scale test" ) - print("All Decimal.from_int() tests passed!") + print("All Decimal128.from_int() tests passed!") diff --git a/tests/decimal/test_decimal_from_string.mojo b/tests/decimal128/test_decimal128_from_string.mojo similarity index 74% rename from tests/decimal/test_decimal_from_string.mojo rename to tests/decimal128/test_decimal128_from_string.mojo index e84cb9c..4608cd0 100644 --- a/tests/decimal/test_decimal_from_string.mojo +++ b/tests/decimal128/test_decimal128_from_string.mojo @@ -1,10 +1,10 @@ """ -Comprehensive tests for the Decimal.from_string() constructor method. +Comprehensive tests for the Decimal128.from_string() constructor method. Tests 50 different cases to ensure proper conversion from string values. """ import testing -from decimojo.prelude import dm, Decimal, RoundingMode +from decimojo.prelude import dm, Decimal128, RoundingMode fn test_basic_integers() raises: @@ -12,31 +12,31 @@ fn test_basic_integers() raises: print("Testing basic integer string conversions...") # Test case 1: Zero - var zero = Decimal.from_string("0") + var zero = Decimal128.from_string("0") testing.assert_equal( - String(zero), "0", "String '0' should convert to Decimal 0" + String(zero), "0", "String '0' should convert to Decimal128 0" ) # Test case 2: One - var one = Decimal.from_string("1") + var one = Decimal128.from_string("1") testing.assert_equal( - String(one), "1", "String '1' should convert to Decimal 1" + String(one), "1", "String '1' should convert to Decimal128 1" ) # Test case 3: Simple integer - var simple_int = Decimal.from_string("123") + var simple_int = Decimal128.from_string("123") testing.assert_equal( String(simple_int), "123", "String '123' should convert correctly" ) # Test case 4: Large integer - var large_int = Decimal.from_string("123456789") + var large_int = Decimal128.from_string("123456789") testing.assert_equal(String(large_int), "123456789") # Test case 5: Integer with internal spaces (should fail) var exception_caught = False try: - var _invalid = Decimal.from_string("1 234") + var _invalid = Decimal128.from_string("1 234") testing.assert_equal( True, False, "Should have raised exception for space in integer" ) @@ -52,23 +52,25 @@ fn test_basic_decimals() raises: print("Testing basic decimal string conversions...") # Test case 6: Simple decimal - var simple_dec = Decimal.from_string("123.45") + var simple_dec = Decimal128.from_string("123.45") testing.assert_equal(String(simple_dec), "123.45") # Test case 7: Zero decimal point - var zero_point = Decimal.from_string("0.0") + var zero_point = Decimal128.from_string("0.0") testing.assert_equal(String(zero_point), "0.0") # Test case 8: Single digit with decimal - var single_digit = Decimal.from_string("1.23") + var single_digit = Decimal128.from_string("1.23") testing.assert_equal(String(single_digit), "1.23") # Test case 9: One-level precision - var one_precision = Decimal.from_string("9.9") + var one_precision = Decimal128.from_string("9.9") testing.assert_equal(String(one_precision), "9.9") # Test case 10: High precision decimal - var high_precision = Decimal.from_string("0.12345678901234567890123456789") + var high_precision = Decimal128.from_string( + "0.12345678901234567890123456789" + ) testing.assert_equal( String(high_precision), "0.1234567890123456789012345679" ) @@ -81,21 +83,21 @@ fn test_negative_numbers() raises: print("Testing negative number string conversions...") # Test case 11: Negative integer - var neg_int = Decimal.from_string("-123") + var neg_int = Decimal128.from_string("-123") testing.assert_equal(String(neg_int), "-123") # Test case 12: Negative decimal - var neg_dec = Decimal.from_string("-123.45") + var neg_dec = Decimal128.from_string("-123.45") testing.assert_equal(String(neg_dec), "-123.45") # Test case 13: Negative zero - var neg_zero = Decimal.from_string("-0") + var neg_zero = Decimal128.from_string("-0") testing.assert_equal( String(neg_zero), "-0", "Negative zero should convert to '-0'" ) # Test case 14: Negative decimal zero - var neg_decimal_zero = Decimal.from_string("-0.0") + var neg_decimal_zero = Decimal128.from_string("-0.0") testing.assert_equal( String(neg_decimal_zero), "-0.0", @@ -103,7 +105,7 @@ fn test_negative_numbers() raises: ) # Test case 15: Negative small value - var neg_small = Decimal.from_string("-0.001") + var neg_small = Decimal128.from_string("-0.001") testing.assert_equal(String(neg_small), "-0.001") print("✓ Negative number tests passed") @@ -114,15 +116,15 @@ fn test_zeros_variants() raises: print("Testing zero variants string conversions...") # Test case 16: Single zero - var single_zero = Decimal.from_string("0") + var single_zero = Decimal128.from_string("0") testing.assert_equal(String(single_zero), "0") # Test case 17: Zero with decimal - var zero_decimal = Decimal.from_string("0.0") + var zero_decimal = Decimal128.from_string("0.0") testing.assert_equal(String(zero_decimal), "0.0") # Test case 18: Zero with high precision - var zero_high_precision = Decimal.from_string( + var zero_high_precision = Decimal128.from_string( "0.00000000000000000000000000" ) testing.assert_equal( @@ -130,13 +132,13 @@ fn test_zeros_variants() raises: ) # Test case 19: Multiple leading zeros - var leading_zeros = Decimal.from_string("000000") + var leading_zeros = Decimal128.from_string("000000") testing.assert_equal( String(leading_zeros), "0", "Multiple zeros should convert to just '0'" ) # Test case 20: Multiple leading zeros with decimal - var leading_zeros_decimal = Decimal.from_string("000.000") + var leading_zeros_decimal = Decimal128.from_string("000.000") testing.assert_equal(String(leading_zeros_decimal), "0.000") print("✓ Zero variants tests passed") @@ -147,23 +149,23 @@ fn test_scientific_notation() raises: print("Testing scientific notation string conversions...") # Test case 21: Simple positive exponent - var simple_pos_exp = Decimal.from_string("1.23e2") + var simple_pos_exp = Decimal128.from_string("1.23e2") testing.assert_equal(String(simple_pos_exp), "123") # Test case 22: Simple negative exponent - var simple_neg_exp = Decimal.from_string("1.23e-2") + var simple_neg_exp = Decimal128.from_string("1.23e-2") testing.assert_equal(String(simple_neg_exp), "0.0123") # Test case 23: Zero with exponent - var zero_exp = Decimal.from_string("0e10") + var zero_exp = Decimal128.from_string("0e10") testing.assert_equal(String(zero_exp), "0") # Test case 24: Explicit positive exponent - var explicit_pos_exp = Decimal.from_string("1.23E+2") + var explicit_pos_exp = Decimal128.from_string("1.23E+2") testing.assert_equal(String(explicit_pos_exp), "123") # Test case 25: Large exponent value - var large_exp = Decimal.from_string("1.23e20") + var large_exp = Decimal128.from_string("1.23e20") testing.assert_equal(String(large_exp), "123000000000000000000") print("✓ Scientific notation tests passed") @@ -174,31 +176,31 @@ fn test_formatting_variants() raises: print("Testing string formatting variants...") # Test case 26: Leading zeros with integer - var leading_zeros_int = Decimal.from_string("00123") + var leading_zeros_int = Decimal128.from_string("00123") testing.assert_equal(String(leading_zeros_int), "123") # Test case 27: Trailing zeros after decimal point - var trailing_zeros = Decimal.from_string("123.4500") + var trailing_zeros = Decimal128.from_string("123.4500") testing.assert_equal(String(trailing_zeros), "123.4500") # Test case 28: Both leading and trailing zeros - var both_zeros = Decimal.from_string("00123.4500") + var both_zeros = Decimal128.from_string("00123.4500") testing.assert_equal(String(both_zeros), "123.4500") - # Test case 29: Decimal point with no following digits - var decimal_no_digits = Decimal.from_string("123.") + # Test case 29: Decimal128 point with no following digits + var decimal_no_digits = Decimal128.from_string("123.") testing.assert_equal( String(decimal_no_digits), "123", - "Decimal point with no digits should be ignored", + "Decimal128 point with no digits should be ignored", ) - # Test case 30: Decimal point with no preceding digits - var decimal_no_preceding = Decimal.from_string(".123") + # Test case 30: Decimal128 point with no preceding digits + var decimal_no_preceding = Decimal128.from_string(".123") testing.assert_equal( String(decimal_no_preceding), "0.123", - "Decimal point with no preceding digits should add leading 0", + "Decimal128 point with no preceding digits should add leading 0", ) print("✓ Formatting variants tests passed") @@ -209,23 +211,23 @@ fn test_special_characters() raises: print("Testing strings with special characters...") # Test case 31: Positive sign - var positive_sign = Decimal.from_string("+123.45") + var positive_sign = Decimal128.from_string("+123.45") testing.assert_equal(String(positive_sign), "123.45") # Test case 32: Positive sign with scientific notation - var pos_sign_exp = Decimal.from_string("+1.23e+2") + var pos_sign_exp = Decimal128.from_string("+1.23e+2") testing.assert_equal(String(pos_sign_exp), "123") # Test case 33: Negative sign with scientific notation - var neg_sign_exp = Decimal.from_string("-1.23e+2") + var neg_sign_exp = Decimal128.from_string("-1.23e+2") testing.assert_equal(String(neg_sign_exp), "-123") - # Test case 34: Decimal with positive exponent - var decimal_pos_exp = Decimal.from_string("1.23e+2") + # Test case 34: Decimal128 with positive exponent + var decimal_pos_exp = Decimal128.from_string("1.23e+2") testing.assert_equal(String(decimal_pos_exp), "123") # Test case 35: Scientific notation with multiple digits in exponent - var multi_digit_exp = Decimal.from_string("1.23e+12") + var multi_digit_exp = Decimal128.from_string("1.23e+12") testing.assert_equal(String(multi_digit_exp), "1230000000000") print("✓ Special character tests passed") @@ -238,7 +240,7 @@ fn test_invalid_inputs() raises: # Test case 36: Empty string var exception_caught = False try: - var _empty_string = Decimal.from_string("") + var _empty_string = Decimal128.from_string("") testing.assert_equal(True, False, "Empty string should raise exception") except: exception_caught = True @@ -247,7 +249,7 @@ fn test_invalid_inputs() raises: # Test case 37: Non-numeric string exception_caught = False try: - var _non_numeric = Decimal.from_string("abc") + var _non_numeric = Decimal128.from_string("abc") testing.assert_equal( True, False, "Non-numeric string should raise exception" ) @@ -258,7 +260,7 @@ fn test_invalid_inputs() raises: # Test case 38: Multiple decimal points exception_caught = False try: - var _multiple_points = Decimal.from_string("1.2.3") + var _multiple_points = Decimal128.from_string("1.2.3") testing.assert_equal( True, False, "Multiple decimal points should raise exception" ) @@ -269,7 +271,7 @@ fn test_invalid_inputs() raises: # Test case 39: Invalid scientific notation exception_caught = False try: - var _invalid_exp = Decimal.from_string("1.23e") + var _invalid_exp = Decimal128.from_string("1.23e") testing.assert_equal( True, False, "Invalid scientific notation should raise exception" ) @@ -280,7 +282,7 @@ fn test_invalid_inputs() raises: # Test case 40: Mixed digits and characters exception_caught = False try: - var _mixed = Decimal.from_string("123a456") + var _mixed = Decimal128.from_string("123a456") testing.assert_equal( True, False, "Mixed digits and characters should raise exception" ) @@ -296,24 +298,24 @@ fn test_boundary_cases() raises: print("Testing boundary cases...") # Test case 41: Value at maximum precision - var max_precision = Decimal.from_string("0." + "1" * 28) + var max_precision = Decimal128.from_string("0." + "1" * 28) testing.assert_equal(String(max_precision), "0." + "1" * 28) # Test case 42: Large integer part - var large_integer_part = Decimal.from_string("9" * 28 + ".5") + var large_integer_part = Decimal128.from_string("9" * 28 + ".5") testing.assert_equal(String(large_integer_part), "1" + "0" * 28) # Test case 43: Single digit maximum - var single_digit_max = Decimal.from_string("9") + var single_digit_max = Decimal128.from_string("9") testing.assert_equal(String(single_digit_max), "9") # Test case 44: Smallest non-zero positive value - var smallest_positive = Decimal.from_string("0." + "0" * 27 + "1") + var smallest_positive = Decimal128.from_string("0." + "0" * 27 + "1") testing.assert_equal(String(smallest_positive), "0." + "0" * 27 + "1") # Test case 45: String representing maximum possible value var max_value_str = "79228162514264337593543950335" - var max_value = Decimal.from_string(max_value_str) + var max_value = Decimal128.from_string(max_value_str) testing.assert_equal(String(max_value), max_value_str) print("✓ Boundary case tests passed") @@ -324,28 +326,28 @@ fn test_special_cases() raises: print("Testing special cases...") # Test case 46: Very long decimal - var long_decimal = Decimal.from_string( + var long_decimal = Decimal128.from_string( "0.11111111111111111111111111111111111" ) # Should be truncated to max precision testing.assert_true(String(long_decimal).startswith("0.11111111111")) # Test case 47: Removing trailing zeros in whole number - var whole_number = Decimal.from_string("1230.00") + var whole_number = Decimal128.from_string("1230.00") testing.assert_equal( String(whole_number), "1230.00", "Trailing zeros should be preserved" ) # Test case 48: Value with all 9s - var all_nines = Decimal.from_string("9.999999999999999999999999999") + var all_nines = Decimal128.from_string("9.999999999999999999999999999") testing.assert_equal(String(all_nines), "9.999999999999999999999999999") # Test case 49: Value with alternating digits - var alternating = Decimal.from_string("1.010101010101010101010101010") + var alternating = Decimal128.from_string("1.010101010101010101010101010") testing.assert_equal(String(alternating), "1.010101010101010101010101010") # Test case 50: Value with specific pattern - var pattern = Decimal.from_string("123.456789012345678901234567") + var pattern = Decimal128.from_string("123.456789012345678901234567") testing.assert_equal(String(pattern), "123.456789012345678901234567") print("✓ Special case tests passed") @@ -370,7 +372,7 @@ fn run_test_with_error_handling( fn main() raises: print("=========================================") - print("Running 50 tests for Decimal.from_string()") + print("Running 50 tests for Decimal128.from_string()") print("=========================================") run_test_with_error_handling(test_basic_integers, "Basic integers test") @@ -390,4 +392,4 @@ fn main() raises: run_test_with_error_handling(test_boundary_cases, "Boundary cases test") run_test_with_error_handling(test_special_cases, "Special cases test") - print("All 50 Decimal.from_string() tests passed!") + print("All 50 Decimal128.from_string() tests passed!") diff --git a/tests/decimal/test_decimal_ln.mojo b/tests/decimal128/test_decimal128_ln.mojo similarity index 85% rename from tests/decimal/test_decimal_ln.mojo rename to tests/decimal128/test_decimal128_ln.mojo index 3ef16cc..c6e9291 100644 --- a/tests/decimal/test_decimal_ln.mojo +++ b/tests/decimal128/test_decimal128_ln.mojo @@ -5,8 +5,8 @@ and edge cases to ensure proper calculation of the natural logarithm. """ import testing -from decimojo import Decimal, RoundingMode -from decimojo.decimal.exponential import ln +from decimojo import Decimal128, RoundingMode +from decimojo.decimal128.exponential import ln fn test_basic_ln_values() raises: @@ -14,14 +14,14 @@ fn test_basic_ln_values() raises: print("Testing basic natural logarithm values...") # Test case 1: ln(1) = 0 - var one = Decimal(1) + var one = Decimal128(1) var result1 = one.ln() testing.assert_equal( String(result1), "0", "ln(1) should be 0, got " + String(result1) ) # Test case 2: ln(e) = 1 - var e = Decimal("2.718281828459045235360287471") + var e = Decimal128("2.718281828459045235360287471") var result_e = e.ln() testing.assert_true( String(result_e).startswith("1.00000000000000000000"), @@ -29,7 +29,7 @@ fn test_basic_ln_values() raises: ) # Test case 3: ln(10) - var ten = Decimal(10) + var ten = Decimal128(10) var result_ten = ln(ten) testing.assert_true( String(result_ten).startswith("2.30258509299404568401799145"), @@ -38,7 +38,7 @@ fn test_basic_ln_values() raises: ) # Test case 4: ln(0.1) - var tenth = Decimal("0.1") + var tenth = Decimal128("0.1") var result_tenth = ln(tenth) testing.assert_true( String(result_tenth).startswith("-2.302585092994045684017991454"), @@ -55,7 +55,7 @@ fn test_fractional_ln_values() raises: print("Testing natural logarithm values with fractional inputs...") # Test case 5: ln(0.5) - var half = Decimal("0.5") + var half = Decimal128("0.5") var result_half = ln(half) testing.assert_true( String(result_half).startswith("-0.693147180559945309417232121"), @@ -65,7 +65,7 @@ fn test_fractional_ln_values() raises: ) # Test case 6: ln(2) - var two = Decimal(2) + var two = Decimal128(2) var result_two = ln(two) testing.assert_true( String(result_two).startswith("0.693147180559945309417232121"), @@ -74,7 +74,7 @@ fn test_fractional_ln_values() raises: ) # Test case 7: ln(5) - var five = Decimal(5) + var five = Decimal128(5) var result_five = ln(five) testing.assert_true( String(result_five).startswith("1.609437912434100374600759333"), @@ -90,12 +90,12 @@ fn test_mathematical_identities() raises: print("Testing mathematical identities for natural logarithm...") # Test case 8: ln(a * b) = ln(a) + ln(b) - var a = Decimal(2) - var b = Decimal(3) + var a = Decimal128(2) + var b = Decimal128(3) var ln_a_times_b = ln(a * b) var ln_a_plus_ln_b = ln(a) + ln(b) testing.assert_true( - abs(ln_a_times_b - ln_a_plus_ln_b) < Decimal("0.0000000001"), + abs(ln_a_times_b - ln_a_plus_ln_b) < Decimal128("0.0000000001"), "ln(a * b) should equal ln(a) + ln(b) within tolerance", ) @@ -103,15 +103,15 @@ fn test_mathematical_identities() raises: var ln_a_div_b = ln(a / b) var ln_a_minus_ln_b = ln(a) - ln(b) testing.assert_true( - abs(ln_a_div_b - ln_a_minus_ln_b) < Decimal("0.0000000001"), + abs(ln_a_div_b - ln_a_minus_ln_b) < Decimal128("0.0000000001"), "ln(a / b) should equal ln(a) - ln(b) within tolerance", ) # Test case 10: ln(e^x) = x - var x = Decimal(5) + var x = Decimal128(5) var ln_e_to_x = ln(x.exp()) testing.assert_true( - abs(ln_e_to_x - x) < Decimal("0.0000000001"), + abs(ln_e_to_x - x) < Decimal128("0.0000000001"), "ln(e^x) should equal x within tolerance", ) @@ -123,7 +123,7 @@ fn test_edge_cases() raises: print("Testing edge cases for natural logarithm function...") # Test case 11: ln(0) should raise an exception - var zero = Decimal(0) + var zero = Decimal128(0) var exception_caught = False try: var _ln0 = ln(zero) @@ -133,7 +133,7 @@ fn test_edge_cases() raises: testing.assert_equal(exception_caught, True) # Test case 12: ln of a negative number should raise an exception - var neg_one = Decimal(-1) + var neg_one = Decimal128(-1) exception_caught = False try: var _ln = ln(neg_one) @@ -145,7 +145,7 @@ fn test_edge_cases() raises: testing.assert_equal(exception_caught, True) # Test case 13: ln of a very small number - var very_small = Decimal("0.000000000000000000000000001") + var very_small = Decimal128("0.000000000000000000000000001") var result_small = ln(very_small) testing.assert_true( String(result_small).startswith("-62.16979751083923346848576927"), @@ -156,7 +156,7 @@ fn test_edge_cases() raises: ) # Test case 14: ln of a very large number - var very_large = Decimal("10000000000000000000000000000") + var very_large = Decimal128("10000000000000000000000000000") var result_large = ln(very_large) testing.assert_true( String(result_large).startswith("64.4723"), @@ -173,7 +173,7 @@ fn test_precision() raises: print("Testing precision of natural logarithm calculations...") # Test case 15: ln(2) with high precision - var two = Decimal(2) + var two = Decimal128(2) var result_two = ln(two) testing.assert_true( String(result_two).startswith("0.693147180559945309417232121"), @@ -181,7 +181,7 @@ fn test_precision() raises: ) # Test case 16: ln(10) with high precision - var ten = Decimal(10) + var ten = Decimal128(10) var result_ten = ln(ten) testing.assert_true( String(result_ten).startswith("2.30258509299404568401"), @@ -200,20 +200,22 @@ fn test_range_of_values() raises: # Test case 17: ln(x) for x in range (3, 10) testing.assert_true( - Decimal(3).ln() > Decimal(0), "ln(x) should be positive for x > 2" + Decimal128(3).ln() > Decimal128(0), "ln(x) should be positive for x > 2" ) testing.assert_true( - Decimal(10).ln() > Decimal(2), + Decimal128(10).ln() > Decimal128(2), "ln(x) should be greater than x for x > 2", ) # Test case 18: ln(x) for x in range (0.1, 1, 0.1) testing.assert_true( - Decimal("0.1").ln() < Decimal(0), "ln(x) should be negative for x < 1" + Decimal128("0.1").ln() < Decimal128(0), + "ln(x) should be negative for x < 1", ) testing.assert_true( - Decimal("0.9").ln() < Decimal(0), "ln(x) should be negative for x < 1" + Decimal128("0.9").ln() < Decimal128(0), + "ln(x) should be negative for x < 1", ) print("✓ Range of values tests passed!") @@ -224,15 +226,15 @@ fn test_special_cases() raises: print("Testing special cases for natural logarithm function...") # Test case 19: ln(1) = 0 (revisited) - var one = Decimal(1) + var one = Decimal128(1) var result_one = ln(one) testing.assert_equal(String(result_one), "0", "ln(1) should be exactly 0") # Test case 20: ln(e) close to 1 - var e = Decimal("2.718281828459045235360287471") + var e = Decimal128("2.718281828459045235360287471") var result_e = ln(e) testing.assert_true( - abs(result_e - Decimal(1)) < Decimal("0.0000000001"), + abs(result_e - Decimal128(1)) < Decimal128("0.0000000001"), "ln(e) should be very close to 1", ) diff --git a/tests/decimal/test_decimal_log.mojo b/tests/decimal128/test_decimal128_log.mojo similarity index 79% rename from tests/decimal/test_decimal_log.mojo rename to tests/decimal128/test_decimal128_log.mojo index 302ff79..3355701 100644 --- a/tests/decimal/test_decimal_log.mojo +++ b/tests/decimal128/test_decimal128_log.mojo @@ -1,10 +1,10 @@ """ -Comprehensive tests for the log() function of the Decimal type. +Comprehensive tests for the log() function of the Decimal128 type. Tests various scenarios to ensure proper calculation of logarithms with arbitrary bases. """ import testing -from decimojo.prelude import dm, Decimal, RoundingMode +from decimojo.prelude import dm, Decimal128, RoundingMode fn test_basic_log() raises: @@ -12,16 +12,16 @@ fn test_basic_log() raises: print("Testing basic logarithm calculations...") # Test case 1: log_2(8) = 3 - var val1 = Decimal(8) - var base1 = Decimal(2) + var val1 = Decimal128(8) + var base1 = Decimal128(2) var result1 = val1.log(base1) testing.assert_equal( String(result1), "3", "log_2(8) should be 3, got " + String(result1) ) # Test case 2: log_3(27) = 3 - var val2 = Decimal(27) - var base2 = Decimal(3) + var val2 = Decimal128(27) + var base2 = Decimal128(3) var result2 = val2.log(base2) testing.assert_equal( String(result2.round()), @@ -30,8 +30,8 @@ fn test_basic_log() raises: ) # Test case 3: log_5(125) = 3 - var val3 = Decimal(125) - var base3 = Decimal(5) + var val3 = Decimal128(125) + var base3 = Decimal128(5) var result3 = val3.log(base3) testing.assert_equal( String(result3.round()), @@ -40,16 +40,16 @@ fn test_basic_log() raises: ) # Test case 4: log_10(1000) = 3 - var val4 = Decimal(1000) - var base4 = Decimal(10) + var val4 = Decimal128(1000) + var base4 = Decimal128(10) var result4 = val4.log(base4) testing.assert_equal( String(result4), "3", "log_10(1000) should be 3, got " + String(result4) ) # Test case 5: log_e(e) = 1 (natural log of e) - var val5 = Decimal.E() - var base5 = Decimal.E() + var val5 = Decimal128.E() + var base5 = Decimal128.E() var result5 = val5.log(base5) testing.assert_equal( String(result5), "1", "log_e(e) should be 1, got " + String(result5) @@ -63,8 +63,8 @@ fn test_non_integer_results() raises: print("Testing logarithm calculations with non-integer results...") # Test case 1: log_2(10) - var val1 = Decimal(10) - var base1 = Decimal(2) + var val1 = Decimal128(10) + var base1 = Decimal128(2) var result1 = val1.log(base1) testing.assert_true( String(result1).startswith("3.321928094887362347"), @@ -72,8 +72,8 @@ fn test_non_integer_results() raises: ) # Test case 2: log_3(10) - var val2 = Decimal(10) - var base2 = Decimal(3) + var val2 = Decimal128(10) + var base2 = Decimal128(3) var result2 = val2.log(base2) testing.assert_true( String(result2).startswith("2.0959032742893846"), @@ -82,8 +82,8 @@ fn test_non_integer_results() raises: ) # Test case 3: log_10(2) - var val3 = Decimal(2) - var base3 = Decimal(10) + var val3 = Decimal128(2) + var base3 = Decimal128(10) var result3 = val3.log(base3) testing.assert_true( String(result3).startswith("0.301029995663981195"), @@ -91,8 +91,8 @@ fn test_non_integer_results() raises: ) # Test case 4: log_e(10) - var val4 = Decimal(10) - var base4 = Decimal.E() + var val4 = Decimal128(10) + var base4 = Decimal128.E() var result4 = val4.log(base4) testing.assert_true( String(result4).startswith("2.302585092994045684"), @@ -100,8 +100,8 @@ fn test_non_integer_results() raises: ) # Test case 5: log_7(19) - var val5 = Decimal(19) - var base5 = Decimal(7) + var val5 = Decimal128(19) + var base5 = Decimal128(7) var result5 = val5.log(base5) testing.assert_true( String(result5).startswith("1.5131423106"), @@ -117,16 +117,16 @@ fn test_fractional_inputs() raises: print("Testing logarithm calculations with fractional inputs...") # Test case 1: log_2(0.5) = -1 - var val1 = Decimal("0.5") - var base1 = Decimal(2) + var val1 = Decimal128("0.5") + var base1 = Decimal128(2) var result1 = val1.log(base1) testing.assert_equal( String(result1), "-1", "log_2(0.5) should be -1, got " + String(result1) ) # Test case 2: log_3(0.125) - var val2 = Decimal("0.125") - var base2 = Decimal(3) + var val2 = Decimal128("0.125") + var base2 = Decimal128(3) var result2 = val2.log(base2) testing.assert_true( String(result2).startswith("-1.89278926"), @@ -135,16 +135,16 @@ fn test_fractional_inputs() raises: ) # Test case 3: log_0.5(2) = -1 (logarithm with fractional base) - var val3 = Decimal(2) - var base3 = Decimal("0.5") + var val3 = Decimal128(2) + var base3 = Decimal128("0.5") var result3 = val3.log(base3) testing.assert_equal( String(result3), "-1", "log_0.5(2) should be -1, got " + String(result3) ) # Test case 4: log_0.1(0.001) = 3 - var val4 = Decimal("0.001") - var base4 = Decimal("0.1") + var val4 = Decimal128("0.001") + var base4 = Decimal128("0.1") var result4 = val4.log(base4) testing.assert_equal( String(result4.round()), @@ -153,8 +153,8 @@ fn test_fractional_inputs() raises: ) # Test case 5: log with fractional base and value - var val5 = Decimal("1.5") - var base5 = Decimal("2.5") + var val5 = Decimal128("1.5") + var base5 = Decimal128("2.5") var result5 = val5.log(base5) testing.assert_true( String(result5).startswith("0.4425070493497599"), @@ -170,8 +170,8 @@ fn test_edge_cases() raises: print("Testing logarithm edge cases...") # Test case 1: log of a negative number (should raise error) - var val1 = Decimal(-10) - var base1 = Decimal(10) + var val1 = Decimal128(-10) + var base1 = Decimal128(10) var exception_caught = False try: var _result1 = val1.log(base1) @@ -185,8 +185,8 @@ fn test_edge_cases() raises: ) # Test case 2: log of zero (should raise error) - var val2 = Decimal(0) - var base2 = Decimal(10) + var val2 = Decimal128(0) + var base2 = Decimal128(10) exception_caught = False try: var _result2 = val2.log(base2) @@ -198,8 +198,8 @@ fn test_edge_cases() raises: ) # Test case 3: log with base 1 (should raise error) - var val3 = Decimal(10) - var base3 = Decimal(1) + var val3 = Decimal128(10) + var base3 = Decimal128(1) exception_caught = False try: var _result3 = val3.log(base3) @@ -211,8 +211,8 @@ fn test_edge_cases() raises: ) # Test case 4: log with base 0 (should raise error) - var val4 = Decimal(10) - var base4 = Decimal(0) + var val4 = Decimal128(10) + var base4 = Decimal128(0) exception_caught = False try: var _result4 = val4.log(base4) @@ -224,8 +224,8 @@ fn test_edge_cases() raises: ) # Test case 5: log with negative base (should raise error) - var val5 = Decimal(10) - var base5 = Decimal(-2) + var val5 = Decimal128(10) + var base5 = Decimal128(-2) exception_caught = False try: var _result5 = val5.log(base5) @@ -239,8 +239,8 @@ fn test_edge_cases() raises: ) # Test case 6: log_b(1) = 0 for any base b - var val6 = Decimal(1) - var base6 = Decimal(7.5) # Any base should give result 0 + var val6 = Decimal128(1) + var base6 = Decimal128(7.5) # Any base should give result 0 var result6 = val6.log(base6) testing.assert_equal( String(result6), @@ -249,7 +249,7 @@ fn test_edge_cases() raises: ) # Test case 7: log_b(b) = 1 for any base b - var base7 = Decimal("3.14159") + var base7 = Decimal128("3.14159") var val7 = base7 # Value same as base should give result 1 var result7 = val7.log(base7) testing.assert_equal( @@ -266,10 +266,10 @@ fn test_precision() raises: print("Testing logarithm precision...") # Test case 1: High precision logarithm - var val1 = Decimal( + var val1 = Decimal128( "2.718281828459045235360287471352" ) # e to high precision - var base1 = Decimal(10) + var base1 = Decimal128(10) var result1 = val1.log(base1) testing.assert_true( String(result1).startswith("0.434294481903251827651"), @@ -277,8 +277,8 @@ fn test_precision() raises: ) # Test case 2: log_2(1024) = 10 exactly - var val2 = Decimal(1024) - var base2 = Decimal(2) + var val2 = Decimal128(1024) + var base2 = Decimal128(2) var result2 = val2.log(base2) testing.assert_equal( String(result2.round()), @@ -287,9 +287,9 @@ fn test_precision() raises: ) # Test case 3: Small difference in value - var val3a = Decimal("1.000001") - var val3b = Decimal("1.000002") - var base3 = Decimal(10) + var val3a = Decimal128("1.000001") + var val3b = Decimal128("1.000002") + var base3 = Decimal128(10) var result3a = val3a.log(base3) var result3b = val3b.log(base3) testing.assert_true( @@ -298,8 +298,8 @@ fn test_precision() raises: ) # Test case 4: Verify log precision with a known value - var val4 = Decimal(2) - var base4 = Decimal.E() + var val4 = Decimal128(2) + var base4 = Decimal128.E() var result4 = val4.log(base4) testing.assert_true( String(result4).startswith("0.693147180559945309"), @@ -315,56 +315,56 @@ fn test_mathematical_properties() raises: print("Testing mathematical properties of logarithms...") # Test case 1: log_a(x*y) = log_a(x) + log_a(y) - var x1 = Decimal(3) - var y1 = Decimal(4) - var a1 = Decimal(5) + var x1 = Decimal128(3) + var y1 = Decimal128(4) + var a1 = Decimal128(5) var log_product1 = (x1 * y1).log(a1) var sum_logs1 = x1.log(a1) + y1.log(a1) testing.assert_true( - abs(log_product1 - sum_logs1) < Decimal("0.000000000001"), + abs(log_product1 - sum_logs1) < Decimal128("0.000000000001"), "log_a(x*y) should equal log_a(x) + log_a(y)", ) # Test case 2: log_a(x/y) = log_a(x) - log_a(y) - var x2 = Decimal(20) - var y2 = Decimal(5) - var a2 = Decimal(2) + var x2 = Decimal128(20) + var y2 = Decimal128(5) + var a2 = Decimal128(2) var log_quotient2 = (x2 / y2).log(a2) var diff_logs2 = x2.log(a2) - y2.log(a2) testing.assert_true( - abs(log_quotient2 - diff_logs2) < Decimal("0.000000000001"), + abs(log_quotient2 - diff_logs2) < Decimal128("0.000000000001"), "log_a(x/y) should equal log_a(x) - log_a(y)", ) # Test case 3: log_a(x^n) = n * log_a(x) - var x3 = Decimal(3) + var x3 = Decimal128(3) var n3 = 4 - var a3 = Decimal(7) + var a3 = Decimal128(7) var log_power3 = (x3**n3).log(a3) - var n_times_log3 = Decimal(n3) * x3.log(a3) + var n_times_log3 = Decimal128(n3) * x3.log(a3) testing.assert_true( - abs(log_power3 - n_times_log3) < Decimal("0.000000000001"), + abs(log_power3 - n_times_log3) < Decimal128("0.000000000001"), "log_a(x^n) should equal n * log_a(x)", ) # Test case 4: log_a(1/x) = -log_a(x) - var x4 = Decimal(7) - var a4 = Decimal(3) - var log_inverse4 = (Decimal(1) / x4).log(a4) + var x4 = Decimal128(7) + var a4 = Decimal128(3) + var log_inverse4 = (Decimal128(1) / x4).log(a4) var neg_log4 = -x4.log(a4) testing.assert_true( - abs(log_inverse4 - neg_log4) < Decimal("0.000000000001"), + abs(log_inverse4 - neg_log4) < Decimal128("0.000000000001"), "log_a(1/x) should equal -log_a(x)", ) # Test case 5: log_a(b) = log_c(b) / log_c(a) (change of base formula) - var b5 = Decimal(7) - var a5 = Decimal(3) - var c5 = Decimal(10) # Arbitrary third base + var b5 = Decimal128(7) + var a5 = Decimal128(3) + var c5 = Decimal128(10) # Arbitrary third base var direct_log5 = b5.log(a5) var change_base5 = b5.log(c5) / a5.log(c5) testing.assert_true( - abs(direct_log5 - change_base5) < Decimal("0.000000000001"), + abs(direct_log5 - change_base5) < Decimal128("0.000000000001"), "log_a(b) should equal log_c(b) / log_c(a)", ) @@ -376,20 +376,20 @@ fn test_consistency_with_other_logarithms() raises: print("Testing consistency with other logarithm functions...") # Test case 1: log_10(x) == log10(x) - var val1 = Decimal(7) - var log_base10_val1 = val1.log(Decimal(10)) + var val1 = Decimal128(7) + var log_base10_val1 = val1.log(Decimal128(10)) var log10_val1 = val1.log10() testing.assert_true( - abs(log_base10_val1 - log10_val1) < Decimal("0.000000000001"), + abs(log_base10_val1 - log10_val1) < Decimal128("0.000000000001"), "log(x, 10) should equal log10(x)", ) # Test case 2: log_e(x) == ln(x) - var val2 = Decimal(5) - var log_base_e_val2 = val2.log(Decimal.E()) + var val2 = Decimal128(5) + var log_base_e_val2 = val2.log(Decimal128.E()) var ln_val2 = val2.ln() testing.assert_true( - abs(log_base_e_val2 - ln_val2) < Decimal("0.000000000001"), + abs(log_base_e_val2 - ln_val2) < Decimal128("0.000000000001"), "log(x, e) should equal ln(x)", ) diff --git a/tests/decimal/test_decimal_log10.mojo b/tests/decimal128/test_decimal128_log10.mojo similarity index 82% rename from tests/decimal/test_decimal_log10.mojo rename to tests/decimal128/test_decimal128_log10.mojo index 55be121..28daa1a 100644 --- a/tests/decimal/test_decimal_log10.mojo +++ b/tests/decimal128/test_decimal128_log10.mojo @@ -1,10 +1,10 @@ """ -Comprehensive tests for the log10() function of the Decimal type. +Comprehensive tests for the log10() function of the Decimal128 type. Tests various scenarios to ensure proper calculation of base-10 logarithms. """ import testing -from decimojo.prelude import dm, Decimal, RoundingMode +from decimojo.prelude import dm, Decimal128, RoundingMode fn test_basic_log10() raises: @@ -12,42 +12,42 @@ fn test_basic_log10() raises: print("Testing basic log10 calculations...") # Test case 1: log10(1) = 0 - var val1 = Decimal(1) + var val1 = Decimal128(1) var result1 = val1.log10() testing.assert_equal( String(result1), "0", "log10(1) should be 0, got " + String(result1) ) # Test case 2: log10(10) = 1 - var val2 = Decimal(10) + var val2 = Decimal128(10) var result2 = val2.log10() testing.assert_equal( String(result2), "1", "log10(10) should be 1, got " + String(result2) ) # Test case 3: log10(100) = 2 - var val3 = Decimal(100) + var val3 = Decimal128(100) var result3 = val3.log10() testing.assert_equal( String(result3), "2", "log10(100) should be 2, got " + String(result3) ) # Test case 4: log10(1000) = 3 - var val4 = Decimal(1000) + var val4 = Decimal128(1000) var result4 = val4.log10() testing.assert_equal( String(result4), "3", "log10(1000) should be 3, got " + String(result4) ) # Test case 5: log10(0.1) = -1 - var val5 = Decimal("0.1") + var val5 = Decimal128("0.1") var result5 = val5.log10() testing.assert_equal( String(result5), "-1", "log10(0.1) should be -1, got " + String(result5) ) # Test case 6: log10(0.01) = -2 - var val6 = Decimal("0.01") + var val6 = Decimal128("0.01") var result6 = val6.log10() testing.assert_equal( String(result6), @@ -63,7 +63,7 @@ fn test_non_powers_of_ten() raises: print("Testing log10 of non-powers of 10...") # Test case 1: log10(2) - var val1 = Decimal(2) + var val1 = Decimal128(2) var result1 = val1.log10() testing.assert_true( String(result1).startswith("0.301029995663981"), @@ -72,7 +72,7 @@ fn test_non_powers_of_ten() raises: ) # Test case 2: log10(5) - var val2 = Decimal(5) + var val2 = Decimal128(5) var result2 = val2.log10() testing.assert_true( String(result2).startswith("0.698970004336018"), @@ -81,7 +81,7 @@ fn test_non_powers_of_ten() raises: ) # Test case 3: log10(3) - var val3 = Decimal(3) + var val3 = Decimal128(3) var result3 = val3.log10() testing.assert_true( String(result3).startswith("0.477121254719662"), @@ -90,7 +90,7 @@ fn test_non_powers_of_ten() raises: ) # Test case 4: log10(7) - var val4 = Decimal(7) + var val4 = Decimal128(7) var result4 = val4.log10() testing.assert_true( String(result4).startswith("0.845098040014256"), @@ -99,7 +99,7 @@ fn test_non_powers_of_ten() raises: ) # Test case 5: log10(0.5) - var val5 = Decimal("0.5") + var val5 = Decimal128("0.5") var result5 = val5.log10() testing.assert_true( String(result5).startswith("-0.301029995663981"), @@ -115,7 +115,7 @@ fn test_edge_cases() raises: print("Testing log10 edge cases...") # Test case 1: log10 of a negative number (should raise error) - var val1 = Decimal(-10) + var val1 = Decimal128(-10) var exception_caught = False try: var _result1 = val1.log10() @@ -129,7 +129,7 @@ fn test_edge_cases() raises: ) # Test case 2: log10 of zero (should raise error) - var val2 = Decimal(0) + var val2 = Decimal128(0) exception_caught = False try: var _result2 = val2.log10() @@ -141,15 +141,15 @@ fn test_edge_cases() raises: ) # Test case 3: log10 of value very close to 1 - var val3 = Decimal("1.0000000001") + var val3 = Decimal128("1.0000000001") var result3 = val3.log10() testing.assert_true( - abs(result3) < Decimal("0.0000001"), + abs(result3) < Decimal128("0.0000001"), "log10 of value very close to 1 should be very close to 0", ) # Test case 4: log10 of a very large number - var val4 = Decimal("1" + "0" * 20) # 10^20 + var val4 = Decimal128("1" + "0" * 20) # 10^20 var result4 = val4.log10() testing.assert_equal( String(result4), @@ -158,7 +158,7 @@ fn test_edge_cases() raises: ) # Test case 5: log10 of a very small number - var val5 = Decimal("0." + "0" * 19 + "1") # 10^-20 + var val5 = Decimal128("0." + "0" * 19 + "1") # 10^-20 var result5 = val5.log10() testing.assert_equal( String(result5), @@ -174,7 +174,7 @@ fn test_precision() raises: print("Testing log10 precision...") # Test case 1: High precision decimal - var val1 = Decimal("3.14159265358979323846") + var val1 = Decimal128("3.14159265358979323846") var result1 = val1.log10() testing.assert_true( String(result1).startswith("0.497149872694133"), @@ -182,7 +182,7 @@ fn test_precision() raises: ) # Test case 2: Special value - e - var val2 = Decimal.E() + var val2 = Decimal128.E() var result2 = val2.log10() testing.assert_true( String(result2).startswith("0.434294481903251"), @@ -191,16 +191,16 @@ fn test_precision() raises: ) # Test case 3: Check against known value - var val3 = Decimal(2) + var val3 = Decimal128(2) var result3 = val3.log10() testing.assert_true( - abs(result3 - Decimal("0.301029995663981")) - < Decimal("0.000000000000001"), + abs(result3 - Decimal128("0.301029995663981")) + < Decimal128("0.000000000000001"), "log10(2) should match high precision value", ) # Test case 4: Check precision with a number close to a power of 10 - var val4 = Decimal("9.999999999") + var val4 = Decimal128("9.999999999") var result4 = val4.log10() testing.assert_true( String(result4).startswith("0.999999999"), @@ -215,45 +215,45 @@ fn test_mathematical_properties() raises: print("Testing mathematical properties of log10...") # Test case 1: log10(a*b) = log10(a) + log10(b) - var a1 = Decimal(2) - var b1 = Decimal(5) + var a1 = Decimal128(2) + var b1 = Decimal128(5) var product1 = a1 * b1 var log_product1 = product1.log10() var sum_logs1 = a1.log10() + b1.log10() testing.assert_true( - abs(log_product1 - sum_logs1) < Decimal("0.000000000001"), + abs(log_product1 - sum_logs1) < Decimal128("0.000000000001"), "log10(a*b) should equal log10(a) + log10(b)", ) # Test case 2: log10(a/b) = log10(a) - log10(b) - var a2 = Decimal(8) - var b2 = Decimal(2) + var a2 = Decimal128(8) + var b2 = Decimal128(2) var quotient2 = a2 / b2 var log_quotient2 = quotient2.log10() var diff_logs2 = a2.log10() - b2.log10() testing.assert_true( - abs(log_quotient2 - diff_logs2) < Decimal("0.000000000001"), + abs(log_quotient2 - diff_logs2) < Decimal128("0.000000000001"), "log10(a/b) should equal log10(a) - log10(b)", ) # Test case 3: log10(a^n) = n * log10(a) - var a3 = Decimal(3) + var a3 = Decimal128(3) var n3 = 4 var power3 = a3**n3 var log_power3 = power3.log10() - var n_times_log3 = Decimal(n3) * a3.log10() + var n_times_log3 = Decimal128(n3) * a3.log10() testing.assert_true( - abs(log_power3 - n_times_log3) < Decimal("0.000000000001"), + abs(log_power3 - n_times_log3) < Decimal128("0.000000000001"), "log10(a^n) should equal n * log10(a)", ) # Test case 4: log10(1/a) = -log10(a) - var a4 = Decimal(7) - var inverse4 = Decimal(1) / a4 + var a4 = Decimal128(7) + var inverse4 = Decimal128(1) / a4 var log_inverse4 = inverse4.log10() var neg_log4 = -a4.log10() testing.assert_true( - abs(log_inverse4 - neg_log4) < Decimal("0.000000000001"), + abs(log_inverse4 - neg_log4) < Decimal128("0.000000000001"), "log10(1/a) should equal -log10(a)", ) @@ -265,22 +265,22 @@ fn test_consistency_with_other_logarithms() raises: print("Testing consistency with other logarithm functions...") # Test case 1: log10(x) = ln(x) / ln(10) - var val1 = Decimal(7) + var val1 = Decimal128(7) var log10_val1 = val1.log10() var ln_val1 = val1.ln() - var ln_10 = Decimal(10).ln() + var ln_10 = Decimal128(10).ln() var ln_ratio1 = ln_val1 / ln_10 testing.assert_true( - abs(log10_val1 - ln_ratio1) < Decimal("0.000000000001"), + abs(log10_val1 - ln_ratio1) < Decimal128("0.000000000001"), "log10(x) should equal ln(x) / ln(10)", ) # Test case 2: log10(x) = log(x, 10) [using base 10 in generic log function] - var val2 = Decimal(5) + var val2 = Decimal128(5) var log10_val2 = val2.log10() - var log_base10_val2 = val2.log(Decimal(10)) + var log_base10_val2 = val2.log(Decimal128(10)) testing.assert_true( - abs(log10_val2 - log_base10_val2) < Decimal("0.000000000001"), + abs(log10_val2 - log_base10_val2) < Decimal128("0.000000000001"), "log10(x) should equal log(x, 10)", ) diff --git a/tests/decimal/test_decimal_modulo.mojo b/tests/decimal128/test_decimal128_modulo.mojo similarity index 79% rename from tests/decimal/test_decimal_modulo.mojo rename to tests/decimal128/test_decimal128_modulo.mojo index fbf0d15..18c6c05 100644 --- a/tests/decimal/test_decimal_modulo.mojo +++ b/tests/decimal128/test_decimal128_modulo.mojo @@ -1,10 +1,10 @@ """ -Comprehensive tests for the modulo (%) operation of the Decimal type. +Comprehensive tests for the modulo (%) operation of the Decimal128 type. Tests various scenarios to ensure proper remainder calculation behavior. """ import testing -from decimojo.prelude import dm, Decimal, RoundingMode +from decimojo.prelude import dm, Decimal128, RoundingMode fn test_basic_modulo() raises: @@ -12,24 +12,24 @@ fn test_basic_modulo() raises: print("Testing basic modulo operations...") # Test case 1: Simple modulo with remainder - var a1 = Decimal(10) - var b1 = Decimal(3) + var a1 = Decimal128(10) + var b1 = Decimal128(3) var result1 = a1 % b1 testing.assert_equal( String(result1), "1", "10 % 3 should equal 1, got " + String(result1) ) # Test case 2: Modulo with no remainder - var a2 = Decimal(10) - var b2 = Decimal(5) + var a2 = Decimal128(10) + var b2 = Decimal128(5) var result2 = a2 % b2 testing.assert_equal( String(result2), "0", "10 % 5 should equal 0, got " + String(result2) ) # Test case 3: Modulo with decimal values - var a3 = Decimal("10.5") - var b3 = Decimal("3.5") + var a3 = Decimal128("10.5") + var b3 = Decimal128("3.5") var result3 = a3 % b3 testing.assert_equal( String(result3), @@ -38,8 +38,8 @@ fn test_basic_modulo() raises: ) # Test case 4: Modulo with different decimal places - var a4 = Decimal("10.75") - var b4 = Decimal("2.5") + var a4 = Decimal128("10.75") + var b4 = Decimal128("2.5") var result4 = a4 % b4 testing.assert_equal( String(result4), @@ -48,8 +48,8 @@ fn test_basic_modulo() raises: ) # Test case 5: Modulo with modulus > dividend - var a5 = Decimal(3) - var b5 = Decimal(10) + var a5 = Decimal128(3) + var b5 = Decimal128(10) var result5 = a5 % b5 testing.assert_equal( String(result5), "3", "3 % 10 should equal 3, got " + String(result5) @@ -63,24 +63,24 @@ fn test_negative_modulo() raises: print("Testing modulo with negative numbers...") # Test case 1: Negative dividend, positive divisor - var a1 = Decimal(-10) - var b1 = Decimal(3) + var a1 = Decimal128(-10) + var b1 = Decimal128(3) var result1 = a1 % b1 testing.assert_equal( String(result1), "-1", "-10 % 3 should equal -1, got " + String(result1) ) # Test case 2: Positive dividend, negative divisor - var a2 = Decimal(10) - var b2 = Decimal(-3) + var a2 = Decimal128(10) + var b2 = Decimal128(-3) var result2 = a2 % b2 testing.assert_equal( String(result2), "1", "10 % -3 should equal 1, got " + String(result2) ) # Test case 3: Negative dividend, negative divisor - var a3 = Decimal(-10) - var b3 = Decimal(-3) + var a3 = Decimal128(-10) + var b3 = Decimal128(-3) var result3 = a3 % b3 testing.assert_equal( String(result3), @@ -88,9 +88,9 @@ fn test_negative_modulo() raises: "-10 % -3 should equal -1, got " + String(result3), ) - # Test case 4: Decimal values, Negative dividend, positive divisor - var a4 = Decimal("-10.5") - var b4 = Decimal("3.5") + # Test case 4: Decimal128 values, Negative dividend, positive divisor + var a4 = Decimal128("-10.5") + var b4 = Decimal128("3.5") var result4 = a4 % b4 testing.assert_equal( String(result4), @@ -98,9 +98,9 @@ fn test_negative_modulo() raises: "-10.5 % 3.5 should equal 0.0, got " + String(result4), ) - # Test case 5: Decimal values with remainder, Negative dividend, positive divisor - var a5 = Decimal("-10.5") - var b5 = Decimal("3") + # Test case 5: Decimal128 values with remainder, Negative dividend, positive divisor + var a5 = Decimal128("-10.5") + var b5 = Decimal128("3") var result5 = a5 % b5 testing.assert_equal( String(result5), @@ -108,9 +108,9 @@ fn test_negative_modulo() raises: "-10.5 % 3 should equal -1.5, got " + String(result5), ) - # Test case 6: Decimal values with remainder, Positive dividend, negative divisor - var a6 = Decimal("10.5") - var b6 = Decimal("-3") + # Test case 6: Decimal128 values with remainder, Positive dividend, negative divisor + var a6 = Decimal128("10.5") + var b6 = Decimal128("-3") var result6 = a6 % b6 testing.assert_equal( String(result6), @@ -126,24 +126,24 @@ fn test_edge_cases() raises: print("Testing modulo edge cases...") # Test case 1: Modulo by 1 - var a1 = Decimal(10) - var b1 = Decimal(1) + var a1 = Decimal128(10) + var b1 = Decimal128(1) var result1 = a1 % b1 testing.assert_equal( String(result1), "0", "10 % 1 should equal 0, got " + String(result1) ) # Test case 2: Zero dividend - var a2 = Decimal(0) - var b2 = Decimal(5) + var a2 = Decimal128(0) + var b2 = Decimal128(5) var result2 = a2 % b2 testing.assert_equal( String(result2), "0", "0 % 5 should equal 0, got " + String(result2) ) # Test case 3: Modulo with a decimal < 1 - var a3 = Decimal(10) - var b3 = Decimal("0.3") + var a3 = Decimal128(10) + var b3 = Decimal128("0.3") var result3 = a3 % b3 testing.assert_equal( String(result3), @@ -152,8 +152,8 @@ fn test_edge_cases() raises: ) # Test case 4: Modulo by zero (should raise error) - var a4 = Decimal(10) - var b4 = Decimal(0) + var a4 = Decimal128(10) + var b4 = Decimal128(0) var exception_caught = False try: var _result4 = a4 % b4 @@ -165,16 +165,16 @@ fn test_edge_cases() raises: ) # Test case 5: Large number modulo - var a5 = Decimal("1000000007") - var b5 = Decimal("13") + var a5 = Decimal128("1000000007") + var b5 = Decimal128("13") var result5 = a5 % b5 testing.assert_equal( String(result5), "6", "1000000007 % 13 calculated incorrectly" ) # Test case 6: Small number modulo - var a6 = Decimal("0.0000023") - var b6 = Decimal("0.0000007") + var a6 = Decimal128("0.0000023") + var b6 = Decimal128("0.0000007") var result6 = a6 % b6 testing.assert_equal( String(result6), @@ -183,8 +183,8 @@ fn test_edge_cases() raises: ) # Test case 7: Equal values - var a7 = Decimal("7.5") - var b7 = Decimal("7.5") + var a7 = Decimal128("7.5") + var b7 = Decimal128("7.5") var result7 = a7 % b7 testing.assert_equal( String(result7), @@ -200,8 +200,8 @@ fn test_mathematical_relationships() raises: print("Testing mathematical relationships...") # Test case 1: a = (a // b) * b + (a % b) - var a1 = Decimal(10) - var b1 = Decimal(3) + var a1 = Decimal128(10) + var b1 = Decimal128(3) var floor_div = a1 // b1 var mod_result = a1 % b1 var reconstructed = floor_div * b1 + mod_result @@ -212,17 +212,17 @@ fn test_mathematical_relationships() raises: ) # Test case 2: 0 <= (a % b) < b for positive b - var a2 = Decimal("10.5") - var b2 = Decimal("3.2") + var a2 = Decimal128("10.5") + var b2 = Decimal128("3.2") var mod_result2 = a2 % b2 testing.assert_true( - (mod_result2 >= Decimal(0)) and (mod_result2 < b2), + (mod_result2 >= Decimal128(0)) and (mod_result2 < b2), "For positive b, 0 <= (a % b) < b should hold", ) # Test case 3: Relationship with negative values - var a3 = Decimal(-10) - var b3 = Decimal(3) + var a3 = Decimal128(-10) + var b3 = Decimal128(3) var floor_div3 = a3 // b3 var mod_result3 = a3 % b3 var reconstructed3 = floor_div3 * b3 + mod_result3 @@ -233,17 +233,17 @@ fn test_mathematical_relationships() raises: ) # Test case 4: a % b for negative b - var a4 = Decimal("10.5") - var b4 = Decimal("-3.2") + var a4 = Decimal128("10.5") + var b4 = Decimal128("-3.2") var mod_result4 = a4 % b4 testing.assert_true( - mod_result4 == Decimal("0.9"), + mod_result4 == Decimal128("0.9"), "10.5 % -3.2 should equal 0.9, got " + String(mod_result4), ) # Test case 5: (a % b) % b = a % b - var a5 = Decimal(17) - var b5 = Decimal(5) + var a5 = Decimal128(17) + var b5 = Decimal128(5) var mod_once = a5 % b5 var mod_twice = mod_once % b5 testing.assert_equal( @@ -258,8 +258,8 @@ fn test_consistency_with_floor_division() raises: print("Testing consistency with floor division...") # Test case 1: a % b and a - (a // b) * b - var a1 = Decimal(10) - var b1 = Decimal(3) + var a1 = Decimal128(10) + var b1 = Decimal128(3) var mod_result = a1 % b1 var floor_div = a1 // b1 var calc_mod = a1 - floor_div * b1 @@ -270,8 +270,8 @@ fn test_consistency_with_floor_division() raises: ) # Test case 2: Consistency with negative values - var a2 = Decimal(-10) - var b2 = Decimal(3) + var a2 = Decimal128(-10) + var b2 = Decimal128(3) var mod_result2 = a2 % b2 var floor_div2 = a2 // b2 var calc_mod2 = a2 - floor_div2 * b2 @@ -282,8 +282,8 @@ fn test_consistency_with_floor_division() raises: ) # Test case 3: Consistency with decimal values - var a3 = Decimal("10.5") - var b3 = Decimal("2.5") + var a3 = Decimal128("10.5") + var b3 = Decimal128("2.5") var mod_result3 = a3 % b3 var floor_div3 = a3 // b3 var calc_mod3 = a3 - floor_div3 * b3 @@ -294,8 +294,8 @@ fn test_consistency_with_floor_division() raises: ) # Test case 4: Consistency with mixed positive and negative values - var a4 = Decimal(10) - var b4 = Decimal(-3) + var a4 = Decimal128(10) + var b4 = Decimal128(-3) var mod_result4 = a4 % b4 var floor_div4 = a4 // b4 var calc_mod4 = a4 - floor_div4 * b4 @@ -327,7 +327,7 @@ fn run_test_with_error_handling( fn main() raises: print("=========================================") - print("Running Decimal Modulo Tests") + print("Running Decimal128 Modulo Tests") print("=========================================") run_test_with_error_handling( diff --git a/tests/decimal/test_decimal_multiply.mojo b/tests/decimal128/test_decimal128_multiply.mojo similarity index 78% rename from tests/decimal/test_decimal_multiply.mojo rename to tests/decimal128/test_decimal128_multiply.mojo index 8954518..b9a9418 100644 --- a/tests/decimal/test_decimal_multiply.mojo +++ b/tests/decimal128/test_decimal128_multiply.mojo @@ -1,9 +1,9 @@ """ -Comprehensive tests for the multiplication operation of the Decimal type. +Comprehensive tests for the multiplication operation of the Decimal128 type. """ import testing -from decimojo.prelude import dm, Decimal, RoundingMode +from decimojo.prelude import dm, Decimal128, RoundingMode fn test_basic_multiplication() raises: @@ -11,16 +11,16 @@ fn test_basic_multiplication() raises: print("Testing basic multiplication...") # Test case 1: Simple integer multiplication - var a1 = Decimal(5) - var b1 = Decimal(3) + var a1 = Decimal128(5) + var b1 = Decimal128(3) var result1 = a1 * b1 testing.assert_equal( String(result1), "15", "5 * 3 should equal 15, got " + String(result1) ) # Test case 2: Simple decimal multiplication - var a2 = Decimal("2.5") - var b2 = Decimal(4) + var a2 = Decimal128("2.5") + var b2 = Decimal128(4) var result2 = a2 * b2 testing.assert_equal( String(result2), @@ -28,9 +28,9 @@ fn test_basic_multiplication() raises: "2.5 * 4 should equal 10.0, got " + String(result2), ) - # Test case 3: Decimal * decimal - var a3 = Decimal("1.5") - var b3 = Decimal("2.5") + # Test case 3: Decimal128 * decimal + var a3 = Decimal128("1.5") + var b3 = Decimal128("2.5") var result3 = a3 * b3 testing.assert_equal( String(result3), @@ -39,8 +39,8 @@ fn test_basic_multiplication() raises: ) # Test case 4: Multiplication with more decimal places - var a4 = Decimal("3.14") - var b4 = Decimal("2.0") + var a4 = Decimal128("3.14") + var b4 = Decimal128("2.0") var result4 = a4 * b4 testing.assert_equal( String(result4), @@ -49,8 +49,8 @@ fn test_basic_multiplication() raises: ) # Test case 5: Multiplication with different decimal places - var a5 = Decimal("0.125") - var b5 = Decimal("0.4") + var a5 = Decimal128("0.125") + var b5 = Decimal128("0.4") var result5 = a5 * b5 testing.assert_equal( String(result5), @@ -66,8 +66,8 @@ fn test_special_cases() raises: print("Testing multiplication with special cases...") # Test case 1: Multiplication by zero - var a1 = Decimal("123.45") - var zero = Decimal(0) + var a1 = Decimal128("123.45") + var zero = Decimal128(0) var result1 = a1 * zero testing.assert_equal( String(result1), @@ -76,8 +76,8 @@ fn test_special_cases() raises: ) # Test case 2: Multiplication by one - var a2 = Decimal("123.45") - var one = Decimal(1) + var a2 = Decimal128("123.45") + var one = Decimal128(1) var result2 = a2 * one testing.assert_equal( String(result2), @@ -86,8 +86,8 @@ fn test_special_cases() raises: ) # Test case 3: Multiplication of zero by any number - var a3 = Decimal(0) - var b3 = Decimal("987.654") + var a3 = Decimal128(0) + var b3 = Decimal128("987.654") var result3 = a3 * b3 testing.assert_equal( String(result3), @@ -96,8 +96,8 @@ fn test_special_cases() raises: ) # Test case 4: Multiplication by negative one - var a4 = Decimal("123.45") - var neg_one = Decimal(-1) + var a4 = Decimal128("123.45") + var neg_one = Decimal128(-1) var result4 = a4 * neg_one testing.assert_equal( String(result4), @@ -106,7 +106,7 @@ fn test_special_cases() raises: ) # Test case 5: Multiplication of very small value by one - var small = Decimal("0." + "0" * 27 + "1") # Smallest representable + var small = Decimal128("0." + "0" * 27 + "1") # Smallest representable var result5 = small * one testing.assert_equal( String(result5), @@ -122,8 +122,8 @@ fn test_negative_multiplication() raises: print("Testing multiplication with negative numbers...") # Test case 1: Negative * positive - var a1 = Decimal(-5) - var b1 = Decimal(3) + var a1 = Decimal128(-5) + var b1 = Decimal128(3) var result1 = a1 * b1 testing.assert_equal( String(result1), @@ -132,8 +132,8 @@ fn test_negative_multiplication() raises: ) # Test case 2: Positive * negative - var a2 = Decimal(5) - var b2 = Decimal(-3) + var a2 = Decimal128(5) + var b2 = Decimal128(-3) var result2 = a2 * b2 testing.assert_equal( String(result2), @@ -142,16 +142,16 @@ fn test_negative_multiplication() raises: ) # Test case 3: Negative * negative - var a3 = Decimal(-5) - var b3 = Decimal(-3) + var a3 = Decimal128(-5) + var b3 = Decimal128(-3) var result3 = a3 * b3 testing.assert_equal( String(result3), "15", "-5 * -3 should equal 15, got " + String(result3) ) - # Test case 4: Decimal with negative and positive - var a4 = Decimal("-2.5") - var b4 = Decimal("4.2") + # Test case 4: Decimal128 with negative and positive + var a4 = Decimal128("-2.5") + var b4 = Decimal128("4.2") var result4 = a4 * b4 testing.assert_equal( String(result4), @@ -160,8 +160,8 @@ fn test_negative_multiplication() raises: ) # Test case 5: Negative zero (result should be zero) - var a5 = Decimal("-0") - var b5 = Decimal("123.45") + var a5 = Decimal128("-0") + var b5 = Decimal128("123.45") var result5 = a5 * b5 testing.assert_equal( String(result5), @@ -177,8 +177,8 @@ fn test_precision_scale() raises: print("Testing multiplication precision and scale...") # Test case 1: Addition of scales - var a1 = Decimal("0.5") # scale 1 - var b1 = Decimal("0.25") # scale 2 + var a1 = Decimal128("0.5") # scale 1 + var b1 = Decimal128("0.25") # scale 2 var result1 = a1 * b1 # scale should be 3 testing.assert_equal( String(result1), @@ -188,8 +188,8 @@ fn test_precision_scale() raises: testing.assert_equal(result1.scale(), 3) # Test case 2: High precision - var a2 = Decimal("0.1234567890") - var b2 = Decimal("0.9876543210") + var a2 = Decimal128("0.1234567890") + var b2 = Decimal128("0.9876543210") var result2 = a2 * b2 testing.assert_equal( String(result2), @@ -199,22 +199,22 @@ fn test_precision_scale() raises: testing.assert_equal(result2.scale(), 20) # Test case 3: Maximum scale edge case - var a3 = Decimal("0." + "1" * 14) # scale 14 - var b3 = Decimal("0." + "9" * 14) # scale 14 + var a3 = Decimal128("0." + "1" * 14) # scale 14 + var b3 = Decimal128("0." + "9" * 14) # scale 14 var result3 = a3 * b3 # would be scale 28 (just at the limit) testing.assert_equal(result3.scale(), 28, "Scale should be capped at 28") # Test case 4: Scale overflow handling (scale > 28) - var a4 = Decimal("0." + "1" * 15) # scale 15 - var b4 = Decimal("0." + "9" * 15) # scale 15 - var result4 = a4 * b4 # would be scale 30, but Decimal.MAX_SCALE is 28 + var a4 = Decimal128("0." + "1" * 15) # scale 15 + var b4 = Decimal128("0." + "9" * 15) # scale 15 + var result4 = a4 * b4 # would be scale 30, but Decimal128.MAX_SCALE is 28 testing.assert_equal( result4.scale(), 28, "Scale should be capped at MAX_SCALE (28)" ) # Test case 5: Rounding during scale adjustment - var a5 = Decimal("0.123456789012345678901234567") # scale 27 - var b5 = Decimal("0.2") # scale 1 + var a5 = Decimal128("0.123456789012345678901234567") # scale 27 + var b5 = Decimal128("0.2") # scale 1 var result5 = a5 * b5 # would be scale 28, but requires rounding testing.assert_equal( result5.scale(), 28, "Scale should be correctly adjusted with rounding" @@ -228,15 +228,15 @@ fn test_boundary_cases() raises: print("Testing multiplication with boundary values...") # Test case 1: Multiplication near max value - var near_max = Decimal("38614081257132168796771975168") # ~half max - var result1 = near_max * Decimal("1.9") # Almost 2x max + var near_max = Decimal128("38614081257132168796771975168") # ~half max + var result1 = near_max * Decimal128("1.9") # Almost 2x max testing.assert_true( - result1 < Decimal.MAX(), "Result should be less than MAX value" + result1 < Decimal128.MAX(), "Result should be less than MAX value" ) # Test case 2: Zero scale result with different input scales - var a2 = Decimal("0.5") - var b2 = Decimal("2.0") + var a2 = Decimal128("0.5") + var b2 = Decimal128("2.0") var result2 = a2 * b2 testing.assert_equal( String(result2), @@ -245,8 +245,8 @@ fn test_boundary_cases() raises: ) # Test case 3: Very different scales - var tiny = Decimal("0." + "0" * 20 + "1") # Very small - var huge = Decimal("1" + "0" * 20) # Very large + var tiny = Decimal128("0." + "0" * 20 + "1") # Very small + var huge = Decimal128("1" + "0" * 20) # Very large var result3 = tiny * huge testing.assert_equal( String(result3), @@ -255,8 +255,8 @@ fn test_boundary_cases() raises: ) # Test case 4: Multiplication at max value - var max_dec = Decimal.MAX() - var one_hundredth = Decimal("0.01") + var max_dec = Decimal128.MAX() + var one_hundredth = Decimal128("0.01") var result4 = max_dec * one_hundredth testing.assert_equal( String(result4), @@ -265,8 +265,8 @@ fn test_boundary_cases() raises: ) # Test case 5: Result has trailing zeros with integer - var a5 = Decimal("1.25") - var b5 = Decimal(4) + var a5 = Decimal128("1.25") + var b5 = Decimal128(4) var result5 = a5 * b5 testing.assert_equal( String(result5), @@ -283,8 +283,8 @@ fn test_commutative_property() raises: print("Testing commutative property of multiplication...") # Test pair 1: Integers - var a1 = Decimal(10) - var b1 = Decimal(20) + var a1 = Decimal128(10) + var b1 = Decimal128(20) var result1a = a1 * b1 var result1b = b1 * a1 testing.assert_equal( @@ -294,8 +294,8 @@ fn test_commutative_property() raises: ) # Test pair 2: Mixed decimal and integer - var a2 = Decimal("3.5") - var b2 = Decimal(2) + var a2 = Decimal128("3.5") + var b2 = Decimal128(2) var result2a = a2 * b2 var result2b = b2 * a2 testing.assert_equal( @@ -305,8 +305,8 @@ fn test_commutative_property() raises: ) # Test pair 3: Negative and positive - var a3 = Decimal(-5) - var b3 = Decimal(7) + var a3 = Decimal128(-5) + var b3 = Decimal128(7) var result3a = a3 * b3 var result3b = b3 * a3 testing.assert_equal( @@ -316,8 +316,8 @@ fn test_commutative_property() raises: ) # Test pair 4: Small and large decimal - var a4 = Decimal("0.123") - var b4 = Decimal("9.87") + var a4 = Decimal128("0.123") + var b4 = Decimal128("9.87") var result4a = a4 * b4 var result4b = b4 * a4 testing.assert_equal( @@ -327,8 +327,8 @@ fn test_commutative_property() raises: ) # Test pair 5: Very small and very large - var a5 = Decimal("0.0001") - var b5 = Decimal(10000) + var a5 = Decimal128("0.0001") + var b5 = Decimal128(10000) var result5a = a5 * b5 var result5b = b5 * a5 testing.assert_equal( @@ -359,7 +359,7 @@ fn run_test_with_error_handling( fn main() raises: print("=========================================") - print("Running Decimal Multiplication Tests") + print("Running Decimal128 Multiplication Tests") print("=========================================") run_test_with_error_handling( diff --git a/tests/decimal/test_decimal_power.mojo b/tests/decimal128/test_decimal128_power.mojo similarity index 76% rename from tests/decimal/test_decimal_power.mojo rename to tests/decimal128/test_decimal128_power.mojo index 50c6170..2df14e7 100644 --- a/tests/decimal/test_decimal_power.mojo +++ b/tests/decimal128/test_decimal128_power.mojo @@ -1,18 +1,18 @@ """ -Comprehensive tests for the power function of the Decimal type. +Comprehensive tests for the power function of the Decimal128 type. """ import testing -from decimojo.prelude import dm, Decimal, RoundingMode -from decimojo.decimal.exponential import power +from decimojo.prelude import dm, Decimal128, RoundingMode +from decimojo.decimal128.exponential import power fn test_integer_powers() raises: - """Test raising a Decimal to an integer power.""" + """Test raising a Decimal128 to an integer power.""" print("Testing integer powers...") # Test case 1: Positive base, positive exponent - var base1 = Decimal(2) + var base1 = Decimal128(2) var exponent1 = 3 var result1 = power(base1, exponent1) testing.assert_equal( @@ -20,7 +20,7 @@ fn test_integer_powers() raises: ) # Test case 2: Positive base, zero exponent - var base2 = Decimal(5) + var base2 = Decimal128(5) var exponent2 = 0 var result2 = power(base2, exponent2) testing.assert_equal( @@ -28,23 +28,23 @@ fn test_integer_powers() raises: ) # Test case 3: Positive base, negative exponent - var base3 = Decimal(2) + var base3 = Decimal128(2) var exponent3 = -2 var result3 = power(base3, exponent3) testing.assert_equal( String(result3), "0.25", "2^-2 should be 0.25, got " + String(result3) ) - # Test case 4: Decimal base, positive exponent - var base4 = Decimal("2.5") + # Test case 4: Decimal128 base, positive exponent + var base4 = Decimal128("2.5") var exponent4 = 2 var result4 = power(base4, exponent4) testing.assert_equal( String(result4), "6.25", "2.5^2 should be 6.25, got " + String(result4) ) - # Test case 5: Decimal base, negative exponent - var base5 = Decimal("0.5") + # Test case 5: Decimal128 base, negative exponent + var base5 = Decimal128("0.5") var exponent5 = -1 var result5 = power(base5, exponent5) testing.assert_equal( @@ -55,29 +55,29 @@ fn test_integer_powers() raises: fn test_decimal_powers() raises: - """Test raising a Decimal to a Decimal power.""" + """Test raising a Decimal128 to a Decimal128 power.""" print("Testing decimal powers...") # Test case 1: Positive base, simple fractional exponent (0.5) - var base1 = Decimal(9) - var exponent1 = Decimal("0.5") + var base1 = Decimal128(9) + var exponent1 = Decimal128("0.5") var result1 = power(base1, exponent1) testing.assert_equal( String(result1), "3", "9^0.5 should be 3, got " + String(result1) ) # Test case 2: Positive base, more complex fractional exponent - var base2 = Decimal(2) - var exponent2 = Decimal("1.5") + var base2 = Decimal128(2) + var exponent2 = Decimal128("1.5") var result2 = power(base2, exponent2) testing.assert_true( String(result2).startswith("2.828427124746190097603377448"), "2^1.5 should be approximately 2.828..., got " + String(result2), ) - # Test case 3: Decimal base, decimal exponent - var base3 = Decimal("2.5") - var exponent3 = Decimal("0.5") + # Test case 3: Decimal128 base, decimal exponent + var base3 = Decimal128("2.5") + var exponent3 = Decimal128("0.5") var result3 = power(base3, exponent3) testing.assert_true( String(result3).startswith("1.5811388300841896659994467722"), @@ -87,14 +87,14 @@ fn test_decimal_powers() raises: ) # Test case 4: Base > 1, exponent < 0 - var base4 = Decimal(4) - var exponent4 = Decimal("-0.5") + var base4 = Decimal128(4) + var exponent4 = Decimal128("-0.5") var result4 = power(base4, exponent4) testing.assert_equal( String(result4), "0.5", "4^-0.5 should be 0.5, got " + String(result4) ) - print("✓ Decimal powers tests passed!") + print("✓ Decimal128 powers tests passed!") fn test_edge_cases() raises: @@ -102,16 +102,16 @@ fn test_edge_cases() raises: print("Testing power edge cases...") # Test case 1: Zero base, positive exponent - var base1 = Decimal(0) - var exponent1 = Decimal(2) + var base1 = Decimal128(0) + var exponent1 = Decimal128(2) var result1 = power(base1, exponent1) testing.assert_equal( String(result1), "0", "0^2 should be 0, got " + String(result1) ) # Test case 2: Zero base, negative exponent (should raise error) - var base2 = Decimal(0) - var exponent2 = Decimal(-2) + var base2 = Decimal128(0) + var exponent2 = Decimal128(-2) var exception_caught = False try: var _result = power(base2, exponent2) @@ -125,16 +125,16 @@ fn test_edge_cases() raises: ) # Test case 3: Negative base, integer exponent - var base3 = Decimal(-2) - var exponent3 = Decimal(3) + var base3 = Decimal128(-2) + var exponent3 = Decimal128(3) var result3 = power(base3, exponent3) testing.assert_equal( String(result3), "-8", "(-2)^3 should be -8, got " + String(result3) ) # Test case 4: Negative base, non-integer exponent (should raise error) - var base4 = Decimal(-2) - var exponent4 = Decimal("0.5") + var base4 = Decimal128(-2) + var exponent4 = Decimal128("0.5") exception_caught = False try: var _result2 = power(base4, exponent4) @@ -169,11 +169,11 @@ fn run_test_with_error_handling( fn main() raises: print("=========================================") - print("Running Decimal Power Function Tests") + print("Running Decimal128 Power Function Tests") print("=========================================") run_test_with_error_handling(test_integer_powers, "Integer powers test") - run_test_with_error_handling(test_decimal_powers, "Decimal powers test") + run_test_with_error_handling(test_decimal_powers, "Decimal128 powers test") run_test_with_error_handling(test_edge_cases, "Edge cases test") print("All power function tests passed!") diff --git a/tests/decimal/test_decimal_quantize.mojo b/tests/decimal128/test_decimal128_quantize.mojo similarity index 91% rename from tests/decimal/test_decimal_quantize.mojo rename to tests/decimal128/test_decimal128_quantize.mojo index ce94fa9..50dba0d 100644 --- a/tests/decimal/test_decimal_quantize.mojo +++ b/tests/decimal128/test_decimal128_quantize.mojo @@ -1,12 +1,12 @@ """ -Comprehensive tests for the Decimal.quantize() method. +Comprehensive tests for the Decimal128.quantize() method. Tests various scenarios to ensure proper quantization behavior and compatibility with Python's decimal module implementation. """ import testing from python import Python, PythonObject -from decimojo.prelude import dm, Decimal, RoundingMode +from decimojo.prelude import dm, Decimal128, RoundingMode fn test_basic_quantization() raises: @@ -16,8 +16,8 @@ fn test_basic_quantization() raises: var pydecimal = Python.import_module("decimal") pydecimal.getcontext().prec = 28 # Match DeciMojo's precision - var value1 = Decimal("3.14159") - var quant1 = Decimal("0.01") + var value1 = Decimal128("3.14159") + var quant1 = Decimal128("0.01") var result1 = value1.quantize(quant1) var py_value1 = pydecimal.Decimal("3.14159") var py_quant1 = pydecimal.Decimal("0.01") @@ -29,8 +29,8 @@ fn test_basic_quantization() raises: "Quantizing 3.14159 to 0.01 gave incorrect result: " + String(result1), ) - var value2 = Decimal("42.7") - var quant2 = Decimal("1") + var value2 = Decimal128("42.7") + var quant2 = Decimal128("1") var result2 = value2.quantize(quant2) var py_value2 = pydecimal.Decimal("42.7") var py_quant2 = pydecimal.Decimal("1") @@ -42,8 +42,8 @@ fn test_basic_quantization() raises: "Quantizing 42.7 to 1 gave incorrect result: " + String(result2), ) - var value3 = Decimal("5.5") - var quant3 = Decimal("0.001") + var value3 = Decimal128("5.5") + var quant3 = Decimal128("0.001") var result3 = value3.quantize(quant3) var py_value3 = pydecimal.Decimal("5.5") var py_quant3 = pydecimal.Decimal("0.001") @@ -55,8 +55,8 @@ fn test_basic_quantization() raises: "Quantizing 5.5 to 0.001 gave incorrect result: " + String(result3), ) - var value4 = Decimal("123.456789") - var quant4 = Decimal("0.01") + var value4 = Decimal128("123.456789") + var quant4 = Decimal128("0.01") var result4 = value4.quantize(quant4) var py_value4 = pydecimal.Decimal("123.456789") var py_quant4 = pydecimal.Decimal("0.01") @@ -69,8 +69,8 @@ fn test_basic_quantization() raises: + String(result4), ) - var value5 = Decimal("9.876") - var quant5 = Decimal("1.00") + var value5 = Decimal128("9.876") + var quant5 = Decimal128("1.00") var result5 = value5.quantize(quant5) var py_value5 = pydecimal.Decimal("9.876") var py_quant5 = pydecimal.Decimal("1.00") @@ -92,8 +92,8 @@ fn test_rounding_modes() raises: var pydecimal = Python.import_module("decimal") pydecimal.getcontext().prec = 28 - var test_value = Decimal("3.5") - var quantizer = Decimal("1") + var test_value = Decimal128("3.5") + var quantizer = Decimal128("1") var py_value = pydecimal.Decimal("3.5") var py_quantizer = pydecimal.Decimal("1") @@ -137,7 +137,7 @@ fn test_rounding_modes() raises: "ROUND_UP gave incorrect result: " + String(result4), ) - var neg_test_value = Decimal("-3.5") + var neg_test_value = Decimal128("-3.5") var result5 = neg_test_value.quantize(quantizer, RoundingMode.ROUND_DOWN) var py_neg_value = pydecimal.Decimal("-3.5") var py_result5 = py_neg_value.quantize( @@ -169,8 +169,8 @@ fn test_edge_cases() raises: var pydecimal = Python.import_module("decimal") pydecimal.getcontext().prec = 28 - var zero = Decimal("0") - var quant1 = Decimal("0.001") + var zero = Decimal128("0") + var quant1 = Decimal128("0.001") var result1 = zero.quantize(quant1) var py_zero = pydecimal.Decimal("0") var py_quant1 = pydecimal.Decimal("0.001") @@ -182,8 +182,8 @@ fn test_edge_cases() raises: "Quantizing 0 to 0.001 gave incorrect result: " + String(result1), ) - var value2 = Decimal("123.45") - var quant2 = Decimal("0.01") + var value2 = Decimal128("123.45") + var quant2 = Decimal128("0.01") var result2 = value2.quantize(quant2) var py_value2 = pydecimal.Decimal("123.45") var py_quant2 = pydecimal.Decimal("0.01") @@ -195,8 +195,8 @@ fn test_edge_cases() raises: "Quantizing to same exponent gave incorrect result: " + String(result2), ) - var value3 = Decimal("9.9999") - var quant3 = Decimal("1") + var value3 = Decimal128("9.9999") + var quant3 = Decimal128("1") var result3 = value3.quantize(quant3) var py_value3 = pydecimal.Decimal("9.9999") var py_quant3 = pydecimal.Decimal("1") @@ -208,8 +208,8 @@ fn test_edge_cases() raises: "Rounding 9.9999 to 1 gave incorrect result: " + String(result3), ) - var value4 = Decimal("0.0000001") - var quant4 = Decimal("0.001") + var value4 = Decimal128("0.0000001") + var quant4 = Decimal128("0.001") var result4 = value4.quantize(quant4) var py_value4 = pydecimal.Decimal("0.0000001") var py_quant4 = pydecimal.Decimal("0.001") @@ -222,8 +222,8 @@ fn test_edge_cases() raises: + String(result4), ) - var value5 = Decimal("-1.5") - var quant5 = Decimal("1") + var value5 = Decimal128("-1.5") + var quant5 = Decimal128("1") var result5 = value5.quantize(quant5, RoundingMode.ROUND_HALF_EVEN) var py_value5 = pydecimal.Decimal("-1.5") var py_quant5 = pydecimal.Decimal("1") @@ -248,8 +248,8 @@ fn test_special_cases() raises: var pydecimal = Python.import_module("decimal") pydecimal.getcontext().prec = 28 - var value1 = Decimal("12.34") - var quant1 = Decimal("0.0000") + var value1 = Decimal128("12.34") + var quant1 = Decimal128("0.0000") var result1 = value1.quantize(quant1) var py_value1 = pydecimal.Decimal("12.34") var py_quant1 = pydecimal.Decimal("0.0000") @@ -261,8 +261,8 @@ fn test_special_cases() raises: "Increasing precision gave incorrect result: " + String(result1), ) - var value2 = Decimal("2.5") - var quant2 = Decimal("1") + var value2 = Decimal128("2.5") + var quant2 = Decimal128("1") var result2 = value2.quantize(quant2, RoundingMode.ROUND_HALF_EVEN) var py_value2 = pydecimal.Decimal("2.5") var py_quant2 = pydecimal.Decimal("1") @@ -276,8 +276,8 @@ fn test_special_cases() raises: "Banker's rounding for 2.5 gave incorrect result: " + String(result2), ) - var value3 = Decimal("123.456") - var quant3 = Decimal("10") + var value3 = Decimal128("123.456") + var quant3 = Decimal128("10") var result3 = value3.quantize(quant3) var py_value3 = pydecimal.Decimal("123.456") var py_quant3 = pydecimal.Decimal("10") @@ -290,8 +290,8 @@ fn test_special_cases() raises: + String(result3), ) - var value4 = Decimal("3.1415926535") - var quant4 = Decimal("0.00000001") + var value4 = Decimal128("3.1415926535") + var quant4 = Decimal128("0.00000001") var result4 = value4.quantize(quant4) var py_value4 = pydecimal.Decimal("3.1415926535") var py_quant4 = pydecimal.Decimal("0.00000001") @@ -303,8 +303,8 @@ fn test_special_cases() raises: "Very precise quantization gave incorrect result: " + String(result4), ) - var value5 = Decimal("123.456") - var quant5 = Decimal("1") + var value5 = Decimal128("123.456") + var quant5 = Decimal128("1") var result5 = value5.quantize(quant5) var py_value5 = pydecimal.Decimal("123.456") var py_quant5 = pydecimal.Decimal("1") @@ -328,8 +328,8 @@ fn test_quantize_exceptions() raises: var exception_caught = False try: - var value1 = Decimal("123.456") - var quant1 = Decimal("1000") + var value1 = Decimal128("123.456") + var quant1 = Decimal128("1000") var _result1 = value1.quantize(quant1) except: exception_caught = True @@ -387,7 +387,7 @@ fn test_comprehensive_comparison() raises: ) test_single_quantize_case("0", "1", mojo_round_up, py_round_up, pydecimal) - # Test case 2: Decimal with 2 decimal places quantizer + # Test case 2: Decimal128 with 2 decimal places quantizer test_single_quantize_case( "1.23456", "0.01", mojo_round_half_even, py_round_half_even, pydecimal ) @@ -401,7 +401,7 @@ fn test_comprehensive_comparison() raises: "1.23456", "0.01", mojo_round_up, py_round_up, pydecimal ) - # Test case 3: Decimal with 1 decimal place quantizer + # Test case 3: Decimal128 with 1 decimal place quantizer test_single_quantize_case( "9.999", "0.1", mojo_round_half_even, py_round_half_even, pydecimal ) @@ -585,8 +585,8 @@ fn test_single_quantize_case( """Test a single quantize case comparing Mojo and Python implementations.""" try: - var mojo_value = Decimal(value_str) - var mojo_quant = Decimal(quant_str) + var mojo_value = Decimal128(value_str) + var mojo_quant = Decimal128(quant_str) var py_value = pydecimal.Decimal(value_str) var py_quant = pydecimal.Decimal(quant_str) @@ -628,7 +628,7 @@ fn run_test_with_error_handling( fn main() raises: print("=========================================") - print("Running Decimal.quantize() Tests") + print("Running Decimal128.quantize() Tests") print("=========================================") run_test_with_error_handling( @@ -644,4 +644,4 @@ fn main() raises: test_comprehensive_comparison, "Comprehensive comparison test" ) - print("All Decimal.quantize() tests passed!") + print("All Decimal128.quantize() tests passed!") diff --git a/tests/decimal/test_decimal_root.mojo b/tests/decimal128/test_decimal128_root.mojo similarity index 86% rename from tests/decimal/test_decimal_root.mojo rename to tests/decimal128/test_decimal128_root.mojo index 356bf8c..70d2f94 100644 --- a/tests/decimal/test_decimal_root.mojo +++ b/tests/decimal128/test_decimal128_root.mojo @@ -5,8 +5,8 @@ and edge cases to ensure proper calculation of x^(1/n). """ import testing -from decimojo.prelude import dm, Decimal, RoundingMode -from decimojo.decimal.exponential import root +from decimojo.prelude import dm, Decimal128, RoundingMode +from decimojo.decimal128.exponential import root fn test_basic_root_calculations() raises: @@ -14,28 +14,28 @@ fn test_basic_root_calculations() raises: print("Testing basic root calculations...") # Test case 1: Square root (n=2) - var num1 = Decimal(9) + var num1 = Decimal128(9) var result1 = root(num1, 2) testing.assert_equal( String(result1), "3", "√9 should be 3, got " + String(result1) ) # Test case 2: Cube root (n=3) - var num2 = Decimal(8) + var num2 = Decimal128(8) var result2 = root(num2, 3) testing.assert_equal( String(result2), "2", "∛8 should be 2, got " + String(result2) ) # Test case 3: Fourth root (n=4) - var num3 = Decimal(16) + var num3 = Decimal128(16) var result3 = root(num3, 4) testing.assert_equal( String(result3), "2", "∜16 should be 2, got " + String(result3) ) # Test case 4: Square root of non-perfect square - var num4 = Decimal(2) + var num4 = Decimal128(2) var result4 = root(num4, 2) testing.assert_true( String(result4).startswith("1.4142135623730950488"), @@ -43,7 +43,7 @@ fn test_basic_root_calculations() raises: ) # Test case 5: Cube root of non-perfect cube - var num5 = Decimal(10) + var num5 = Decimal128(10) var result5 = root(num5, 3) testing.assert_true( String(result5).startswith("2.154434690031883721"), @@ -58,29 +58,29 @@ fn test_fractional_inputs() raises: print("Testing root calculations with fractional inputs...") # Test case 1: Square root of decimal - var num1 = Decimal("0.25") + var num1 = Decimal128("0.25") var result1 = root(num1, 2) testing.assert_equal( String(result1), "0.5", "√0.25 should be 0.5, got " + String(result1) ) # Test case 2: Cube root of decimal - var num2 = Decimal("0.125") + var num2 = Decimal128("0.125") var result2 = root(num2, 3) testing.assert_equal( String(result2), "0.5", "∛0.125 should be 0.5, got " + String(result2) ) # Test case 3: High precision decimal input - var num3 = Decimal("1.44") + var num3 = Decimal128("1.44") var result3 = root(num3, 2) testing.assert_true( String(result3).startswith("1.2"), "√1.44 should be 1.2, got " + String(result3), ) - # Test case 4: Decimal input with non-integer result - var num4 = Decimal("0.5") + # Test case 4: Decimal128 input with non-integer result + var num4 = Decimal128("0.5") var result4 = root(num4, 2) testing.assert_true( String(result4).startswith("0.7071067811865475"), @@ -95,14 +95,14 @@ fn test_edge_cases() raises: print("Testing root edge cases...") # Test case 1: Root of 0 - var zero = Decimal(0) + var zero = Decimal128(0) var result1 = root(zero, 2) testing.assert_equal( String(result1), "0", "√0 should be 0, got " + String(result1) ) # Test case 2: Root of 1 - var one = Decimal(1) + var one = Decimal128(1) var result2 = root(one, 100) # Any root of 1 is 1 testing.assert_equal( String(result2), @@ -111,7 +111,7 @@ fn test_edge_cases() raises: ) # Test case 3: 1st root of any number is the number itself - var num3 = Decimal("123.456") + var num3 = Decimal128("123.456") var result3 = root(num3, 1) testing.assert_equal( String(result3), @@ -120,7 +120,7 @@ fn test_edge_cases() raises: ) # Test case 4: Very large root of a number - var num4 = Decimal(10) + var num4 = Decimal128(10) var result4 = root(num4, 100) # 100th root of 10 testing.assert_true( String(result4).startswith("1.02329299228075413096627517"), @@ -137,7 +137,7 @@ fn test_error_conditions() raises: print("Testing root error conditions...") # Test case 1: 0th root (should raise error) - var num1 = Decimal(10) + var num1 = Decimal128(10) var exception_caught = False try: var _result = root(num1, 0) @@ -149,7 +149,7 @@ fn test_error_conditions() raises: testing.assert_equal(exception_caught, True) # Test case 2: Negative root (should raise error) - var num2 = Decimal(10) + var num2 = Decimal128(10) exception_caught = False try: var _result = root(num2, -2) @@ -161,7 +161,7 @@ fn test_error_conditions() raises: testing.assert_equal(exception_caught, True) # Test case 3: Negative number with even root (should raise error) - var num3 = Decimal(-4) + var num3 = Decimal128(-4) exception_caught = False try: var _result = root(num3, 2) @@ -175,7 +175,7 @@ fn test_error_conditions() raises: testing.assert_equal(exception_caught, True) # Test case 4: Negative number with odd root (should work) - var num4 = Decimal(-8) + var num4 = Decimal128(-8) var result4 = root(num4, 3) testing.assert_equal( String(result4), "-2", "∛-8 should be -2, got " + String(result4) @@ -189,7 +189,7 @@ fn test_precision() raises: print("Testing precision of root calculations...") # Test case 1: High precision square root - var num1 = Decimal(2) + var num1 = Decimal128(2) var result1 = root(num1, 2) testing.assert_true( String(result1).startswith("1.414213562373095048801688724"), @@ -197,7 +197,7 @@ fn test_precision() raises: ) # Test case 2: High precision cube root - var num2 = Decimal(2) + var num2 = Decimal128(2) var result2 = root(num2, 3) testing.assert_true( String(result2).startswith("1.25992104989487316476721060"), @@ -205,7 +205,7 @@ fn test_precision() raises: ) # Test case 3: Compare with known precise values - var num3 = Decimal(5) + var num3 = Decimal128(5) var result3 = root(num3, 2) testing.assert_true( String(result3).startswith("2.236067977499789696"), @@ -220,40 +220,40 @@ fn test_mathematical_identities() raises: print("Testing mathematical identities involving roots...") # Test case 1: (√x)^2 = x - var x1 = Decimal(7) + var x1 = Decimal128(7) var sqrt_x1 = root(x1, 2) var squared_back = sqrt_x1 * sqrt_x1 testing.assert_true( - abs(squared_back - x1) < Decimal("0.0000000001"), + abs(squared_back - x1) < Decimal128("0.0000000001"), "(√x)^2 should equal x within tolerance", ) # Test case 2: ∛(x^3) = x - var x2 = Decimal(3) + var x2 = Decimal128(3) var cubed = x2 * x2 * x2 var root_back = root(cubed, 3) testing.assert_true( - abs(root_back - x2) < Decimal("0.0000000001"), + abs(root_back - x2) < Decimal128("0.0000000001"), "∛(x^3) should equal x within tolerance", ) # Test case 3: √(a*b) = √a * √b - var a = Decimal(4) - var b = Decimal(9) + var a = Decimal128(4) + var b = Decimal128(9) var sqrt_product = root(a * b, 2) var product_sqrts = root(a, 2) * root(b, 2) testing.assert_true( - abs(sqrt_product - product_sqrts) < Decimal("0.0000000001"), + abs(sqrt_product - product_sqrts) < Decimal128("0.0000000001"), "√(a*b) should equal √a * √b within tolerance", ) # Test case 4: Consistency with power function: x^(1/n) = nth root of x - var x4 = Decimal(5) + var x4 = Decimal128(5) var n = 3 # Cube root - var power_result = x4 ** (Decimal(1) / Decimal(n)) + var power_result = x4 ** (Decimal128(1) / Decimal128(n)) var root_result = root(x4, n) testing.assert_true( - abs(power_result - root_result) < Decimal("0.0000000001"), + abs(power_result - root_result) < Decimal128("0.0000000001"), "x^(1/n) should equal nth root of x within tolerance", ) diff --git a/tests/decimal/test_decimal_round.mojo b/tests/decimal128/test_decimal128_round.mojo similarity index 74% rename from tests/decimal/test_decimal_round.mojo rename to tests/decimal128/test_decimal128_round.mojo index 2208acc..1516a50 100644 --- a/tests/decimal/test_decimal_round.mojo +++ b/tests/decimal128/test_decimal128_round.mojo @@ -1,7 +1,7 @@ """ -Test Decimal round methods with different rounding modes and precision levels. +Test Decimal128 round methods with different rounding modes and precision levels. """ -from decimojo.prelude import dm, Decimal, RoundingMode +from decimojo.prelude import dm, Decimal128, RoundingMode import testing @@ -9,26 +9,26 @@ fn test_basic_rounding() raises: print("Testing basic decimal rounding...") # Test case 1: Round to 2 decimal places (banker's rounding) - var d1 = Decimal("123.456") + var d1 = Decimal128("123.456") var result1 = round(d1, 2) testing.assert_equal( String(result1), "123.46", "Basic rounding to 2 decimal places" ) # Test case 2: Round to 0 decimal places - var d2 = Decimal("123.456") + var d2 = Decimal128("123.456") var result2 = round(d2, 0) testing.assert_equal(String(result2), "123", "Rounding to 0 decimal places") # Test case 3: Round to more decimal places than original (should pad with zeros) - var d3 = Decimal("123.45") + var d3 = Decimal128("123.45") var result3 = round(d3, 4) testing.assert_equal( String(result3), "123.4500", "Rounding to more decimal places" ) # Test case 4: Round number that's already at target precision - var d4 = Decimal("123.45") + var d4 = Decimal128("123.45") var result4 = round(d4, 2) testing.assert_equal( String(result4), "123.45", "Rounding to same precision" @@ -40,22 +40,22 @@ fn test_basic_rounding() raises: fn test_different_rounding_modes() raises: print("Testing different rounding modes...") - var test_value = Decimal("123.456") + var test_value = Decimal128("123.456") # Test case 1: Round down (truncate) - var result1 = dm.round(test_value, 2, RoundingMode.ROUND_DOWN) + var result1 = test_value.round(2, RoundingMode.ROUND_DOWN) testing.assert_equal(String(result1), "123.45", "Rounding down") # Test case 2: Round up (away from zero) - var result2 = dm.round(test_value, 2, RoundingMode.ROUND_UP) + var result2 = test_value.round(2, RoundingMode.ROUND_UP) testing.assert_equal(String(result2), "123.46", "Rounding up") # Test case 3: Round half up - var result3 = dm.round(test_value, 2, RoundingMode.ROUND_HALF_UP) + var result3 = test_value.round(2, RoundingMode.ROUND_HALF_UP) testing.assert_equal(String(result3), "123.46", "Rounding half up") # Test case 4: Round half even (banker's rounding) - var result4 = dm.round(test_value, 2, RoundingMode.ROUND_HALF_EVEN) + var result4 = test_value.round(2, RoundingMode.ROUND_HALF_EVEN) testing.assert_equal(String(result4), "123.46", "Rounding half even") print("Rounding mode tests passed!") @@ -65,42 +65,42 @@ fn test_edge_cases() raises: print("Testing edge cases for rounding...") # Test case 1: Rounding exactly 0.5 with different modes - var half_value = Decimal("123.5") + var half_value = Decimal128("123.5") testing.assert_equal( - String(dm.round(half_value, 0, RoundingMode.ROUND_DOWN)), + String(half_value.round(0, RoundingMode.ROUND_DOWN)), "123", "Rounding 0.5 down", ) testing.assert_equal( - String(dm.round(half_value, 0, RoundingMode.ROUND_UP)), + String(half_value.round(0, RoundingMode.ROUND_UP)), "124", "Rounding 0.5 up", ) testing.assert_equal( - String(dm.round(half_value, 0, RoundingMode.ROUND_HALF_UP)), + String(half_value.round(0, RoundingMode.ROUND_HALF_UP)), "124", "Rounding 0.5 half up", ) testing.assert_equal( - String(dm.round(half_value, 0, RoundingMode.ROUND_HALF_EVEN)), + String(half_value.round(0, RoundingMode.ROUND_HALF_EVEN)), "124", "Rounding 0.5 half even (even is 124)", ) # Another test with half to even value - var half_even_value = Decimal("124.5") + var half_even_value = Decimal128("124.5") testing.assert_equal( - String(dm.round(half_even_value, 0, RoundingMode.ROUND_HALF_EVEN)), + String(half_even_value.round(0, RoundingMode.ROUND_HALF_EVEN)), "124", "Rounding 124.5 half even (even is 124)", ) # Test case 2: Rounding very small numbers - var small_value = Decimal( + var small_value = Decimal128( "0." + "0" * 27 + "1" ) # 0.0000...01 (1 at 28th place) testing.assert_equal( @@ -110,28 +110,28 @@ fn test_edge_cases() raises: ) # Test case 3: Rounding negative numbers - var negative_value = Decimal("-123.456") + var negative_value = Decimal128("-123.456") testing.assert_equal( - String(dm.round(negative_value, 2, RoundingMode.ROUND_DOWN)), + String(negative_value.round(2, RoundingMode.ROUND_DOWN)), "-123.45", "Rounding negative number down", ) testing.assert_equal( - String(dm.round(negative_value, 2, RoundingMode.ROUND_UP)), + String(negative_value.round(2, RoundingMode.ROUND_UP)), "-123.46", "Rounding negative number up", ) testing.assert_equal( - String(dm.round(negative_value, 2, RoundingMode.ROUND_HALF_EVEN)), + String(negative_value.round(2, RoundingMode.ROUND_HALF_EVEN)), "-123.46", "Rounding negative number half even", ) # Test case 4: Rounding that causes carry propagation - var carry_value = Decimal("9.999") + var carry_value = Decimal128("9.999") testing.assert_equal( String(round(carry_value, 2)), @@ -140,7 +140,7 @@ fn test_edge_cases() raises: ) # Test case 5: Rounding to maximum precision - var MAX_SCALE = Decimal("0." + "1" * 28) # 0.1111...1 (28 digits) + var MAX_SCALE = Decimal128("0." + "1" * 28) # 0.1111...1 (28 digits) testing.assert_equal( String(round(MAX_SCALE, 14)), "0.11111111111111", @@ -157,8 +157,8 @@ fn test_rounding_consistency() raises: # constructing the same value # Two ways to create 123.45 - var d1 = Decimal("123.45") - var d2 = Decimal(123.45) + var d1 = Decimal128("123.45") + var d2 = Decimal128(123.45) # Both should round the same way testing.assert_equal( @@ -168,7 +168,7 @@ fn test_rounding_consistency() raises: ) # Test that repeated rounding is consistent - var start = Decimal("123.456789") + var start = Decimal128("123.456789") var round_once = round(start, 4) # 123.4568 var round_twice = round(round_once, 2) # 123.46 var direct = round(start, 2) # 123.46 diff --git a/tests/decimal/test_decimal_sqrt.mojo b/tests/decimal128/test_decimal128_sqrt.mojo similarity index 84% rename from tests/decimal/test_decimal_sqrt.mojo rename to tests/decimal128/test_decimal128_sqrt.mojo index 8b1a3b3..6de0488 100644 --- a/tests/decimal/test_decimal_sqrt.mojo +++ b/tests/decimal128/test_decimal128_sqrt.mojo @@ -1,8 +1,7 @@ """ -Comprehensive tests for the sqrt function of the Decimal type. +Comprehensive tests for the sqrt function of the Decimal128 type. """ -from decimojo.prelude import dm, Decimal, RoundingMode -from decimojo import sqrt +from decimojo.prelude import dm, Decimal128, RoundingMode import testing @@ -11,9 +10,9 @@ fn test_perfect_squares() raises: # Test case 1: sqrt(1) = 1 try: - var d1 = Decimal(1) + var d1 = Decimal128(1) print(" Testing sqrt(1)...") - var result1 = sqrt(d1) + var result1 = d1.sqrt() print(" Got result: " + String(result1)) testing.assert_equal( String(result1), @@ -28,9 +27,9 @@ fn test_perfect_squares() raises: # Test case 2: sqrt(4) = 2 try: - var d2 = Decimal(4) + var d2 = Decimal128(4) print(" Testing sqrt(4)...") - var result2 = sqrt(d2) + var result2 = d2.sqrt() print(" Got result: " + String(result2)) testing.assert_equal( String(result2), @@ -45,9 +44,9 @@ fn test_perfect_squares() raises: # Test case 3: sqrt(9) = 3 try: - var d3 = Decimal(9) + var d3 = Decimal128(9) print(" Testing sqrt(9)...") - var result3 = sqrt(d3) + var result3 = d3.sqrt() print(" Got result: " + String(result3)) testing.assert_equal( String(result3), @@ -62,9 +61,9 @@ fn test_perfect_squares() raises: # Test case 4: sqrt(16) = 4 try: - var d4 = Decimal(16) + var d4 = Decimal128(16) print(" Testing sqrt(16)...") - var result4 = sqrt(d4) + var result4 = d4.sqrt() print(" Got result: " + String(result4)) testing.assert_equal( String(result4), @@ -79,9 +78,9 @@ fn test_perfect_squares() raises: # Test case 5: sqrt(25) = 5 try: - var d5 = Decimal(25) + var d5 = Decimal128(25) print(" Testing sqrt(25)...") - var result5 = sqrt(d5) + var result5 = d5.sqrt() print(" Got result: " + String(result5)) testing.assert_equal( String(result5), @@ -96,9 +95,9 @@ fn test_perfect_squares() raises: # Test case 6: sqrt(36) = 6 try: - var d6 = Decimal(36) + var d6 = Decimal128(36) print(" Testing sqrt(36)...") - var result6 = sqrt(d6) + var result6 = d6.sqrt() print(" Got result: " + String(result6)) testing.assert_equal( String(result6), @@ -113,9 +112,9 @@ fn test_perfect_squares() raises: # Test case 7: sqrt(49) = 7 try: - var d7 = Decimal(49) + var d7 = Decimal128(49) print(" Testing sqrt(49)...") - var result7 = sqrt(d7) + var result7 = d7.sqrt() print(" Got result: " + String(result7)) testing.assert_equal( String(result7), @@ -130,9 +129,9 @@ fn test_perfect_squares() raises: # Test case 8: sqrt(64) = 8 try: - var d8 = Decimal(64) + var d8 = Decimal128(64) print(" Testing sqrt(64)...") - var result8 = sqrt(d8) + var result8 = d8.sqrt() print(" Got result: " + String(result8)) testing.assert_equal( String(result8), @@ -147,9 +146,9 @@ fn test_perfect_squares() raises: # Test case 9: sqrt(81) = 9 try: - var d9 = Decimal(81) + var d9 = Decimal128(81) print(" Testing sqrt(81)...") - var result9 = sqrt(d9) + var result9 = d9.sqrt() print(" Got result: " + String(result9)) testing.assert_equal( String(result9), @@ -164,9 +163,9 @@ fn test_perfect_squares() raises: # Test case 10: sqrt(100) = 10 try: - var d10 = Decimal(100) + var d10 = Decimal128(100) print(" Testing sqrt(100)...") - var result10 = sqrt(d10) + var result10 = d10.sqrt() print(" Got result: " + String(result10)) testing.assert_equal( String(result10), @@ -181,9 +180,9 @@ fn test_perfect_squares() raises: # Test case 11: sqrt(10000) = 100 try: - var d11 = Decimal(10000) + var d11 = Decimal128(10000) print(" Testing sqrt(10000)...") - var result11 = sqrt(d11) + var result11 = d11.sqrt() print(" Got result: " + String(result11)) testing.assert_equal( String(result11), @@ -198,9 +197,9 @@ fn test_perfect_squares() raises: # Test case 12: sqrt(1000000) = 1000 try: - var d12 = Decimal(1000000) + var d12 = Decimal128(1000000) print(" Testing sqrt(1000000)...") - var result12 = sqrt(d12) + var result12 = d12.sqrt() print(" Got result: " + String(result12)) testing.assert_equal( String(result12), @@ -221,9 +220,9 @@ fn test_non_perfect_squares() raises: # Test case 1 try: - var d1 = Decimal(2) + var d1 = Decimal128(2) var expected_prefix1 = "1.414213562373095048801688724" - var result1 = sqrt(d1) + var result1 = d1.sqrt() var result_str1 = String(result1) testing.assert_true( result_str1.startswith(expected_prefix1), @@ -239,9 +238,9 @@ fn test_non_perfect_squares() raises: raise e # Test case 2 - var d2 = Decimal(3) + var d2 = Decimal128(3) var expected_prefix2 = "1.73205080756887729352744634" - var result2 = sqrt(d2) + var result2 = d2.sqrt() var result_str2 = String(result2) testing.assert_true( result_str2.startswith(expected_prefix2), @@ -254,9 +253,9 @@ fn test_non_perfect_squares() raises: ) # Test case 3 - var d3 = Decimal(5) + var d3 = Decimal128(5) var expected_prefix3 = "2.23606797749978969640917366" - var result3 = sqrt(d3) + var result3 = d3.sqrt() var result_str3 = String(result3) testing.assert_true( result_str3.startswith(expected_prefix3), @@ -269,9 +268,9 @@ fn test_non_perfect_squares() raises: ) # Test case 4 - var d4 = Decimal(10) + var d4 = Decimal128(10) var expected_prefix4 = "3.162277660168379331998893544" - var result4 = sqrt(d4) + var result4 = d4.sqrt() var result_str4 = String(result4) testing.assert_true( result_str4.startswith(expected_prefix4), @@ -284,9 +283,9 @@ fn test_non_perfect_squares() raises: ) # Test case 5 - var d5 = Decimal(50) + var d5 = Decimal128(50) var expected_prefix5 = "7.071067811865475244008443621" - var result5 = sqrt(d5) + var result5 = d5.sqrt() var result_str5 = String(result5) testing.assert_true( result_str5.startswith(expected_prefix5), @@ -299,9 +298,9 @@ fn test_non_perfect_squares() raises: ) # Test case 6 - var d6 = Decimal(99) + var d6 = Decimal128(99) var expected_prefix6 = "9.949874371066199547344798210" - var result6 = sqrt(d6) + var result6 = d6.sqrt() var result_str6 = String(result6) testing.assert_true( result_str6.startswith(expected_prefix6), @@ -314,9 +313,9 @@ fn test_non_perfect_squares() raises: ) # Test case 7 - var d7 = Decimal(999) + var d7 = Decimal128(999) var expected_prefix7 = "31.6069612585582165452042139" - var result7 = sqrt(d7) + var result7 = d7.sqrt() var result_str7 = String(result7) testing.assert_true( result_str7.startswith(expected_prefix7), @@ -336,9 +335,9 @@ fn test_decimal_values() raises: # Test case 1 try: - var d1 = Decimal("0.25") + var d1 = Decimal128("0.25") var expected1 = "0.5" - var result1 = sqrt(d1) + var result1 = d1.sqrt() testing.assert_equal( String(result1), expected1, @@ -349,9 +348,9 @@ fn test_decimal_values() raises: raise e # Test case 2 - var d2 = Decimal("0.09") + var d2 = Decimal128("0.09") var expected2 = "0.3" - var result2 = sqrt(d2) + var result2 = d2.sqrt() testing.assert_equal( String(result2), expected2, @@ -359,9 +358,9 @@ fn test_decimal_values() raises: ) # Test case 3 - var d3 = Decimal("0.04") + var d3 = Decimal128("0.04") var expected3 = "0.2" - var result3 = sqrt(d3) + var result3 = d3.sqrt() testing.assert_equal( String(result3), expected3, @@ -369,9 +368,9 @@ fn test_decimal_values() raises: ) # Test case 4 - var d4 = Decimal("0.01") + var d4 = Decimal128("0.01") var expected4 = "0.1" - var result4 = sqrt(d4) + var result4 = d4.sqrt() testing.assert_equal( String(result4), expected4, @@ -379,9 +378,9 @@ fn test_decimal_values() raises: ) # Test case 5 - var d5 = Decimal("1.44") + var d5 = Decimal128("1.44") var expected5 = "1.2" - var result5 = sqrt(d5) + var result5 = d5.sqrt() testing.assert_equal( String(result5), expected5, @@ -389,9 +388,9 @@ fn test_decimal_values() raises: ) # Test case 6 - var d6 = Decimal("2.25") + var d6 = Decimal128("2.25") var expected6 = "1.5" - var result6 = sqrt(d6) + var result6 = d6.sqrt() testing.assert_equal( String(result6), expected6, @@ -399,16 +398,16 @@ fn test_decimal_values() raises: ) # Test case 7 - var d7 = Decimal("6.25") + var d7 = Decimal128("6.25") var expected7 = "2.5" - var result7 = sqrt(d7) + var result7 = d7.sqrt() testing.assert_equal( String(result7), expected7, "sqrt(" + String(d7) + ") should be " + expected7, ) - print("Decimal value tests passed!") + print("Decimal128 value tests passed!") fn test_edge_cases() raises: @@ -416,8 +415,8 @@ fn test_edge_cases() raises: # Test sqrt(0) = 0 try: - var zero = Decimal(0) - var result_zero = sqrt(zero) + var zero = Decimal128(0) + var result_zero = zero.sqrt() testing.assert_equal(String(result_zero), "0", "sqrt(0) should be 0") except e: print("ERROR in test_edge_cases: sqrt(0) = 0") @@ -425,8 +424,8 @@ fn test_edge_cases() raises: # Test sqrt(1) = 1 try: - var one = Decimal(1) - var result_one = sqrt(one) + var one = Decimal128(1) + var result_one = one.sqrt() testing.assert_equal(String(result_one), "1", "sqrt(1) should be 1") except e: print("ERROR in test_edge_cases: sqrt(1) = 1") @@ -434,8 +433,8 @@ fn test_edge_cases() raises: # Test very small positive number try: - var very_small = Decimal(1, 28) # Smallest possible positive decimal - var result_small = sqrt(very_small) + var very_small = Decimal128(1, 28) # Smallest possible positive decimal + var result_small = very_small.sqrt() testing.assert_equal( String(result_small), "0.00000000000001", @@ -450,10 +449,10 @@ fn test_edge_cases() raises: # Test very large number try: - var very_large = Decimal.from_uint128( - decimojo.utility.power_of_10[DType.uint128](27) + var very_large = Decimal128.from_uint128( + decimojo.decimal128.utility.power_of_10[DType.uint128](27) ) # Large decimal - var result_large = sqrt(very_large) + var result_large = very_large.sqrt() testing.assert_true( String(result_large).startswith("31622776601683.79331998893544"), "sqrt of 10^27 should start with 31622776601683.79331998893544...", @@ -465,8 +464,8 @@ fn test_edge_cases() raises: # Test negative number exception var negative_exception_caught = False try: - var negative = Decimal(-1) - var _result_negative = sqrt(negative) + var negative = Decimal128(-1) + var _result_negative = negative.sqrt() testing.assert_equal( True, False, "sqrt() of negative should raise exception" ) @@ -496,8 +495,8 @@ fn test_precision() raises: ) # Test precision for irrational numbers - var two = Decimal(2) - var result = sqrt(two) + var two = Decimal128(2) + var result = two.sqrt() # Check at least 10 decimal places (should be enough for most applications) testing.assert_true( @@ -506,18 +505,18 @@ fn test_precision() raises: ) # Test high precision values - var precise_value = Decimal.from_uint128( + var precise_value = Decimal128.from_uint128( UInt128(20000000000000000000000000), 25 ) - var precise_result = sqrt(precise_value) + var precise_result = precise_value.sqrt() testing.assert_true( String(precise_result).startswith(expected_sqrt2), "sqrt of high precision 2 should start with " + expected_sqrt2, ) # Check that results are appropriately rounded - var d = Decimal(1894128128951235, 9) - var sqrt_d = sqrt(d) + var d = Decimal128(1894128128951235, 9) + var sqrt_d = d.sqrt() testing.assert_true( String(sqrt_d).startswith("1376.27327553478091940498131"), ( @@ -535,8 +534,8 @@ fn test_mathematical_identities() raises: # Test that sqrt(x)² = x - Expanded for each test number # Test number 1 - var num1 = Decimal(2) - var sqrt_num1 = sqrt(num1) + var num1 = Decimal128(2) + var sqrt_num1 = num1.sqrt() var squared1 = sqrt_num1 * sqrt_num1 var original_rounded1 = round(num1, 10) var squared_rounded1 = round(squared1, 10) @@ -551,8 +550,8 @@ fn test_mathematical_identities() raises: ) # Test number 2 - var num2 = Decimal(3) - var sqrt_num2 = sqrt(num2) + var num2 = Decimal128(3) + var sqrt_num2 = num2.sqrt() var squared2 = sqrt_num2 * sqrt_num2 var original_rounded2 = round(num2, 10) var squared_rounded2 = round(squared2, 10) @@ -565,8 +564,8 @@ fn test_mathematical_identities() raises: ) # Test number 3 - var num3 = Decimal(5) - var sqrt_num3 = sqrt(num3) + var num3 = Decimal128(5) + var sqrt_num3 = num3.sqrt() var squared3 = sqrt_num3 * sqrt_num3 var original_rounded3 = round(num3, 10) var squared_rounded3 = round(squared3, 10) @@ -579,8 +578,8 @@ fn test_mathematical_identities() raises: ) # Test number 4 - var num4 = Decimal(7) - var sqrt_num4 = sqrt(num4) + var num4 = Decimal128(7) + var sqrt_num4 = num4.sqrt() var squared4 = sqrt_num4 * sqrt_num4 var original_rounded4 = round(num4, 10) var squared_rounded4 = round(squared4, 10) @@ -593,8 +592,8 @@ fn test_mathematical_identities() raises: ) # Test number 5 - var num5 = Decimal(10) - var sqrt_num5 = sqrt(num5) + var num5 = Decimal128(10) + var sqrt_num5 = num5.sqrt() var squared5 = sqrt_num5 * sqrt_num5 var original_rounded5 = round(num5, 10) var squared_rounded5 = round(squared5, 10) @@ -607,8 +606,8 @@ fn test_mathematical_identities() raises: ) # Test number 6 - var num6 = Decimal(5, 1) - var sqrt_num6 = sqrt(num6) + var num6 = Decimal128(5, 1) + var sqrt_num6 = num6.sqrt() var squared6 = sqrt_num6 * sqrt_num6 var original_rounded6 = round(num6, 10) var squared_rounded6 = round(squared6, 10) @@ -621,8 +620,8 @@ fn test_mathematical_identities() raises: ) # Test number 7 - var num7 = Decimal(25, 2) - var sqrt_num7 = sqrt(num7) + var num7 = Decimal128(25, 2) + var sqrt_num7 = num7.sqrt() var squared7 = sqrt_num7 * sqrt_num7 var original_rounded7 = round(num7, 10) var squared_rounded7 = round(squared7, 10) @@ -635,8 +634,8 @@ fn test_mathematical_identities() raises: ) # Test number 8 - var num8 = Decimal(144, 2) - var sqrt_num8 = sqrt(num8) + var num8 = Decimal128(144, 2) + var sqrt_num8 = num8.sqrt() var squared8 = sqrt_num8 * sqrt_num8 var original_rounded8 = round(num8, 10) var squared_rounded8 = round(squared8, 10) @@ -651,12 +650,12 @@ fn test_mathematical_identities() raises: # Test that sqrt(x*y) = sqrt(x) * sqrt(y) - Expanded for each pair # Pair 1: 4 and 9 try: - var x1 = Decimal(4) - var y1 = Decimal(9) + var x1 = Decimal128(4) + var y1 = Decimal128(9) var product1 = x1 * y1 - var sqrt_product1 = sqrt(product1) - var sqrt_x1 = sqrt(x1) - var sqrt_y1 = sqrt(y1) + var sqrt_product1 = product1.sqrt() + var sqrt_x1 = x1.sqrt() + var sqrt_y1 = y1.sqrt() var sqrt_product_separate1 = sqrt_x1 * sqrt_y1 var sqrt_product_rounded1 = round(sqrt_product1, 10) var sqrt_product_separate_rounded1 = round(sqrt_product_separate1, 10) @@ -680,12 +679,12 @@ fn test_mathematical_identities() raises: raise e # Pair 2: 16 and 25 - var x2 = Decimal(16) - var y2 = Decimal(25) + var x2 = Decimal128(16) + var y2 = Decimal128(25) var product2 = x2 * y2 - var sqrt_product2 = sqrt(product2) - var sqrt_x2 = sqrt(x2) - var sqrt_y2 = sqrt(y2) + var sqrt_product2 = product2.sqrt() + var sqrt_x2 = x2.sqrt() + var sqrt_y2 = y2.sqrt() var sqrt_product_separate2 = sqrt_x2 * sqrt_y2 var sqrt_product_rounded2 = round(sqrt_product2, 10) var sqrt_product_separate_rounded2 = round(sqrt_product_separate2, 10) @@ -703,12 +702,12 @@ fn test_mathematical_identities() raises: ) # Pair 3: 2 and 8 - var x3 = Decimal(2) - var y3 = Decimal(8) + var x3 = Decimal128(2) + var y3 = Decimal128(8) var product3 = x3 * y3 - var sqrt_product3 = sqrt(product3) - var sqrt_x3 = sqrt(x3) - var sqrt_y3 = sqrt(y3) + var sqrt_product3 = product3.sqrt() + var sqrt_x3 = x3.sqrt() + var sqrt_y3 = y3.sqrt() var sqrt_product_separate3 = sqrt_x3 * sqrt_y3 var sqrt_product_rounded3 = round(sqrt_product3, 10) var sqrt_product_separate_rounded3 = round(sqrt_product_separate3, 10) @@ -733,8 +732,8 @@ fn test_sqrt_performance() raises: # Test case 1 try: - var num1 = Decimal("0.0001") - var result1 = sqrt(num1) + var num1 = Decimal128("0.0001") + var result1 = num1.sqrt() var squared1 = result1 * result1 var diff1 = squared1 - num1 diff1 = -diff1 if diff1.is_negative() else diff1 @@ -752,8 +751,8 @@ fn test_sqrt_performance() raises: # Test case 2 try: - var num2 = Decimal("0.01") - var result2 = sqrt(num2) + var num2 = Decimal128("0.01") + var result2 = num2.sqrt() var squared2 = result2 * result2 var diff2 = squared2 - num2 diff2 = -diff2 if diff2.is_negative() else diff2 @@ -771,8 +770,8 @@ fn test_sqrt_performance() raises: # Test case 3 try: - var num3 = Decimal(1) - var result3 = sqrt(num3) + var num3 = Decimal128(1) + var result3 = num3.sqrt() var squared3 = result3 * result3 var diff3 = squared3 - num3 diff3 = -diff3 if diff3.is_negative() else diff3 @@ -790,14 +789,14 @@ fn test_sqrt_performance() raises: # Test case 4 try: - var num4 = Decimal(10) - var result4 = sqrt(num4) + var num4 = Decimal128(10) + var result4 = num4.sqrt() var squared4 = result4 * result4 var diff4 = squared4 - num4 diff4 = -diff4 if diff4.is_negative() else diff4 var rel_diff4 = diff4 / num4 testing.assert_true( - rel_diff4 < Decimal(0.00001), + rel_diff4 < Decimal128(0.00001), "Square root calculation for " + String(num4) + " should be accurate within 0.001%", @@ -808,8 +807,8 @@ fn test_sqrt_performance() raises: # Test case 5 try: - var num5 = Decimal(10000) - var result5 = sqrt(num5) + var num5 = Decimal128(10000) + var result5 = num5.sqrt() var squared5 = result5 * result5 var diff5 = squared5 - num5 diff5 = -diff5 if diff5.is_negative() else diff5 @@ -827,8 +826,8 @@ fn test_sqrt_performance() raises: # Test case 6 try: - var num6 = Decimal("10000000000") - var result6 = sqrt(num6) + var num6 = Decimal128("10000000000") + var result6 = num6.sqrt() var squared6 = result6 * result6 var diff6 = squared6 - num6 diff6 = -diff6 if diff6.is_negative() else diff6 @@ -846,8 +845,8 @@ fn test_sqrt_performance() raises: # Test case 7 try: - var num7 = Decimal("0.999999999") - var result7 = String(sqrt(num7)) + var num7 = Decimal128("0.999999999") + var result7 = String(num7.sqrt()) var expected_result7 = String("0.99999999949999999987") testing.assert_true( result7.startswith(expected_result7), "sqrt(0.999999999)" @@ -858,8 +857,8 @@ fn test_sqrt_performance() raises: # Test case 8 try: - var num8 = Decimal("1.000000001") - var result8 = String(sqrt(num8)) + var num8 = Decimal128("1.000000001") + var result8 = String(num8.sqrt()) var expected_result8 = String("1.000000000499999999875") testing.assert_true( result8.startswith(expected_result8), "sqrt(1.000000001)" @@ -870,8 +869,8 @@ fn test_sqrt_performance() raises: # Test case 9 try: - var num9 = Decimal("3.999999999") - var result9 = sqrt(num9) + var num9 = Decimal128("3.999999999") + var result9 = num9.sqrt() var squared9 = result9 * result9 var diff9 = squared9 - num9 diff9 = -diff9 if diff9.is_negative() else diff9 @@ -889,15 +888,15 @@ fn test_sqrt_performance() raises: # Test case 10 try: - var num10 = Decimal("4.000000001") - var result10 = sqrt(num10) + var num10 = Decimal128("4.000000001") + var result10 = num10.sqrt() var squared10 = result10 * result10 # Using manual absolute difference calculation var diff10 = squared10 - num10 diff10 = -diff10 if diff10.is_negative() else diff10 var rel_diff10 = diff10 / num10 testing.assert_true( - rel_diff10 < Decimal("0.00001"), + rel_diff10 < Decimal128("0.00001"), "Square root calculation for " + String(num10) + " should be accurate within 0.001%", @@ -930,13 +929,13 @@ fn run_test_with_error_handling( fn main() raises: print("=========================================") - print("Running comprehensive Decimal square root tests") + print("Running comprehensive Decimal128 square root tests") run_test_with_error_handling(test_perfect_squares, "Perfect squares test") run_test_with_error_handling( test_non_perfect_squares, "Non-perfect squares test" ) - run_test_with_error_handling(test_decimal_values, "Decimal values test") + run_test_with_error_handling(test_decimal_values, "Decimal128 values test") run_test_with_error_handling(test_edge_cases, "Edge cases test") run_test_with_error_handling(test_precision, "Precision test") run_test_with_error_handling( diff --git a/tests/decimal/test_decimal_to_float.mojo b/tests/decimal128/test_decimal128_to_float.mojo similarity index 76% rename from tests/decimal/test_decimal_to_float.mojo rename to tests/decimal128/test_decimal128_to_float.mojo index b9af040..ff67c36 100644 --- a/tests/decimal/test_decimal_to_float.mojo +++ b/tests/decimal128/test_decimal128_to_float.mojo @@ -1,10 +1,10 @@ """ -Comprehensive tests for the Decimal.__float__() method. -Tests 20 different cases to ensure proper conversion from Decimal to float. +Comprehensive tests for the Decimal128.__float__() method. +Tests 20 different cases to ensure proper conversion from Decimal128 to float. """ import testing -from decimojo.prelude import dm, Decimal, RoundingMode +from decimojo.prelude import dm, Decimal128, RoundingMode fn test_basic_integer_conversions() raises: @@ -12,28 +12,28 @@ fn test_basic_integer_conversions() raises: print("Testing basic integer conversions to float...") # Test case 1: Zero - var zero = Decimal(0) + var zero = Decimal128(0) var zero_float = Float64(zero) testing.assert_equal( - zero_float, 0.0, "Decimal('0') should convert to float 0.0" + zero_float, 0.0, "Decimal128('0') should convert to float 0.0" ) # Test case 2: One - var one = Decimal(1) + var one = Decimal128(1) var one_float = Float64(one) testing.assert_equal( - one_float, 1.0, "Decimal('1') should convert to float 1.0" + one_float, 1.0, "Decimal128('1') should convert to float 1.0" ) # Test case 3: Ten - var ten = Decimal(10) + var ten = Decimal128(10) var ten_float = Float64(ten) testing.assert_equal( - ten_float, 10.0, "Decimal('10') should convert to float 10.0" + ten_float, 10.0, "Decimal128('10') should convert to float 10.0" ) # Test case 4: Large integer - var large_int = Decimal(123456) + var large_int = Decimal128(123456) var large_int_float = Float64(large_int) testing.assert_equal(large_int_float, 123456.0) @@ -45,29 +45,31 @@ fn test_decimal_conversions() raises: print("Testing decimal conversions to float...") # Test case 5: Simple decimal - var simple_dec = Decimal("3.14") + var simple_dec = Decimal128("3.14") var simple_dec_float = Float64(simple_dec) testing.assert_equal( - simple_dec_float, 3.14, "Decimal('3.14') should convert to float 3.14" + simple_dec_float, + 3.14, + "Decimal128('3.14') should convert to float 3.14", ) - # Test case 6: Decimal with many places - var pi = Decimal("3.14159265358979323846") + # Test case 6: Decimal128 with many places + var pi = Decimal128("3.14159265358979323846") var pi_float = Float64(pi) # Allow for small difference due to float precision testing.assert_true(abs(pi_float - 3.14159265358979323846) < 1e-15) # Test case 7: Small decimal - var small_dec = Decimal("0.0001") + var small_dec = Decimal128("0.0001") var small_dec_float = Float64(small_dec) testing.assert_equal(small_dec_float, 0.0001) # Test case 8: Repeating decimal - var repeating = Decimal("0.33333333333333") + var repeating = Decimal128("0.33333333333333") var repeating_float = Float64(repeating) testing.assert_true(abs(repeating_float - 0.33333333333333) < 1e-14) - print("✓ Decimal conversions to float passed!") + print("✓ Decimal128 conversions to float passed!") fn test_negative_conversions() raises: @@ -75,17 +77,17 @@ fn test_negative_conversions() raises: print("Testing negative value conversions to float...") # Test case 9: Negative integer - var neg_int = Decimal("-123") + var neg_int = Decimal128("-123") var neg_int_float = Float64(neg_int) testing.assert_equal(neg_int_float, -123.0) # Test case 10: Negative decimal - var neg_dec = Decimal("-0.5") + var neg_dec = Decimal128("-0.5") var neg_dec_float = Float64(neg_dec) testing.assert_equal(neg_dec_float, -0.5) # Test case 11: Negative zero - var neg_zero = Decimal("-0") + var neg_zero = Decimal128("-0") var neg_zero_float = Float64(neg_zero) testing.assert_equal( neg_zero_float, 0.0 @@ -99,15 +101,15 @@ fn test_edge_cases() raises: print("Testing edge cases for float conversion...") # Test case 12: Very small positive number - var very_small = Decimal("0." + "0" * 20 + "1") # 0.00000000000000000001 + var very_small = Decimal128("0." + "0" * 20 + "1") # 0.00000000000000000001 var very_small_float = Float64(very_small) testing.assert_true( very_small_float > 0.0 and very_small_float < 1e-19, - "Very small Decimal should convert to near-zero positive float", + "Very small Decimal128 should convert to near-zero positive float", ) # Test case 13: Value close to float precision - var precision_edge = Decimal("0.1234567890123456") + var precision_edge = Decimal128("0.1234567890123456") var precision_edge_float = Float64(precision_edge) testing.assert_true( abs(precision_edge_float - 0.1234567890123456) < 1e-15, @@ -115,12 +117,12 @@ fn test_edge_cases() raises: ) # Test case 14: Very large number - var large_num = Decimal("1e15") # 1,000,000,000,000,000 + var large_num = Decimal128("1e15") # 1,000,000,000,000,000 var large_num_float = Float64(large_num) testing.assert_equal(large_num_float, 1e15) # Test case 15: Number larger than float precision but within range - var large_precise = Decimal( + var large_precise = Decimal128( "9007199254740993" ) # First integer not exactly representable in float64 var large_precise_float = Float64(large_precise) @@ -136,23 +138,23 @@ fn test_special_values() raises: """Test special values for conversion to float.""" print("Testing special values for float conversion...") - # Test case 16: Decimal with trailing zeros - var trailing_zeros = Decimal("5.0000") + # Test case 16: Decimal128 with trailing zeros + var trailing_zeros = Decimal128("5.0000") var trailing_zeros_float = Float64(trailing_zeros) testing.assert_equal(trailing_zeros_float, 5.0) - # Test case 17: Decimal with leading zeros - var leading_zeros = Decimal("000123.456") + # Test case 17: Decimal128 with leading zeros + var leading_zeros = Decimal128("000123.456") var leading_zeros_float = Float64(leading_zeros) testing.assert_equal(leading_zeros_float, 123.456) # Test case 18: Scientific notation - var sci_notation = Decimal("1.23e5") + var sci_notation = Decimal128("1.23e5") var sci_notation_float = Float64(sci_notation) testing.assert_equal(sci_notation_float, 123000.0) # Test case 19: Max decimal convertible to float - var max_decimal = Decimal( + var max_decimal = Decimal128( "79228162514264337593543950335" ) # Approximate Float64 max var max_decimal_float = Float64(max_decimal) @@ -167,7 +169,7 @@ fn test_special_values() raises: ) # Test case 20: Another number with specific precision challenges - var challenge_num = Decimal("0.1") + var challenge_num = Decimal128("0.1") var challenge_float = Float64(challenge_num) # 0.1 cannot be exactly represented in binary floating point testing.assert_true( @@ -197,14 +199,14 @@ fn run_test_with_error_handling( fn main() raises: print("=========================================") - print("Running 20 tests for Decimal.__float__()") + print("Running 20 tests for Decimal128.__float__()") print("=========================================") run_test_with_error_handling( test_basic_integer_conversions, "Basic integer conversions" ) run_test_with_error_handling( - test_decimal_conversions, "Decimal conversions" + test_decimal_conversions, "Decimal128 conversions" ) run_test_with_error_handling( test_negative_conversions, "Negative value conversions" @@ -212,4 +214,4 @@ fn main() raises: run_test_with_error_handling(test_edge_cases, "Edge cases") run_test_with_error_handling(test_special_values, "Special values") - print("All 20 Decimal.__float__() tests passed!") + print("All 20 Decimal128.__float__() tests passed!") diff --git a/tests/decimal/test_decimal_to_int.mojo b/tests/decimal128/test_decimal128_to_int.mojo similarity index 73% rename from tests/decimal/test_decimal_to_int.mojo rename to tests/decimal128/test_decimal128_to_int.mojo index 57e6cbd..f2bf4b7 100644 --- a/tests/decimal/test_decimal_to_int.mojo +++ b/tests/decimal128/test_decimal128_to_int.mojo @@ -1,9 +1,9 @@ """ -Test Decimal conversion methods: __int__ +Test Decimal128 conversion methods: __int__ for different numerical cases. """ -from decimojo.prelude import dm, Decimal, RoundingMode +from decimojo.prelude import dm, Decimal128, RoundingMode import testing import time @@ -13,44 +13,44 @@ fn test_int_conversion() raises: print("--- Testing Int Conversion ---") # Test positive integer - var d1 = Decimal(123) + var d1 = Decimal128(123) var i1 = Int(d1) print("Int(123) =", i1) testing.assert_equal(i1, 123) # Test negative integer - var d2 = Decimal(-456) + var d2 = Decimal128(-456) var i2 = Int(d2) print("Int(-456) =", i2) testing.assert_equal(i2, -456) # Test zero - var d3 = Decimal(0) + var d3 = Decimal128(0) var i3 = Int(d3) print("Int(0) =", i3) testing.assert_equal(i3, 0) # Test decimal truncation - var d4 = Decimal(789987, 3) + var d4 = Decimal128(789987, 3) var i4 = Int(d4) print("Int(789.987) =", i4) testing.assert_equal(i4, 789) # Test negative decimal truncation - var d5 = Decimal(-123456, 3) + var d5 = Decimal128(-123456, 3) var i5 = Int(d5) print("Int(-123.456) =", i5) testing.assert_equal(i5, -123) # Test large number - var d6 = Decimal(9999999999) + var d6 = Decimal128(9999999999) var i6 = Int(d6) print("Int(9999999999) =", i6) testing.assert_equal(i6, 9999999999) fn main() raises: - print("Starting Decimal conversion __int__ tests...") + print("Starting Decimal128 conversion __int__ tests...") test_int_conversion() diff --git a/tests/decimal/test_decimal_to_string.mojo b/tests/decimal128/test_decimal128_to_string.mojo similarity index 73% rename from tests/decimal/test_decimal_to_string.mojo rename to tests/decimal128/test_decimal128_to_string.mojo index 9c5b03a..0253d09 100644 --- a/tests/decimal/test_decimal_to_string.mojo +++ b/tests/decimal128/test_decimal128_to_string.mojo @@ -1,9 +1,9 @@ """ -Test Decimal conversion methods: __str__ +Test Decimal128 conversion methods: __str__ for different numerical cases. """ -from decimojo.prelude import dm, Decimal, RoundingMode +from decimojo.prelude import dm, Decimal128, RoundingMode import testing import time @@ -13,38 +13,38 @@ fn test_str_conversion() raises: print("--- Testing String Conversion ---") # Test positive number - var d1 = Decimal("123.456") + var d1 = Decimal128("123.456") var s1 = String(d1) print("String(123.456) =", s1) testing.assert_equal(s1, "123.456") # Test negative number - var d2 = Decimal("-789.012") + var d2 = Decimal128("-789.012") var s2 = String(d2) print("String(-789.012) =", s2) testing.assert_equal(s2, "-789.012") # Test zero - var d3 = Decimal(0) + var d3 = Decimal128(0) var s3 = String(d3) print("String(0) =", s3) testing.assert_equal(s3, "0") # Test large number with precision - var d4 = Decimal("9876543210.0123456789") + var d4 = Decimal128("9876543210.0123456789") var s4 = String(d4) print("String(9876543210.0123456789) =", s4) testing.assert_equal(s4, "9876543210.0123456789") # Test small number - var d5 = Decimal("0.0000000001") + var d5 = Decimal128("0.0000000001") var s5 = String(d5) print("String(0.0000000001) =", s5) testing.assert_equal(s5, "0.0000000001") fn main() raises: - print("Starting Decimal conversion __str__ tests...") + print("Starting Decimal128 conversion __str__ tests...") test_str_conversion() diff --git a/tests/decimal/test_decimal_truncate_divide.mojo b/tests/decimal128/test_decimal128_truncate_divide.mojo similarity index 80% rename from tests/decimal/test_decimal_truncate_divide.mojo rename to tests/decimal128/test_decimal128_truncate_divide.mojo index 4c8195e..a7c791c 100644 --- a/tests/decimal/test_decimal_truncate_divide.mojo +++ b/tests/decimal128/test_decimal128_truncate_divide.mojo @@ -1,10 +1,10 @@ """ -Comprehensive tests for the floor division (//) operation of the Decimal type. +Comprehensive tests for the floor division (//) operation of the Decimal128 type. Tests various scenarios to ensure proper integer division behavior. """ import testing -from decimojo.prelude import dm, Decimal, RoundingMode +from decimojo.prelude import dm, Decimal128, RoundingMode fn test_basic_floor_division() raises: @@ -12,24 +12,24 @@ fn test_basic_floor_division() raises: print("Testing basic floor division...") # Test case 1: Simple integer division with no remainder - var a1 = Decimal(10) - var b1 = Decimal(2) + var a1 = Decimal128(10) + var b1 = Decimal128(2) var result1 = a1 // b1 testing.assert_equal( String(result1), "5", "10 // 2 should equal 5, got " + String(result1) ) # Test case 2: Simple integer division with remainder - var a2 = Decimal(10) - var b2 = Decimal(3) + var a2 = Decimal128(10) + var b2 = Decimal128(3) var result2 = a2 // b2 testing.assert_equal( String(result2), "3", "10 // 3 should equal 3, got " + String(result2) ) # Test case 3: Division with decimal values - var a3 = Decimal("10.5") - var b3 = Decimal("2.5") + var a3 = Decimal128("10.5") + var b3 = Decimal128("2.5") var result3 = a3 // b3 testing.assert_equal( String(result3), @@ -38,16 +38,16 @@ fn test_basic_floor_division() raises: ) # Test case 4: Division resulting in a decimal value - var a4 = Decimal(5) - var b4 = Decimal(2) + var a4 = Decimal128(5) + var b4 = Decimal128(2) var result4 = a4 // b4 testing.assert_equal( String(result4), "2", "5 // 2 should equal 2, got " + String(result4) ) # Test case 5: Division with different decimal places - var a5 = Decimal("10.75") - var b5 = Decimal("1.5") + var a5 = Decimal128("10.75") + var b5 = Decimal128("1.5") var result5 = a5 // b5 testing.assert_equal( String(result5), @@ -63,8 +63,8 @@ fn test_negative_floor_division() raises: print("Testing floor division with negative numbers...") # Test case 1: Negative // Positive - var a1 = Decimal(-10) - var b1 = Decimal(3) + var a1 = Decimal128(-10) + var b1 = Decimal128(3) var result1 = a1 // b1 testing.assert_equal( String(result1), @@ -73,8 +73,8 @@ fn test_negative_floor_division() raises: ) # Test case 2: Positive // Negative - var a2 = Decimal(10) - var b2 = Decimal(-3) + var a2 = Decimal128(10) + var b2 = Decimal128(-3) var result2 = a2 // b2 testing.assert_equal( String(result2), @@ -83,16 +83,16 @@ fn test_negative_floor_division() raises: ) # Test case 3: Negative // Negative - var a3 = Decimal(-10) - var b3 = Decimal(-3) + var a3 = Decimal128(-10) + var b3 = Decimal128(-3) var result3 = a3 // b3 testing.assert_equal( String(result3), "3", "-10 // -3 should equal 3, got " + String(result3) ) - # Test case 4: Decimal values, Negative // Positive - var a4 = Decimal("-10.5") - var b4 = Decimal("3.5") + # Test case 4: Decimal128 values, Negative // Positive + var a4 = Decimal128("-10.5") + var b4 = Decimal128("3.5") var result4 = a4 // b4 testing.assert_equal( String(result4), @@ -100,9 +100,9 @@ fn test_negative_floor_division() raises: "-10.5 // 3.5 should equal -3, got " + String(result4), ) - # Test case 5: Decimal values, Positive // Negative - var a5 = Decimal("10.5") - var b5 = Decimal("-3.5") + # Test case 5: Decimal128 values, Positive // Negative + var a5 = Decimal128("10.5") + var b5 = Decimal128("-3.5") var result5 = a5 // b5 testing.assert_equal( String(result5), @@ -110,9 +110,9 @@ fn test_negative_floor_division() raises: "10.5 // -3.5 should equal -3, got " + String(result5), ) - # Test case 6: Decimal values, Negative // Negative - var a6 = Decimal("-10.5") - var b6 = Decimal("-3.5") + # Test case 6: Decimal128 values, Negative // Negative + var a6 = Decimal128("-10.5") + var b6 = Decimal128("-3.5") var result6 = a6 // b6 testing.assert_equal( String(result6), @@ -128,24 +128,24 @@ fn test_edge_cases() raises: print("Testing floor division edge cases...") # Test case 1: Division by 1 - var a1 = Decimal(10) - var b1 = Decimal(1) + var a1 = Decimal128(10) + var b1 = Decimal128(1) var result1 = a1 // b1 testing.assert_equal( String(result1), "10", "10 // 1 should equal 10, got " + String(result1) ) # Test case 2: Zero dividend - var a2 = Decimal(0) - var b2 = Decimal(5) + var a2 = Decimal128(0) + var b2 = Decimal128(5) var result2 = a2 // b2 testing.assert_equal( String(result2), "0", "0 // 5 should equal 0, got " + String(result2) ) # Test case 3: Division by a decimal < 1 - var a3 = Decimal(10) - var b3 = Decimal("0.5") + var a3 = Decimal128(10) + var b3 = Decimal128("0.5") var result3 = a3 // b3 testing.assert_equal( String(result3), @@ -154,16 +154,16 @@ fn test_edge_cases() raises: ) # Test case 4: Division resulting in a negative zero (should be 0) - var a4 = Decimal(0) - var b4 = Decimal(-5) + var a4 = Decimal128(0) + var b4 = Decimal128(-5) var result4 = a4 // b4 testing.assert_equal( String(result4), "0", "0 // -5 should equal 0, got " + String(result4) ) # Test case 5: Division by zero (should raise error) - var a5 = Decimal(10) - var b5 = Decimal(0) + var a5 = Decimal128(10) + var b5 = Decimal128(0) var exception_caught = False try: var _result5 = a5 // b5 @@ -175,16 +175,16 @@ fn test_edge_cases() raises: ) # Test case 6: Large number division - var a6 = Decimal("1000000000") - var b6 = Decimal("7") + var a6 = Decimal128("1000000000") + var b6 = Decimal128("7") var result6 = a6 // b6 testing.assert_equal( String(result6), "142857142", "1000000000 // 7 calculated incorrectly" ) # Test case 7: Small number division - var a7 = Decimal("0.0000001") - var b7 = Decimal("0.0000002") + var a7 = Decimal128("0.0000001") + var b7 = Decimal128("0.0000002") var result7 = a7 // b7 testing.assert_equal( String(result7), "0", "0.0000001 // 0.0000002 should equal 0" @@ -198,8 +198,8 @@ fn test_mathematical_relationships() raises: print("Testing mathematical relationships...") # Test case 1: a = (a // b) * b + (a % b) - var a1 = Decimal(10) - var b1 = Decimal(3) + var a1 = Decimal128(10) + var b1 = Decimal128(3) var floor_div = a1 // b1 var mod_result = a1 % b1 var reconstructed = floor_div * b1 + mod_result @@ -210,8 +210,8 @@ fn test_mathematical_relationships() raises: ) # Test case 2: a // b = floor(a / b) - var a2 = Decimal("10.5") - var b2 = Decimal("2.5") + var a2 = Decimal128("10.5") + var b2 = Decimal128("2.5") var floor_div2 = a2 // b2 var div_floored = (a2 / b2).round(0, RoundingMode.ROUND_DOWN) testing.assert_equal( @@ -221,8 +221,8 @@ fn test_mathematical_relationships() raises: ) # Test case 3: Relationship with negative values - var a3 = Decimal(-10) - var b3 = Decimal(3) + var a3 = Decimal128(-10) + var b3 = Decimal128(3) var floor_div3 = a3 // b3 var mod_result3 = a3 % b3 var reconstructed3 = floor_div3 * b3 + mod_result3 @@ -233,11 +233,11 @@ fn test_mathematical_relationships() raises: ) # Test case 4: (a // b) * b ≤ a < (a // b + 1) * b - var a4 = Decimal("10.5") - var b4 = Decimal("3.2") + var a4 = Decimal128("10.5") + var b4 = Decimal128("3.2") var floor_div4 = a4 // b4 var lower_bound = floor_div4 * b4 - var upper_bound = (floor_div4 + Decimal(1)) * b4 + var upper_bound = (floor_div4 + Decimal128(1)) * b4 testing.assert_true( (lower_bound <= a4) and (a4 < upper_bound), "Relationship (a // b) * b ≤ a < (a // b + 1) * b should hold", @@ -265,7 +265,7 @@ fn run_test_with_error_handling( fn main() raises: print("=========================================") - print("Running Decimal Floor Division Tests") + print("Running Decimal128 Floor Division Tests") print("=========================================") run_test_with_error_handling( diff --git a/tests/decimal/test_decimal_utility.mojo b/tests/decimal128/test_decimal128_utility.mojo similarity index 79% rename from tests/decimal/test_decimal_utility.mojo rename to tests/decimal128/test_decimal128_utility.mojo index afa53cf..351b7ca 100644 --- a/tests/decimal/test_decimal_utility.mojo +++ b/tests/decimal128/test_decimal128_utility.mojo @@ -5,8 +5,13 @@ Tests for the utility functions in the decimojo.utility module. from testing import assert_equal, assert_true import max -from decimojo.prelude import dm, Decimal, RoundingMode -from decimojo.utility import truncate_to_max, number_of_digits +from decimojo.prelude import dm, Decimal128, RoundingMode +from decimojo.decimal128.utility import ( + truncate_to_max, + number_of_digits, + round_to_keep_first_n_digits, + bitcast, +) fn test_number_of_digits() raises: @@ -31,11 +36,11 @@ fn test_number_of_digits() raises: assert_equal(number_of_digits(UInt256(10) ** 20), 21) # Test with large values approaching UInt128 maximum - var large_value = UInt128(Decimal.MAX_AS_UINT128) + var large_value = UInt128(Decimal128.MAX_AS_UINT128) assert_equal(number_of_digits(large_value), 29) # Test with values larger than UInt128 max (using UInt256) - var very_large = UInt256(Decimal.MAX_AS_UINT128) * UInt256(10) + var very_large = UInt256(Decimal128.MAX_AS_UINT128) * UInt256(10) assert_equal(number_of_digits(very_large), 30) print("✓ All number_of_digits tests passed!") @@ -54,11 +59,11 @@ fn test_truncate_to_max_below_max() raises: assert_equal(truncate_to_max(small_value_256), small_value_256) # Test with value exactly at MAX_AS_UINT128 - var max_value = UInt128(Decimal.MAX_AS_UINT128) + var max_value = UInt128(Decimal128.MAX_AS_UINT128) assert_equal(truncate_to_max(max_value), max_value) # Test with UInt256 at exact MAX value - var max_value_256 = UInt256(Decimal.MAX_AS_UINT128) + var max_value_256 = UInt256(Decimal128.MAX_AS_UINT128) assert_equal(truncate_to_max(max_value_256), max_value_256) print("✓ All truncate_to_max tests with values below MAX passed!") @@ -69,11 +74,11 @@ fn test_truncate_to_max_above_max() raises: print("Testing truncate_to_max with values above MAX...") # Test with value MAX + 1 (should round appropriately) - var max_plus_1 = UInt256(Decimal.MAX_AS_UINT128) + UInt256(1) + var max_plus_1 = UInt256(Decimal128.MAX_AS_UINT128) + UInt256(1) var result = truncate_to_max(max_plus_1) # The result should be exactly MAX or a truncated value (depending on rounding) - assert_true(result <= UInt256(Decimal.MAX_AS_UINT128)) + assert_true(result <= UInt256(Decimal128.MAX_AS_UINT128)) # Test with a value that requires truncating 1 digit, rounding down # 79228162514264337593543950354 (79228162514264337593543950335 + 19) @@ -90,7 +95,7 @@ fn test_truncate_to_max_above_max() raises: # Test banker's rounding with a value ending in 5 with an even digit before it # 79228162514264337593543950355 (MAX + 20) - should round to even (down) - var banker_round_down = UInt256(Decimal.MAX_AS_UINT128) + UInt256(20) + var banker_round_down = UInt256(Decimal128.MAX_AS_UINT128) + UInt256(20) assert_equal( truncate_to_max(banker_round_down), UInt256(7922816251426433759354395036), @@ -108,11 +113,13 @@ fn test_truncate_to_max_above_max() raises: ) # Rounds to ..330 # Test with a much larger value that requires truncating multiple digits - var much_larger = UInt256(Decimal.MAX_AS_UINT128) * UInt256(1000) + UInt256( - 555 - ) + var much_larger = UInt256(Decimal128.MAX_AS_UINT128) * UInt256( + 1000 + ) + UInt256(555) # Result should be a properly truncated value - assert_true(truncate_to_max(much_larger) <= UInt256(Decimal.MAX_AS_UINT128)) + assert_true( + truncate_to_max(much_larger) <= UInt256(Decimal128.MAX_AS_UINT128) + ) print("✓ All truncate_to_max tests with values above MAX passed!") @@ -169,65 +176,51 @@ fn test_round_to_keep_first_n_digits() raises: # Test case 1: Value with more digits than to keep (round to nearest power of 10) var case1 = UInt128(997) var case1_expected = UInt128(1) - assert_equal( - dm.utility.round_to_keep_first_n_digits(case1, 0), case1_expected - ) + assert_equal(round_to_keep_first_n_digits(case1, 0), case1_expected) # Test case 2: Value with one more digit than to keep var case2 = UInt128(234567) var case2_expected = UInt128(23457) - assert_equal( - dm.utility.round_to_keep_first_n_digits(case2, 5), case2_expected - ) + assert_equal(round_to_keep_first_n_digits(case2, 5), case2_expected) # Test case 3: Value with fewer digits than to keep (should return original) var case3 = UInt128(234567) - assert_equal(dm.utility.round_to_keep_first_n_digits(case3, 29), case3) + assert_equal(round_to_keep_first_n_digits(case3, 29), case3) # Test case 4: Test banker's rounding with 5 (round to even) var case4a = UInt128(12345) # Last digit is 5, preceding digit is even var case4a_expected = UInt128(1234) - assert_equal( - dm.utility.round_to_keep_first_n_digits(case4a, 4), case4a_expected - ) + assert_equal(round_to_keep_first_n_digits(case4a, 4), case4a_expected) var case4b = UInt128(23455) # Last digit is 5, preceding digit is odd var case4b_expected = UInt128(2346) - assert_equal( - dm.utility.round_to_keep_first_n_digits(case4b, 4), case4b_expected - ) + assert_equal(round_to_keep_first_n_digits(case4b, 4), case4b_expected) # Test case 5: Rounding down (< 5) var case5 = UInt128(12342) var case5_expected = UInt128(1234) - assert_equal( - dm.utility.round_to_keep_first_n_digits(case5, 4), case5_expected - ) + assert_equal(round_to_keep_first_n_digits(case5, 4), case5_expected) # Test case 6: Rounding up (> 5) var case6 = UInt128(12347) var case6_expected = UInt128(1235) - assert_equal( - dm.utility.round_to_keep_first_n_digits(case6, 4), case6_expected - ) + assert_equal(round_to_keep_first_n_digits(case6, 4), case6_expected) # Test case 7: Zero input var case7 = UInt128(0) - assert_equal(dm.utility.round_to_keep_first_n_digits(case7, 5), UInt128(0)) + assert_equal(round_to_keep_first_n_digits(case7, 5), UInt128(0)) # Test case 8: Single digit input var case8 = UInt128(7) - assert_equal(dm.utility.round_to_keep_first_n_digits(case8, 1), UInt128(7)) + assert_equal(round_to_keep_first_n_digits(case8, 1), UInt128(7)) assert_equal( - dm.utility.round_to_keep_first_n_digits(case8, 0), UInt128(1) + round_to_keep_first_n_digits(case8, 0), UInt128(1) ) # Round to nearest power of 10 # Test case 9: Large value with UInt256 var case9 = UInt256(9876543210987654321) var case9_expected = UInt256(987654321098765432) - assert_equal( - dm.utility.round_to_keep_first_n_digits(case9, 18), case9_expected - ) + assert_equal(round_to_keep_first_n_digits(case9, 18), case9_expected) print("✓ All round_to_keep_first_n_digits tests passed!") @@ -237,39 +230,39 @@ fn test_bitcast() raises: print("Testing utility.bitcast...") # Test case 1: Basic decimal with fractional part - var original = Decimal("123.456") + var original = Decimal128("123.456") var coef = original.coefficient() - var bits = dm.utility.bitcast[DType.uint128](original) + var bits = bitcast[DType.uint128](original) assert_equal(coef, bits) # Test case 2: Zero value - var zero = Decimal(0) + var zero = Decimal128(0) var zero_coef = zero.coefficient() - var zero_bits = dm.utility.bitcast[DType.uint128](zero) + var zero_bits = bitcast[DType.uint128](zero) assert_equal(zero_coef, zero_bits) # Test case 3: Maximum value - var max_value = Decimal.MAX() + var max_value = Decimal128.MAX() var max_coef = max_value.coefficient() - var max_bits = dm.utility.bitcast[DType.uint128](max_value) + var max_bits = bitcast[DType.uint128](max_value) assert_equal(max_coef, max_bits) # Test case 4: Negative value - var negative = Decimal("-987.654321") + var negative = Decimal128("-987.654321") var neg_coef = negative.coefficient() - var neg_bits = dm.utility.bitcast[DType.uint128](negative) + var neg_bits = bitcast[DType.uint128](negative) assert_equal(neg_coef, neg_bits) # Test case 5: Different scales - var large_scale = Decimal("0.000000000123456789") + var large_scale = Decimal128("0.000000000123456789") var large_scale_coef = large_scale.coefficient() - var large_scale_bits = dm.utility.bitcast[DType.uint128](large_scale) + var large_scale_bits = bitcast[DType.uint128](large_scale) assert_equal(large_scale_coef, large_scale_bits) # Test case 6: Custom bit pattern - var test_decimal = Decimal(12345, 67890, 0xABCDEF, 0x55) + var test_decimal = Decimal128(12345, 67890, 0xABCDEF, 0x55) var test_coef = test_decimal.coefficient() - var test_bits = dm.utility.bitcast[DType.uint128](test_decimal) + var test_bits = bitcast[DType.uint128](test_decimal) assert_equal(test_coef, test_bits) print("✓ All bitcast tests passed!")