Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 25 additions & 4 deletions src/decimo/bigdecimal/arithmetics.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Implements functions for mathematical operations on BigDecimal objects.

from std import math

from decimo.errors import ZeroDivisionError
from decimo.rounding_mode import RoundingMode

# ===----------------------------------------------------------------------=== #
Expand Down Expand Up @@ -319,7 +320,12 @@ def true_divide(
"""
# Check for division by zero
if y.coefficient.is_zero():
raise Error("bigdecimal.arithmetics.true_divide(): Division by zero")
raise Error(
ZeroDivisionError(
message="Division by zero.",
function="true_divide()",
)
)

# Handle dividend of zero
if x.coefficient.is_zero():
Expand Down Expand Up @@ -692,7 +698,12 @@ def true_divide_inexact(

# Check for division by zero
if x2.coefficient.is_zero():
raise Error("Division by zero")
raise Error(
ZeroDivisionError(
message="Division by zero.",
function="true_divide_inexact()",
)
)

# Handle dividend of zero
if x1.coefficient.is_zero():
Expand Down Expand Up @@ -905,7 +916,12 @@ def truncate_divide(x1: BigDecimal, x2: BigDecimal) raises -> BigDecimal:
"""
# Check for division by zero
if x2.coefficient.is_zero():
raise Error("Division by zero")
raise Error(
ZeroDivisionError(
message="Division by zero.",
function="truncate_divide()",
)
)

# Handle dividend of zero
if x1.coefficient.is_zero():
Expand Down Expand Up @@ -945,7 +961,12 @@ def truncate_modulo(
"""
# Check for division by zero
if x2.coefficient.is_zero():
raise Error("Division by zero")
raise Error(
ZeroDivisionError(
message="Division by zero.",
function="truncate_modulo()",
)
)

return subtract(
x1,
Expand Down
24 changes: 15 additions & 9 deletions src/decimo/bigdecimal/bigdecimal.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ from std.memory import UnsafePointer
from std.python import PythonObject
from std import testing

from decimo.errors import DecimoError
from decimo.errors import DecimoError, ConversionError, ValueError
from decimo.rounding_mode import RoundingMode
from decimo.bigdecimal.rounding import round_to_precision
from decimo.bigint10.bigint10 import BigInt10
Expand Down Expand Up @@ -362,14 +362,22 @@ struct BigDecimal(
return Self(coefficient=BigUInt.zero(), scale=0, sign=False)

if value != value: # Check for NaN
raise Error("`from_scalar()`: Cannot convert NaN to BigUInt")
raise Error(
ValueError(
message="Cannot convert NaN to BigDecimal.",
function="BigDecimal.from_scalar()",
)
)
# Convert to string with full precision
try:
return Self.from_string(String(value))
except e:
raise Error(
"`from_scalar()`: Cannot get decimal from string\nTrace back: "
+ String(e),
ConversionError(
message="Cannot convert scalar to BigDecimal.",
function="BigDecimal.from_scalar()",
previous_error=e^,
)
)

@staticmethod
Expand Down Expand Up @@ -537,11 +545,9 @@ struct BigDecimal(

except e:
raise Error(
DecimoError(
file="src/decimo/bigdecimal/bigdecimal.mojo",
function="from_python_decimal()",
message="Failed to convert Python Decimal to BigDecimal: "
+ "as_tuple() returned invalid data or conversion failed.",
ConversionError(
message="Failed to convert Python Decimal to BigDecimal.",
function="BigDecimal.from_python_decimal()",
previous_error=e^,
),
)
Expand Down
7 changes: 6 additions & 1 deletion src/decimo/bigdecimal/constants.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"""

from decimo.bigdecimal.bigdecimal import BigDecimal
from decimo.errors import ValueError
from decimo.bigint10.bigint10 import BigInt10
from decimo.rounding_mode import RoundingMode

Expand Down Expand Up @@ -164,7 +165,11 @@ def pi(precision: Int) raises -> BigDecimal:
"""

if precision < 0:
raise Error("Precision must be non-negative")
raise Error(
ValueError(
message="Precision must be non-negative", function="pi()"
)
)

# TODO: When global variables are supported,
# we can check if we have a cached value for the requested precision.
Expand Down
140 changes: 114 additions & 26 deletions src/decimo/bigdecimal/exponential.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from std import math

from decimo.bigdecimal.bigdecimal import BigDecimal
from decimo.errors import ValueError, OverflowError, ZeroDivisionError
from decimo.rounding_mode import RoundingMode

# ===----------------------------------------------------------------------=== #
Expand Down Expand Up @@ -235,11 +236,20 @@ def power(
# Special cases
if base.coefficient.is_zero():
if exponent.coefficient.is_zero():
raise Error("Error in power: 0^0 is undefined")
raise Error(
ValueError(
message="0^0 is undefined.",
function="power()",
)
)
elif exponent.sign:
raise Error(
"Error in power: Division by zero (negative exponent with zero"
" base)"
ZeroDivisionError(
message=(
"Division by zero (negative exponent with zero base)."
),
function="power()",
)
)
else:
return BigDecimal(BigUInt.zero(), 0, False)
Expand All @@ -264,8 +274,13 @@ def power(
# Check for negative base with non-integer exponent
if base.sign and not exponent.is_integer():
raise Error(
"Error in power: Negative base with non-integer exponent would"
" produce a complex result"
ValueError(
message=(
"Negative base with non-integer exponent would produce"
" a complex result."
),
function="power()",
)
)

# Optimization for integer exponents
Expand Down Expand Up @@ -434,7 +449,12 @@ def root(x: BigDecimal, n: BigDecimal, precision: Int) raises -> BigDecimal:

# Check for n = 0
if n.coefficient.is_zero():
raise Error("Error in `root`: Cannot compute zeroth root")
raise Error(
ValueError(
message="Cannot compute zeroth root.",
function="root()",
)
)

# Special case for integer roots - use more efficient implementation
if not n.sign:
Expand Down Expand Up @@ -493,8 +513,13 @@ def root(x: BigDecimal, n: BigDecimal, precision: Int) raises -> BigDecimal:
var n_is_odd_reciprocal = is_odd_reciprocal(n)
if not n_is_integer and not n_is_odd_reciprocal:
raise Error(
"Error in `root`: Cannot compute non-odd-integer root of a"
" negative number"
ValueError(
message=(
"Cannot compute non-odd-integer root of a negative"
" number."
),
function="root()",
)
)
elif n_is_integer:
var result = integer_root(x, n, precision)
Expand Down Expand Up @@ -549,13 +574,28 @@ def integer_root(

# Handle special case: n must be a positive integer
if n.sign:
raise Error("Error in `root`: Root value must be positive")
raise Error(
ValueError(
message="Root value must be positive.",
function="integer_root()",
)
)

if not n.is_integer():
raise Error("Error in `root`: Root value must be an integer")
raise Error(
ValueError(
message="Root value must be an integer.",
function="integer_root()",
)
)

if n.coefficient.is_zero():
raise Error("Error in `root`: Cannot compute zeroth root")
raise Error(
ValueError(
message="Cannot compute zeroth root.",
function="integer_root()",
)
)

# Special case: n = 1 (1st root is just the number itself)
if n.is_one():
Expand Down Expand Up @@ -594,7 +634,10 @@ def integer_root(
result_sign = True
else: # n_uint.words[0] % 2 == 0: # Even root
raise Error(
"Error in `root`: Cannot compute even root of a negative number"
ValueError(
message="Cannot compute even root of a negative number.",
function="integer_root()",
)
)

# Extract n as Int for Newton's method
Expand Down Expand Up @@ -1183,7 +1226,10 @@ def sqrt_exact(x: BigDecimal, precision: Int) raises -> BigDecimal:
# Handle special cases
if x.sign:
raise Error(
"Error in `sqrt`: Cannot compute square root of negative number"
ValueError(
message="Cannot compute square root of a negative number.",
function="sqrt_exact()",
)
)

if x.coefficient.is_zero():
Expand Down Expand Up @@ -1316,7 +1362,10 @@ def sqrt_reciprocal(x: BigDecimal, precision: Int) raises -> BigDecimal:
# Handle special cases
if x.sign:
raise Error(
"Error in `sqrt`: Cannot compute square root of negative number"
ValueError(
message="Cannot compute square root of a negative number.",
function="sqrt_reciprocal()",
)
)

if x.coefficient.is_zero():
Expand Down Expand Up @@ -1498,7 +1547,10 @@ def sqrt_newton(x: BigDecimal, precision: Int) raises -> BigDecimal:
# Handle special cases
if x.sign:
raise Error(
"Error in `sqrt`: Cannot compute square root of negative number"
ValueError(
message="Cannot compute square root of a negative number.",
function="sqrt_newton()",
)
)

if x.coefficient.is_zero():
Expand Down Expand Up @@ -1583,7 +1635,10 @@ def sqrt_decimal_approach(x: BigDecimal, precision: Int) raises -> BigDecimal:
# Handle special cases
if x.sign:
raise Error(
"Error in `sqrt`: Cannot compute square root of negative number"
ValueError(
message="Cannot compute square root of a negative number.",
function="sqrt_decimal_approach()",
)
)

if x.coefficient.is_zero():
Expand Down Expand Up @@ -1765,7 +1820,11 @@ def exp(x: BigDecimal, precision: Int) raises -> BigDecimal:
# For very large positive values, result will overflow BigDecimal capacity
# TODO: Use BigInt10 as scale can avoid overflow in this case
if not x.sign and x.adjusted() >= 20: # x > 10^20
raise Error("Error in `exp`: Result too large to represent")
raise Error(
OverflowError(
message="Result too large to represent", function="exp()"
)
)

# For very large negative values, result will be effectively zero
if x.sign and x.adjusted() >= 20: # x < -10^20
Expand Down Expand Up @@ -1973,10 +2032,17 @@ def ln(
# Handle special cases
if x.sign:
raise Error(
"Error in `ln`: Cannot compute logarithm of negative number"
ValueError(
message="Cannot compute logarithm of negative number",
function="ln()",
)
)
if x.coefficient.is_zero():
raise Error("Error in `ln`: Cannot compute logarithm of zero")
raise Error(
ValueError(
message="Cannot compute logarithm of zero", function="ln()"
)
)
if x == BigDecimal(BigUInt.one(), 0, False):
return BigDecimal(BigUInt.zero(), 0, False) # ln(1) = 0

Expand Down Expand Up @@ -2066,21 +2132,36 @@ def log(x: BigDecimal, base: BigDecimal, precision: Int) raises -> BigDecimal:
# Special cases
if x.sign:
raise Error(
"Error in log(): Cannot compute logarithm of a negative number"
ValueError(
message="Cannot compute logarithm of a negative number",
function="log()",
)
)
if x.coefficient.is_zero():
raise Error("Error in log(): Cannot compute logarithm of zero")
raise Error(
ValueError(
message="Cannot compute logarithm of zero", function="log()"
)
)

# Base validation
if base.sign:
raise Error("Error in log(): Cannot use a negative base")
raise Error(
ValueError(message="Cannot use a negative base", function="log()")
)
if base.coefficient.is_zero():
raise Error("Error in log(): Cannot use zero as a base")
raise Error(
ValueError(message="Cannot use zero as a base", function="log()")
)
if (
base.coefficient.number_of_digits() == base.scale + 1
and base.coefficient.words[-1] == 1
):
raise Error("Error in log(): Cannot use base 1 for logarithm")
raise Error(
ValueError(
message="Cannot use base 1 for logarithm", function="log()"
)
)

# Special cases
if (
Expand Down Expand Up @@ -2129,10 +2210,17 @@ def log10(x: BigDecimal, precision: Int) raises -> BigDecimal:
# Special cases
if x.sign:
raise Error(
"Error in log10(): Cannot compute logarithm of a negative number"
ValueError(
message="Cannot compute logarithm of a negative number",
function="log10()",
)
)
if x.coefficient.is_zero():
raise Error("Error in log10(): Cannot compute logarithm of zero")
raise Error(
ValueError(
message="Cannot compute logarithm of zero", function="log10()"
)
)

# Fast path: Powers of 10 are handled directly
if x.coefficient.is_power_of_10():
Expand Down
Loading
Loading