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
2 changes: 1 addition & 1 deletion benches/decimal/bench_root.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ fn run_benchmark(
mojo_time = 1 # Prevent division by zero

# Benchmark Python implementation (if possible)
var python_time: Float64 = 0
var python_time: Float64
if not is_negative_odd_root and not (
py_result is Python.evaluate("None")
): # Correct way to check for None
Expand Down
2 changes: 1 addition & 1 deletion docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Version 0.4.1 of DeciMojo introduces implicit type conversion between built-in i

### ⭐️ New

Now DeciMojo supports implicit type conversion between built-in integeral types (`Int`, `UInt`, `Int8`, `UInt8`, `Int16`, `UInt16`, `Int32`, `UInt32`, `Int64`, `UInt64`, `Int128`,`UInt128`, `Int256`, and `UInt256`) and the arbitrary-precision integer types (`BigUInt`, `BigInt`, and `BigDecimal`). This allows you to use these built-in types directly in arithmetic operations with `BigInt` and `BigUInt` without explicit conversion. The merged type will always be the most compatible one (PR #89, PR #90).
Now DeciMojo supports implicit type conversion between built-in integeral types (`Int`, `UInt`, `Int8`, `UInt8`, `Int16`, `UInt16`, `Int32`, `UInt32`, `Int64`, `UInt64`, `Int128`,`UInt128`, `Int256`, and `UInt256`) and the arbitrary-precision types (`BigUInt`, `BigInt`, and `BigDecimal`). This allows you to use these built-in types directly in arithmetic operations with `BigInt` and `BigUInt` without explicit conversion. The merged type will always be the most compatible one (PR #89, PR #90).

For example, you can now do the following:

Expand Down
1 change: 1 addition & 0 deletions docs/todo.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ This is a to-do list for Yuhao's personal use.
- [x] (#31) The `exp()` function performs slower than Python's counterpart in specific cases. Detailed investigation reveals the bottleneck stems from multiplication operations between decimals with significant fractional components. These operations currently rely on UInt256 arithmetic, which introduces performance overhead. Optimization of the `multiply()` function is required to address these performance bottlenecks, particularly for high-precision decimal multiplication with many digits after the decimal point.
- [ ] When Mojo supports global variables, implement a global variable for the `BigDecimal` class to store the precision of the decimal number. This will allow users to set the precision globally, rather than having to set it for each function of the `BigDecimal` class.
- [ ] Implement different methods for augmented arithmetic assignments to improve memeory-efficiency and performance.
- [ ] Implement different methods for adding decimojo types with `Int` types so that an implicit conversion is not required.
- [ ] Implement a method `remove_trailing_zeros` for `BigUInt`.
4 changes: 2 additions & 2 deletions pixi.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ clean = "rm tests/decimojo.mojopkg && rm benches/decimojo.mojopkg && rm tests/to
c = "clear && pixi run clean"

# tests (use the mojo testing tool)
test = "pixi run package && pixi run mojo test tests --filter"
t = "clear && pixi run package && pixi run mojo test tests --filter"
test = "pixi run package && pixi run mojo test tests -D ASSERT=all --filter"
t = "clear && pixi run package && pixi run mojo test tests -D ASSERT=all --filter"

# benches
bench_decimal = "clear && pixi run package && cd benches/decimal && pixi run mojo -I ../ bench.mojo && cd ../.. && pixi run clean"
Expand Down
12 changes: 4 additions & 8 deletions src/decimojo/bigdecimal/arithmetics.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,8 @@ fn add(x1: BigDecimal, x2: BigDecimal) raises -> BigDecimal:
# Handle addition based on signs
if x1.sign == x2.sign:
# Same sign: Add coefficients, keep sign
var result_coef = coef1 + coef2
return BigDecimal(
coefficient=result_coef^, scale=max_scale, sign=x1.sign
)
coef1.add_inplace(coef2)
return BigDecimal(coefficient=coef1^, scale=max_scale, sign=x1.sign)
# Different signs: Subtract smaller coefficient from larger
if coef1 > coef2:
# |x1| > |x2|, result sign is x1's sign
Expand Down Expand Up @@ -143,10 +141,8 @@ fn subtract(x1: BigDecimal, x2: BigDecimal) raises -> BigDecimal:
# Handle subtraction based on signs
if x1.sign != x2.sign:
# Different signs: x1 - (-x2) = x1 + x2, or (-x1) - x2 = -(x1 + x2)
var result_coef = coef1 + coef2
return BigDecimal(
coefficient=result_coef^, scale=max_scale, sign=x1.sign
)
coef1.add_inplace(coef2)
return BigDecimal(coefficient=coef1^, scale=max_scale, sign=x1.sign)

# Same signs: Must perform actual subtraction
if coef1 > coef2:
Expand Down
2 changes: 1 addition & 1 deletion src/decimojo/bigdecimal/bigdecimal.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -829,7 +829,7 @@ struct BigDecimal(
)

@always_inline
fn internal_representation(self) raises:
fn print_internal_representation(self) raises:
"""Prints the internal representation of the BigDecimal."""
var line_width = 30
var string_of_number = self.to_string(line_width=line_width).split("\n")
Expand Down
4 changes: 3 additions & 1 deletion src/decimojo/bigint/arithmetics.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,11 @@ fn add_inplace(mut x1: BigInt, x2: BigInt) raises -> None:
# If signs are different, delegate to `subtract`
if x1.sign != x2.sign:
x1 = subtract(x1, -x2)
return

# Same sign: add magnitudes in place
x1.magnitude += x2.magnitude
else:
x1.magnitude += x2.magnitude


fn subtract(x1: BigInt, x2: BigInt) raises -> BigInt:
Expand Down
4 changes: 2 additions & 2 deletions src/decimojo/bigint/bigint.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,7 @@ struct BigInt(Absable, IntableRaising, Representable, Stringable, Writable):
@always_inline
fn __iadd__(mut self, other: Int) raises:
# Optimize the case `i += 1`
if other == 1:
if (self >= 0) and (other == 1):
self.magnitude.add_inplace_by_1()
else:
decimojo.bigint.arithmetics.add_inplace(self, other)
Expand Down Expand Up @@ -712,7 +712,7 @@ struct BigInt(Absable, IntableRaising, Representable, Stringable, Writable):
# Internal methods
# ===------------------------------------------------------------------=== #

fn internal_representation(self) raises:
fn print_internal_representation(self) raises:
"""Prints the internal representation details of a BigInt."""
var string_of_number = self.to_string(line_width=30).split("\n")
print("\nInternal Representation Details of BigInt")
Expand Down
96 changes: 40 additions & 56 deletions src/decimojo/biguint/arithmetics.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ from decimojo.rounding_mode import RoundingMode
# ===----------------------------------------------------------------------=== #


fn add(x1: BigUInt, x2: BigUInt) raises -> BigUInt:
fn add(x1: BigUInt, x2: BigUInt) -> BigUInt:
"""Returns the sum of two unsigned integers.

Args:
Expand All @@ -69,35 +69,27 @@ fn add(x1: BigUInt, x2: BigUInt) raises -> BigUInt:
Returns:
The sum of the two unsigned integers.
"""

# Short circuit cases
if len(x1.words) == 1:
if x1.is_zero():
return x2
if x1.is_one():
# Optimized case for adding 1
var result = x2
add_inplace_by_1(result)
return result^
if len(x2.words) == 1:
var value = x1.words[0] + x2.words[0]
if value <= 999_999_999:
return BigUInt(List[UInt32](value))
else:
return BigUInt(
List[UInt32](
value % UInt32(1_000_000_000),
value // UInt32(1_000_000_000),
)
)
if len(x2.words) == 1:
if x2.is_zero():
return x1
if x2.is_one():
# Optimized case for adding 1
var result = x1
add_inplace_by_1(result)
return result^
# Zero cases
if x1.is_zero():
return x2

if x2.is_zero():
return x1

# If both numbers are single-word, we can handle them with UInt32
if len(x1.words) == 1 and len(x2.words) == 1:
return BigUInt.from_uint32(x1.words[0] + x2.words[0])

# If both numbers are double-word, we can handle them with UInt64
if len(x1.words) <= 2 and len(x2.words) <= 2:
return BigUInt.from_unsigned_integral_scalar(
x1.to_uint64_with_first_2_words()
+ x2.to_uint64_with_first_2_words()
)

# Normal cases
# The result will have at most one more word than the longer operand
var words = List[UInt32](capacity=max(len(x1.words), len(x2.words)) + 1)

Expand All @@ -118,8 +110,8 @@ fn add(x1: BigUInt, x2: BigUInt) raises -> BigUInt:
sum_of_words += x2.words[ith]

# Compute new word and carry
carry = UInt32(sum_of_words // 1_000_000_000)
words.append(UInt32(sum_of_words % 1_000_000_000))
carry = sum_of_words // UInt32(1_000_000_000)
words.append(sum_of_words % UInt32(1_000_000_000))

ith += 1

Expand All @@ -130,7 +122,7 @@ fn add(x1: BigUInt, x2: BigUInt) raises -> BigUInt:
return BigUInt(words=words^)


fn add_inplace(mut x1: BigUInt, x2: BigUInt) raises -> None:
fn add_inplace(mut x1: BigUInt, x2: BigUInt) -> None:
"""Increments a BigUInt number by another BigUInt number in place.

Args:
Expand All @@ -152,38 +144,30 @@ fn add_inplace(mut x1: BigUInt, x2: BigUInt) raises -> None:
x1.words[0] = value % UInt32(1_000_000_000)
x1.words.append(value // UInt32(1_000_000_000))
return
else:
pass

if len(x2.words) == 1:
if x2.is_zero():
return
if x2.is_one():
if x2.words[0] == 0:
return # No change needed
elif x2.words[0] == 1:
# Optimized case for adding 1
add_inplace_by_1(x1)
return

var carry: UInt32 = 0
var ith: Int = 0
var sum_of_words: UInt32
var x1_len = len(x1.words)

while ith < x1_len or ith < len(x2.words):
sum_of_words = carry

# Add x1's word if available
if ith < len(x1.words):
sum_of_words += x1.words[ith]
# Normal cases
if len(x1.words) < len(x2.words):
x1.words.resize(new_size=len(x2.words), value=UInt32(0))

# Add x2's word if available
if ith < len(x2.words):
sum_of_words += x2.words[ith]
var carry: UInt32 = 0

# Compute new word and carry
carry = UInt32(sum_of_words // 1_000_000_000)
if ith < len(x1.words):
x1.words[ith] = UInt32(sum_of_words % 1_000_000_000)
for i in range(len(x1.words)):
if i < len(x2.words):
x1.words[i] += carry + x2.words[i]
else:
x1.words.append(UInt32(sum_of_words % 1_000_000_000))

ith += 1
x1.words[i] += carry
carry = x1.words[i] // UInt32(1_000_000_000)
x1.words[i] %= UInt32(1_000_000_000)

# Handle final carry if it exists
if carry > 0:
Expand All @@ -192,7 +176,7 @@ fn add_inplace(mut x1: BigUInt, x2: BigUInt) raises -> None:
return


fn add_inplace_by_1(mut x: BigUInt) raises -> None:
fn add_inplace_by_1(mut x: BigUInt) -> None:
"""Increments a BigUInt number by 1."""
var i = 0
while i < len(x.words):
Expand Down
Loading