Skip to content

Commit 38707b4

Browse files
authored
[integer] Refine the BigUInt division operation (#55)
This pull request includes several changes to the `README.md` file and updates to the `bench_biguint_truncate_divide.mojo` and `arithmetics.mojo` files to improve documentation, update benchmarks, and refine arithmetic operations. ### Code improvement: * Improved the algorithm in `divide` operation of `BigUInt`, making it correct for cases against Python's Int and making it faster than Python's Int for numbers up to 1000 digits. For numbers with more than 1000 digits, the limit performance will be around x0.25 compared to Python. ### Codebase Simplification: * Renamed `truncate_divide` function to `floor_divide` in `arithmetics.mojo` to better reflect its operation. * Added comments and organized arithmetic operations in `arithmetics.mojo` for better readability. * Improved handling of division operations for small word lengths in `arithmetics.mojo`. ### Documentation Updates: * Enhanced the library description to include integer mathematics alongside decimal mathematics in `README.md`. * Updated references to `BigInt` and `BigDecimal` for clarity and precision in `README.md`. ### Benchmark Enhancements: * Added new benchmark cases for division of large numbers in `bench_biguint_truncate_divide.mojo`. * Simplified division operation in benchmarks by replacing `truncate_divide` with `//` operator in `bench_biguint_truncate_divide.mojo`.
1 parent 75d2b39 commit 38707b4

File tree

6 files changed

+593
-169
lines changed

6 files changed

+593
-169
lines changed

README.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
# DeciMojo
22

3-
A comprehensive decimal mathematics library for [Mojo](https://www.modular.com/mojo).
3+
A comprehensive decimal and integer mathematics library for [Mojo](https://www.modular.com/mojo).
44

55
**[中文·漢字»](./docs/readme_zht.md)** | **[Repository on GitHub»](https://github.com/forfudan/decimojo)**
66

77
## Overview
88

9-
DeciMojo provides a comprehensive decimal mathematics library for Mojo, delivering exact calculations for financial modeling, scientific computing, and applications where floating-point approximation errors are unacceptable. Beyond basic arithmetic, the library includes advanced mathematical functions with guaranteed precision.
9+
DeciMojo provides a comprehensive decimal and integer mathematics library for Mojo, delivering exact calculations for financial modeling, scientific computing, and applications where floating-point approximation errors are unacceptable. Beyond basic arithmetic, the library includes advanced mathematical functions with guaranteed precision.
1010

1111
The core types are:
1212

13-
- A 128-bit fixed-point decimal implementation (`Decimal`) supporting up to 29 significant digits with a maximum of 28 decimal places[^fixed_precision]. It features a complete set of mathematical functions including logarithms, exponentiation, roots, and trigonometric operations.
14-
- A fully implemented arbitrary-precision integer type (`BigInt`) supporting unlimited digits. It features base-10^9 internal representation and basic arithmetic operations.
13+
- 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, and trigonometric operations.
14+
- An arbitrary-precision integer type (`BigInt`)[^integer] supporting unlimited digits. It features base-10^9 internal representation and basic arithmetic operations.
1515

16-
The library is expanding to include `BigDecimal` types that support arbitrary precision, allowing for calculations with unlimited digits and decimal places. These extensions are currently under active development.
16+
The library is expanding to include `BigDecimal` types that support arbitrary precision[^arbitrary], allowing for calculations with unlimited digits and decimal places. These extensions are currently under active development.
1717

1818
## Installation
1919

@@ -125,6 +125,7 @@ fn main() raises:
125125
print(-a) # Negation: -12345678901234567890
126126
print(abs(BigInt("-12345678901234567890"))) # Absolute value: 12345678901234567890
127127
print(a.is_negative()) # Check if negative: False
128+
```
128129

129130
## Objective
130131

@@ -179,7 +180,7 @@ Regular benchmarks against Python's `decimal` module are available in the `bench
179180

180181
### Future Extensions 🚀 (PLANNED)
181182

182-
- **BigDecimal**: 🔄 **IN PROGRESS** - Arbitrary-precision decimal type with configurable precision[^arbitrary_precision].
183+
- **BigDecimal**: 🔄 **IN PROGRESS** - Arbitrary-precision decimal type with configurable precision[^arbitrary].
183184
- **BigComplex**: 📝 **PLANNED** - Arbitrary-precision complex number type built on BigDecimal.
184185

185186
## Tests and benches
@@ -208,6 +209,6 @@ If you find DeciMojo useful for your research, consider listing it in your citat
208209

209210
This repository and its contributions are licensed under the Apache License v2.0.
210211

211-
[^fixed_precision]: 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.
212-
[^integer]: Integers are a special case of decimal numbers (with zero fractional part). Our BigInt implementation serves both as a standalone arbitrary-precision integer type and as the foundation for our upcoming BigDecimal implementation.
213-
[^arbitrary_precision]: Similar to `decimal` and `mpmath` in Python, `java.math.BigDecimal` in Java, etc.
212+
[^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.
213+
[^integer]: The BigInt implementation uses a base-10^9 representation for efficient storage and calculations, supporting operations on integers with unlimited precision. It serves both as a standalone arbitrary-precision integer type and as the foundation for our upcoming BigDecimal implementation.
214+
[^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.

benches/biguint/bench_biguint_truncate_divide.mojo

Lines changed: 108 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ fn run_benchmark_truncate_divide(
8181

8282
# Execute the operations once to verify correctness
8383
try:
84-
var mojo_result = mojo_dividend.truncate_divide(mojo_divisor)
84+
var mojo_result = mojo_dividend // mojo_divisor
8585
var py_result = py_dividend // py_divisor
8686

8787
# Display results for verification
@@ -91,7 +91,7 @@ fn run_benchmark_truncate_divide(
9191
# Benchmark Mojo implementation
9292
var t0 = perf_counter_ns()
9393
for _ in range(iterations):
94-
_ = mojo_dividend.truncate_divide(mojo_divisor)
94+
_ = mojo_dividend // mojo_divisor
9595
var mojo_time = (perf_counter_ns() - t0) / iterations
9696
if mojo_time == 0:
9797
mojo_time = 1 # Prevent division by zero
@@ -380,6 +380,112 @@ fn main() raises:
380380
speedup_factors,
381381
)
382382

383+
# Case 23: Division of large numbers
384+
run_benchmark_truncate_divide(
385+
"Division of large numbers (270 digits vs 135 digits)",
386+
"123456789" * 30,
387+
"987654321" * 15,
388+
iterations,
389+
log_file,
390+
speedup_factors,
391+
)
392+
393+
# Case 24: Division of very large numbers
394+
run_benchmark_truncate_divide(
395+
"Division of very large numbers (2250 digits vs 900 digits)",
396+
"123456789" * 250,
397+
"987654321" * 100,
398+
iterations,
399+
log_file,
400+
speedup_factors,
401+
)
402+
403+
# Case 25: Division of very, very large numbers
404+
# x1 is more than 400 words long (>= 10^3600)
405+
# x2 is more than 200 words long (>= 10^1800)
406+
run_benchmark_truncate_divide(
407+
"Division of very, very large numbers (3600 digits vs 1800 digits)",
408+
"123456789" * 400,
409+
"987654321" * 200,
410+
iterations,
411+
log_file,
412+
speedup_factors,
413+
)
414+
415+
# Case 26: Division of large numbers
416+
run_benchmark_truncate_divide(
417+
"Division of very large numbers (256 digits vs 128 digits)",
418+
"1234567890193287491287508917213097405916874098123705160923812345678901932874912875089172130974059168740981237051609238749875089170984701759832708497029875019837409871085709813749870897510749875089170984701759832708497029875019837409871085709813749870897510",
419+
"68740981237051609238123456789019328749128750891721309740591687409812370516092387498750879548759387959978279541709847017598327084",
420+
iterations,
421+
log_file,
422+
speedup_factors,
423+
)
424+
425+
# Case 27: Division of numbers with around 200 and 100 digits
426+
run_benchmark_truncate_divide(
427+
"Division of large numbers (200 digits vs 100 digits)",
428+
(
429+
"314159265358979323846264338327950288419716939937510"
430+
"582097494459230781640628620899862803482534211706798214808651"
431+
"328230664709384460955058223172535940812848111745028410270193"
432+
"852110555964462294895493038196"
433+
),
434+
(
435+
"271828182845904523536028747135266249775724709369995"
436+
"95749669676277240766303535475945713821785251664274"
437+
),
438+
iterations,
439+
log_file,
440+
speedup_factors,
441+
)
442+
443+
# Case 28: Division of numbers with around 200 and 100 digits
444+
run_benchmark_truncate_divide(
445+
"Division of large numbers (200 digits vs 100 digits)",
446+
(
447+
"314159265358979323846264338327950288419716939937510"
448+
"582097494459230781640628620899862803482534211706798214808651"
449+
"328230664709384460955058223172535940812848111745028410270193"
450+
"852110555964462294895493038196"
451+
),
452+
(
453+
"141421356237309504880168872420969807856967187537694"
454+
"80731766797379907324784621070388503875343276400719"
455+
),
456+
iterations,
457+
log_file,
458+
speedup_factors,
459+
)
460+
461+
# Case 29: Division of numbers with around 150 and 50 digits
462+
run_benchmark_truncate_divide(
463+
"Division of large numbers (150 digits vs 50 digits)",
464+
(
465+
"314159265358979323846264338327950288419716939937510"
466+
"582097494459230781640628620899862803482534211706798214808651"
467+
"3282306647093844609550582231725359408128"
468+
),
469+
"141421356237309504880168872420969807856967187537694",
470+
iterations,
471+
log_file,
472+
speedup_factors,
473+
)
474+
475+
# Case 30: Division of numbers with around 150 and 50 digits
476+
run_benchmark_truncate_divide(
477+
"Division of large numbers (150 digits vs 50 digits)",
478+
(
479+
"316227766016824890583648059893174009579947593530458"
480+
"382628078540989121552735792899961040720792717368862335475063"
481+
"5167610057579407944886251958020310186466"
482+
),
483+
"141421356237309504880168872420969807856967187537694",
484+
iterations,
485+
log_file,
486+
speedup_factors,
487+
)
488+
383489
# Calculate average speedup factor (ignoring any cases that might have failed)
384490
if len(speedup_factors) > 0:
385491
var sum_speedup: Float64 = 0.0

docs/todo.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
This is a to-do list for Yuhao's personal use.
44

55
- [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.
6-
- [ ] Implement different methods for augmented arithmetic assignments to improve memeory-efficiency and performance.
6+
- [ ] Implement different methods for augmented arithmetic assignments to improve memeory-efficiency and performance.

0 commit comments

Comments
 (0)