diff --git a/README.md b/README.md index d9d305b..7c39fb1 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ A comprehensive decimal and integer mathematics library for [Mojo](https://www.modular.com/mojo). -**[中文·漢字»](https://zhuyuhao.com/decimojo/docs/readme_zht.html)** | **[Repository on GitHub»](https://github.com/forfudan/decimojo)** +**[中文·漢字»](https://zhuyuhao.com/decimojo/docs/readme_zht.html)** | **[Repository on GitHub»](https://github.com/forfudan/decimojo)** | **[Changelog](https://zhuyuhao.com/decimojo/docs/changelog.html)** ## Overview diff --git a/docs/changelog.md b/docs/changelog.md index ac595af..5779331 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ -# DeciMojo released changelog +# DeciMojo changelog -This is a list of RELEASED changes for the DeciMojo Package. +This is a list of RELEASED changes for the DeciMojo Package. For the unreleased changes, please refer to **[changelog_unreleased](https://zhuyuhao.com/decimojo/docs/changelog_unreleased.html)**. ## 01/04/2025 (v0.2.0) diff --git a/docs/changelog_unreleased.md b/docs/changelog_unreleased.md new file mode 100644 index 0000000..e97bab3 --- /dev/null +++ b/docs/changelog_unreleased.md @@ -0,0 +1,11 @@ +# DeciMojo unreleased changelog + +This is a list of UNRELEASED changes for the DeciMojo Package. For the released changes, please refer to **[changelog](https://zhuyuhao.com/decimojo/docs/changelog.html)**. + +### ⭐️ New + +- Add comprehensive `BigDecimal` implementation with unlimited precision arithmetic. + +### 🛠️ Fixed + +- Fix a bug in `BigUInt` multiplication where the calcualtion of carry is mistakenly skipped if a word of x2 is zero (PR #70). diff --git a/mojoproject.toml b/mojoproject.toml index 1fbee2a..389685b 100644 --- a/mojoproject.toml +++ b/mojoproject.toml @@ -10,7 +10,7 @@ readme = "README.md" version = "0.2.0" [dependencies] -max = ">=25.2" +max = "==25.2" [tasks] # format the code @@ -31,7 +31,11 @@ test = "magic run package && magic run mojo test tests --filter" t = "clear && magic run package && magic run mojo test tests --filter" # benches -bench_dec = "clear && magic run package && cd benches/decimal && magic run mojo -I ../ bench.mojo && cd ../.. && magic run clean" -bench_bint = "clear && magic run package && cd benches/bigint && magic run mojo -I ../ bench.mojo && cd ../.. && magic run clean" -bench_buint = "clear && magic run package && cd benches/biguint && magic run mojo -I ../ bench.mojo && cd ../.. && magic run clean" -bench_bdec = "clear && magic run package && cd benches/bigdecimal && magic run mojo -I ../ bench.mojo && cd ../.. && magic run clean" \ No newline at end of file +bench_decimal = "clear && magic run package && cd benches/decimal && magic run mojo -I ../ bench.mojo && cd ../.. && magic run clean" +bench_bigint = "clear && magic run package && cd benches/bigint && magic run mojo -I ../ bench.mojo && cd ../.. && magic run clean" +bench_biguint = "clear && magic run package && cd benches/biguint && magic run mojo -I ../ bench.mojo && cd ../.. && magic run clean" +bench_bigdecimal = "clear && magic run package && cd benches/bigdecimal && magic run mojo -I ../ bench.mojo && cd ../.. && magic run clean" +bench_dec = "magic run bench_decimal" +bench_bint = "magic run bench_bigint" +bench_buint = "magic run bench_biguint" +bench_bdec = "magic run bench_bigdecimal" diff --git a/src/decimojo/bigdecimal/arithmetics.mojo b/src/decimojo/bigdecimal/arithmetics.mojo index 4a1a505..239a707 100644 --- a/src/decimojo/bigdecimal/arithmetics.mojo +++ b/src/decimojo/bigdecimal/arithmetics.mojo @@ -188,31 +188,32 @@ fn multiply(x1: BigDecimal, x2: BigDecimal) raises -> BigDecimal: ) +# TODO: Optimize when divided by power of 10 fn true_divide( - x1: BigDecimal, x2: BigDecimal, max_precision: Int = 28 + x1: BigDecimal, x2: BigDecimal, precision: Int = 28 ) raises -> BigDecimal: """Returns the quotient of two numbers. Args: x1: The first operand (dividend). x2: The second operand (divisor). - max_precision: The maximum precision for the result. It should be - non-negative. + precision: The number of significant digits in the result. Returns: - The quotient of x1 and x2, with precision up to max_precision. + The quotient of x1 and x2, with precision up to `precision` + significant digits. Notes: - If the coefficients can be divided exactly, the number of digits after the decimal point is the difference of the scales of the two operands. - If the coefficients cannot be divided exactly, the number of digits after - the decimal point is max_precision. + the decimal point is precision. - If the division is not exact, the number of digits after the decimal - point is calcuated to max_precision + BUFFER_DIGITS, and the result is - rounded to max_precision according to the specified rules. + point is calcuated to precision + BUFFER_DIGITS, and the result is + rounded to precision according to the specified rules. """ - alias BUFFER_DIGITS = 2 # Buffer digits for rounding + alias BUFFER_DIGITS = 9 # Buffer digits for rounding # Check for division by zero if x2.coefficient.is_zero(): @@ -221,87 +222,89 @@ fn true_divide( # Handle dividend of zero if x1.coefficient.is_zero(): return BigDecimal( - coefficient=BigUInt(UInt32(0)), - scale=max(0, x1.scale - x2.scale), + coefficient=BigUInt.ZERO, + scale=x1.scale - x2.scale, sign=x1.sign != x2.sign, ) - # TODO: Divided by power of 10 + # First estimate the number of significant digits needed in the dividend + # to produce a result with precision significant digits + var x1_digits = x1.coefficient.number_of_digits() + var x2_digits = x2.coefficient.number_of_digits() - # Check whether the coefficients can be divided exactly + # Check whether the coefficients can already be divided exactly # If division is exact, return the result immediately - if len(x1.coefficient.words) >= len(x2.coefficient.words): - # Check if x1 is divisible by x2 + if x1_digits >= x2_digits: var quotient: BigUInt var remainder: BigUInt quotient, remainder = x1.coefficient.divmod(x2.coefficient) + # Calculate the expected result scale + var result_scale = x1.scale - x2.scale if remainder.is_zero(): - return BigDecimal( - coefficient=quotient, - scale=x1.scale - x2.scale, - sign=x1.sign != x2.sign, - ) + # For exact division, calculate significant digits in result + var num_sig_digits = quotient.number_of_digits() + # If the significant digits are within precision, return as is + if num_sig_digits <= precision: + return BigDecimal( + coefficient=quotient^, + scale=result_scale, + sign=x1.sign != x2.sign, + ) + else: # num_sig_digits > precision + # Otherwise, need to truncate to max precision + var digits_to_remove = num_sig_digits - precision + var quotient = quotient.remove_trailing_digits_with_rounding( + digits_to_remove, + RoundingMode.ROUND_HALF_EVEN, + remove_extra_digit_due_to_rounding=True, + ) + result_scale -= digits_to_remove + return BigDecimal( + coefficient=quotient^, + scale=result_scale, + sign=x1.sign != x2.sign, + ) + + # Calculate how many additional digits we need in the dividend + # We want: (x1_digits + additional) - x2_digits ≈ precision + # Scale factor needs to account for the scales of the operands? + var additional_digits = precision + BUFFER_DIGITS - (x1_digits - x2_digits) + additional_digits = max(0, additional_digits) + + # Scale up the dividend to ensure sufficient precision + var scaled_x1 = x1.coefficient + if additional_digits > 0: + scaled_x1 = scaled_x1.scale_up_by_power_of_10(additional_digits) + + # Perform division + var quotient: BigUInt + var remainder: BigUInt + quotient, remainder = scaled_x1.divmod(x2.coefficient) + var result_scale = additional_digits + x1.scale - x2.scale - # Calculate how many extra digits we need to scale x1 by - # We want (max_precision + BUFFER_DIGITS) decimal places in the result - var desired_result_scale = max_precision + BUFFER_DIGITS - var current_result_scale = x1.scale - x2.scale - var scale_factor = max(0, desired_result_scale - current_result_scale) - - # Scale the dividend coefficient - var scaled_x1_coefficient = x1.coefficient - if scale_factor > 0: - scaled_x1_coefficient = x1.coefficient.scale_up_by_power_of_10( - scale_factor - ) + # Check if division is exact + var is_exact = remainder.is_zero() - # Perform the division and get remainder - var result_coefficient: BigUInt - var remainder: BigUInt - result_coefficient, remainder = scaled_x1_coefficient.divmod(x2.coefficient) - var result_scale = x1.scale + scale_factor - x2.scale + # Check total digits in the result + var result_digits = quotient.number_of_digits() # If the division is exact # we may need to remove the extra trailing zeros. # TODO: Think about the behavior, whether division should always return the - # maximum precision even if the result scale is less than max_precision. - # Example: 1 / 1 = 1.0000000000000000000000000000 - if remainder.is_zero(): - # result_scale == scale_factor + (x1.scale - x2.scale) - var number_of_trailing_zeros = result_coefficient.number_of_trailing_zeros() - - # If number_of_trailing_zeros <= scale_factor: - # Just remove the trailing zeros, the scale is larger than expected - # scale (x1.scale - x2.scale) because the division is exact but with - # fractional part. - # If number_of_trailing_zeros > scale_factor: - # We can remove at most scale_factor digits because the result scale - # should be no less than expected scale - var number_of_zeros_to_remove = min( - number_of_trailing_zeros, scale_factor - ) - result_coefficient = result_coefficient.scale_down_by_power_of_10( - number_of_zeros_to_remove - ) - - return BigDecimal( - coefficient=result_coefficient^, - scale=result_scale - number_of_zeros_to_remove, - sign=x1.sign != x2.sign, - ) + # `precision` even if the result scale is less than precision. + # Example: 10 / 4 = 2.50000000000000000000000000000 + # If exact division, remove trailing zeros + if is_exact: + var num_trailing_zeros = quotient.number_of_trailing_zeros() + if num_trailing_zeros > 0: + quotient = quotient.scale_down_by_power_of_10(num_trailing_zeros) + result_scale -= num_trailing_zeros + # Recalculate digits after removing trailing zeros + result_digits = quotient.number_of_digits() # Otherwise, the division is not exact or have too many digits - # round to max_precision - # TODO: Use round() function when it is available - var digits_to_remove = result_scale - max_precision - if digits_to_remove > BUFFER_DIGITS: - print( - "Warning: Remove (={}) more than BUFFER_DIGITS digits (={}), the" - " algorithm may not be optimal.".format( - digits_to_remove, BUFFER_DIGITS - ) - ) - + # round to precision + # If we have too many significant digits, reduce to precision # Extract the digits to be rounded # Example: 2 digits to remove # divisor = 100 @@ -312,26 +315,18 @@ fn true_divide( # If rounding_digits == half_divisor, round up if the last digit of # result_coefficient is odd # If rounding_digits < half_divisor, round down - var divisor = BigUInt.ONE.scale_up_by_power_of_10(digits_to_remove) - var half_divisor = divisor // BigUInt(2) - var rounding_digits: BigUInt - result_coefficient, rounding_digits = result_coefficient.divmod(divisor) - - # Apply rounding rules - var round_up = False - if rounding_digits > half_divisor: - round_up = True - elif rounding_digits == half_divisor: - round_up = result_coefficient.words[0] % 2 == 1 - - if round_up: - result_coefficient += BigUInt(1) - - # Update scale - result_scale = max_precision + if result_digits > precision: + var digits_to_remove = result_digits - precision + quotient = quotient.remove_trailing_digits_with_rounding( + digits_to_remove, + RoundingMode.ROUND_HALF_EVEN, + remove_extra_digit_due_to_rounding=True, + ) + # Adjust the scale accordingly + result_scale -= digits_to_remove return BigDecimal( - coefficient=result_coefficient^, + coefficient=quotient^, scale=result_scale, sign=x1.sign != x2.sign, ) diff --git a/src/decimojo/bigdecimal/bigdecimal.mojo b/src/decimojo/bigdecimal/bigdecimal.mojo index 2030ac0..15cdf11 100644 --- a/src/decimojo/bigdecimal/bigdecimal.mojo +++ b/src/decimojo/bigdecimal/bigdecimal.mojo @@ -298,51 +298,95 @@ struct BigDecimal: # Type-transfer or output methods that are not dunders # ===------------------------------------------------------------------=== # - fn to_string(self, line_width: Int = 0) -> String: + fn to_string( + self, + precision: Int = 28, + scientific_notation: Bool = False, + line_width: Int = 0, + ) -> String: """Returns string representation of the number. Args: + precision: The threshold for scientific notation. + If the digits to display is greater than this value, + the number is represented in scientific notation. + scientific_notation: If True, the number is always represented in + scientific notation. If False, the format is determined by the + `precision` argument. line_width: The maximum line width for the string representation. If 0, the string is returned as a single line. If greater than 0, the string is split into multiple lines. Returns: A string representation of the number. + + Notes: + + In follwing cases, scientific notation is used: + 1. `scientific_notation` is True. + 2. exponent >= `precision`. + 3. There 6 or more leading zeros after decimal and before significand. + 4. The scale is negative. """ if self.coefficient.is_unitialized(): return String("Unitilialized maginitude of BigDecimal") var result = String("-") if self.sign else String("") - var coefficient_string = self.coefficient.to_string() - if self.scale == 0: - result += coefficient_string - - elif self.scale > 0: - if self.scale < len(coefficient_string): - # Example: 123_456 with scale 3 -> 123.456 - result += coefficient_string[ - : len(coefficient_string) - self.scale - ] + # Check whether scientific notation is needed + var exponent = self.coefficient.number_of_digits() - 1 - self.scale + var exponent_ge_precision = exponent >= precision + var leading_zeros_too_many = exponent <= Int(-6) + var negative_scale = self.scale < 0 + + if ( + scientific_notation + or exponent_ge_precision + or leading_zeros_too_many + or negative_scale + ): + # Use scientific notation + var exponent_string = String(exponent) + result += coefficient_string[0] + if len(coefficient_string) > 1: result += "." - result += coefficient_string[ - len(coefficient_string) - self.scale : - ] - else: - # Example: 123_456 with scale 6 -> 0.123_456 - # Example: 123_456 with scale 7 -> 0.012_345_6 - result += "0." - result += "0" * (self.scale - len(coefficient_string)) - result += coefficient_string + result += coefficient_string[1:] + result += "E" + if exponent > 0: + result += "+" + result += exponent_string else: - # scale < 0 - # Example: 12_345 with scale -3 -> 12_345_000 - result += coefficient_string - result += "0" * (-self.scale) + # Normal notation + if self.scale == 0: + result += coefficient_string + + elif self.scale > 0: + if self.scale < len(coefficient_string): + # Example: 123_456 with scale 3 -> 123.456 + result += coefficient_string[ + : len(coefficient_string) - self.scale + ] + result += "." + result += coefficient_string[ + len(coefficient_string) - self.scale : + ] + else: + # Example: 123_456 with scale 6 -> 0.123_456 + # Example: 123_456 with scale 7 -> 0.012_345_6 + result += "0." + result += "0" * (self.scale - len(coefficient_string)) + result += coefficient_string + + else: + # scale < 0 + # Example: 12_345 with scale -3 -> 12_345_000 + result += coefficient_string + result += "0" * (-self.scale) + # Split the result in multiple lines if line_width > 0 if line_width > 0: var start = 0 var end = line_width @@ -415,10 +459,34 @@ struct BigDecimal: fn __truediv__(self, other: Self) raises -> Self: return decimojo.bigdecimal.arithmetics.true_divide(self, other) + # ===------------------------------------------------------------------=== # + # Mathematical methods that do not implement a trait (not a dunder) + # ===------------------------------------------------------------------=== # + + @always_inline + fn true_divide(self, other: Self, precision: Int) raises -> Self: + """Returns the result of true division of two BigDecimal numbers. + See `arithmetics.true_divide()` for more information. + """ + return decimojo.bigdecimal.arithmetics.true_divide( + self, other, precision + ) + # ===------------------------------------------------------------------=== # # Other methods # ===------------------------------------------------------------------=== # + fn exponent(self) -> Int: + """Returns the exponent of the number in scientific notation. + + Notes: + + 123.45 (coefficient = 12345, scale = 2) is represented as 1.2345E+2. + 0.00123 (coefficient = 123, scale = 5) is represented as 1.23E-3. + 123000 (coefficient = 123, scale = -3) is represented as 1.23E+5. + """ + return self.coefficient.number_of_digits() - 1 - self.scale + fn extend_precision(self, precision_diff: Int) raises -> BigDecimal: """Returns a number with additional decimal places (trailing zeros). This multiplies the coefficient by 10^precision_diff and increases diff --git a/src/decimojo/biguint/arithmetics.mojo b/src/decimojo/biguint/arithmetics.mojo index ecda649..635fb4d 100644 --- a/src/decimojo/biguint/arithmetics.mojo +++ b/src/decimojo/biguint/arithmetics.mojo @@ -1014,7 +1014,8 @@ fn scale_down_by_power_of_10(x: BigUInt, n: Int) raises -> BigUInt: var carry = UInt32(0) var divisor: UInt32 if digit_shift == 0: - divisor = UInt32(1) + # No need to shift, just return the result + return result^ elif digit_shift == 1: divisor = UInt32(10) elif digit_shift == 2: diff --git a/src/decimojo/biguint/biguint.mojo b/src/decimojo/biguint/biguint.mojo index 8b096bd..9126312 100644 --- a/src/decimojo/biguint/biguint.mojo +++ b/src/decimojo/biguint/biguint.mojo @@ -852,7 +852,19 @@ struct BigUInt(Absable, IntableRaising, Writable): @always_inline fn ith_digit(self, i: Int) raises -> UInt8: - """Returns the ith digit of the BigUInt.""" + """Returns the ith least significant digit of the BigUInt. + + Args: + i: The index of the digit to return. The least significant digit + is at index 0. + + Returns: + The ith least significant digit of the BigUInt. + + Raises: + Error: If the index is negative or larger than the number of digits + in the BigUInt. + """ if i < 0: raise Error("Error in `ith_digit()`: The index is negative") if i >= len(self.words) * 9: @@ -868,6 +880,24 @@ struct BigUInt(Absable, IntableRaising, Writable): digit = word % 10 return UInt8(digit) + @always_inline + fn number_of_digits(self) -> Int: + """Returns the number of digits in the BigUInt. + + Notes: + + Zero has 1 digit. + """ + if self.is_zero(): + return 1 + + var result: Int = (len(self.words) - 1) * 9 + var last_word = self.words[len(self.words) - 1] + while last_word > 0: + result += 1 + last_word = last_word // 10 + return result + @always_inline fn number_of_words(self) -> Int: """Returns the number of words in the BigInt.""" @@ -893,3 +923,83 @@ struct BigUInt(Absable, IntableRaising, Writable): """Removes leading words of 0 from BigUInt's internal representation.""" while len(self.words) > 1 and self.words[-1] == 0: self.words.resize(len(self.words) - 1) + + @always_inline + fn remove_trailing_digits_with_rounding( + self, + ndigits: Int, + rounding_mode: RoundingMode = RoundingMode.ROUND_DOWN, + remove_extra_digit_due_to_rounding: Bool = False, + ) raises -> Self: + """Removes trailing digits from the BigUInt. + + Args: + ndigits: The number of digits to remove. + rounding_mode: The rounding mode to use. + Default is RoundingMode.ROUND_DOWN, which is the same as + `scale_down_by_power_of_10()`. + remove_extra_digit_due_to_rounding: If True, remove an trailing + digit if the rounding mode result in an extra digit. + Default is False. + + Returns: + The BigUInt with the trailing digits removed. + + Notes: + + Rounding can result in an extra digit. Exmaple: remove last 1 digit of + 999 with rounding up results in 100. If + `remove_extra_digit_due_to_rounding` is True, the result will be 10. + """ + if ndigits < 0: + raise Error( + "Error in `remove_trailing_digits()`: The number of digits to" + " remove is negative" + ) + + if ndigits == 0: + return self + + if ndigits > self.number_of_digits(): + raise Error( + "Error in `remove_trailing_digits()`: The number of digits to" + " remove is larger than the number of digits in the BigUInt" + ) + + # scale_down_by_power_of_10 is the same as removing the last n digits + var result = self.scale_down_by_power_of_10(ndigits) + var round_up: Bool = False + + if rounding_mode == RoundingMode.ROUND_DOWN: + pass + elif rounding_mode == RoundingMode.ROUND_UP: + if self.number_of_trailing_zeros() < ndigits: + round_up = True + elif rounding_mode == RoundingMode.ROUND_HALF_UP: + if self.ith_digit(ndigits - 1) >= 5: + round_up = True + elif rounding_mode == RoundingMode.ROUND_HALF_EVEN: + var cut_off_digit = self.ith_digit(ndigits - 1) + if cut_off_digit > 5: + round_up = True + elif cut_off_digit < 5: + pass + else: # cut_off_digit == 5 + if self.number_of_trailing_zeros() < ndigits - 1: + round_up = True + else: + round_up = self.ith_digit(ndigits) % 2 == 1 + else: + raise Error( + "Error in `remove_trailing_digits()`: Unknown rounding mode" + ) + + if round_up: + result = result + BigUInt.ONE + # Check whether rounding results in extra digit + if result.is_power_of_10(): + if remove_extra_digit_due_to_rounding: + result = result.scale_down_by_power_of_10( + 1, + ) + return result^ diff --git a/tests/bigdecimal/test_bigdecimal_divide.mojo b/tests/bigdecimal/test_bigdecimal_divide.mojo index 2fce93a..5a13c12 100644 --- a/tests/bigdecimal/test_bigdecimal_divide.mojo +++ b/tests/bigdecimal/test_bigdecimal_divide.mojo @@ -73,7 +73,8 @@ fn test_true_divide() raises: passed += 1 except e: print( - "✗ Case", + "=" * 50, + "\n", i + 1, "failed:", test_case.description, diff --git a/tests/bigdecimal/test_data/bigdecimal_arithmetics.toml b/tests/bigdecimal/test_data/bigdecimal_arithmetics.toml index 063aed4..18c3052 100644 --- a/tests/bigdecimal/test_data/bigdecimal_arithmetics.toml +++ b/tests/bigdecimal/test_data/bigdecimal_arithmetics.toml @@ -145,7 +145,7 @@ description = "Small values with different scales" [[addition_tests]] a = "1.23e5" b = "4.56e4" -expected = "168600" +expected = "1.686E+5" description = "Addition with scientific notation" [[addition_tests]] @@ -465,7 +465,7 @@ description = "Small values with different scales" [[subtraction_tests]] a = "1.23e5" b = "4.56e4" -expected = "77400" +expected = "7.74E+4" description = "Subtraction with scientific notation" [[subtraction_tests]] @@ -760,7 +760,7 @@ description = "Small decimal multiplication" [[multiplication_tests]] a = "1.23e5" b = "4.56e2" -expected = "56088000" +expected = "5.6088E+7" description = "Multiplication with scientific notation" [[multiplication_tests]] diff --git a/tests/bigdecimal/test_data/bigdecimal_divide.toml b/tests/bigdecimal/test_data/bigdecimal_divide.toml index 6229ad0..614f117 100644 --- a/tests/bigdecimal/test_data/bigdecimal_divide.toml +++ b/tests/bigdecimal/test_data/bigdecimal_divide.toml @@ -107,7 +107,7 @@ description = "Division with repeating decimal (2/3)" [[division_tests]] a = "10" b = "6" -expected = "1.6666666666666666666666666667" +expected = "1.666666666666666666666666667" description = "Division with repeating decimal (10/6)" [[division_tests]] @@ -119,7 +119,7 @@ description = "Division with repeating digit (1/9)" [[division_tests]] a = "100" b = "3" -expected = "33.3333333333333333333333333333" +expected = "33.33333333333333333333333333" description = "Large repeating division" # === LARGE AND SMALL NUMBER TESTS === @@ -132,7 +132,7 @@ description = "Large number simple division" [[division_tests]] a = "1" b = "9999999999999999999999999999" -expected = "0.0000000000000000000000000001" +expected = "0.0000000000000000000000000001000000000000000000000000000" description = "One divided by large number" [[division_tests]] @@ -144,7 +144,7 @@ description = "Small number division" [[division_tests]] a = "1000000000000000000000000000000" b = "0.0000000000000000000000000001" -expected = "10000000000000000000000000000000000000000000000000000000000" +expected = "1.000000000000000000000000000E+58" description = "Large divided by small" [[division_tests]] @@ -157,26 +157,26 @@ description = "Small divided by large" [[division_tests]] a = "1.23e5" b = "4.56e2" -expected = "269.7368421052631578947368421053" +expected = "269.7368421052631578947368421" description = "Division with scientific notation" [[division_tests]] a = "1.23e-5" b = "4.56e-2" -expected = "0.0002697368421052631578947368" +expected = "0.0002697368421052631578947368421" description = "Division with negative exponents" [[division_tests]] a = "1.23e5" b = "4.56e-2" -expected = "2697368.4210526315789473684210526316" +expected = "2697368.421052631578947368421" description = "Division with mixed exponents" # === SPECIAL CASES === [[division_tests]] a = "3.14159265358979323846" b = "2.71828182845904523536" -expected = "1.1557273497909217179092429607" +expected = "1.155727349790921717909242961" description = "Division of mathematical constants (PI / E)" [[division_tests]] @@ -263,7 +263,7 @@ description = "Financial division (dollars)" [[division_tests]] a = "100.00" b = "3" -expected = "33.3333333333333333333333333333" +expected = "33.33333333333333333333333333" description = "Financial division with repeating result" [[division_tests]] @@ -282,7 +282,7 @@ description = "Circle division (degrees in a circle / months)" [[division_tests]] a = "1000" b = "3" -expected = "333.3333333333333333333333333333" +expected = "333.3333333333333333333333333" description = "Division for equal distribution" [[division_tests]] @@ -307,7 +307,7 @@ description = "Division of very small numbers" [[division_tests]] a = "1" b = "0.000000000000000000000000001" -expected = "1000000000000000000000000000" +expected = "1E+27" description = "One divided by very small number" [[division_tests]]