Skip to content

Commit 51c9832

Browse files
authored
[integer][decimal][docs] Improve type conversions + optimize iadd for BigInt + Update documentation (#90)
This pull request introduces multiple changes across the `DeciMojo` library to enhance functionality, improve performance, and update documentation. Key updates include support for implicit type conversion between built-in integral types and arbitrary-precision types, optimizations for `BigInt` operations, and bug fixes for `BigDecimal`. Additionally, version updates and documentation refinements are included.
1 parent 81b523f commit 51c9832

File tree

9 files changed

+195
-65
lines changed

9 files changed

+195
-65
lines changed

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ DeciMojo provides an arbitrary-precision decimal and integer mathematics library
2020

2121
The core types are:
2222

23-
- 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.
24-
- An arbitrary-precision decimal implementation `BigDecimal` allowing for calculations with unlimited digits and decimal places[^arbitrary].
2523
- 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.
24+
- An arbitrary-precision decimal implementation `BigDecimal` allowing for calculations with unlimited digits and decimal places[^arbitrary].
25+
- 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.
2626

2727
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.
2828

@@ -39,7 +39,7 @@ DeciMojo is available in the [modular-community](https://repo.prefix.dev/modular
3939

4040
From the `pixi` CLI, simply run ```pixi add decimojo```. This fetches the latest version and makes it immediately available for import.
4141

42-
For projects with a `mojoproject.toml`file, add the dependency ```decimojo = "==0.4.0"```. Then run `pixi install` to download and install the package.
42+
For projects with a `mojoproject.toml`file, add the dependency ```decimojo = "==0.4.1"```. Then run `pixi install` to download and install the package.
4343

4444
For the latest development version, clone the [GitHub repository](https://github.com/forfudan/decimojo) and build the package locally.
4545

@@ -49,11 +49,11 @@ For the latest development version, clone the [GitHub repository](https://github
4949
| v0.2.0 | ==25.2 | magic |
5050
| v0.3.0 | ==25.2 | magic |
5151
| v0.3.1 | >=25.2, <25.4 | pixi |
52-
| v0.4.0 | ==25.4 | pixi |
52+
| v0.4.x | ==25.4 | pixi |
5353

5454
## Quick start
5555

56-
Here are some examples showcasing the arbitrary-precision feature of the `BigDecimal` type.
56+
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.
5757

5858
```mojo
5959
from decimojo import BDec, RM
@@ -218,7 +218,7 @@ If you find DeciMojo useful for your research, consider listing it in your citat
218218
year = {2025},
219219
title = {An arbitrary-precision decimal and integer mathematics library for Mojo},
220220
url = {https://github.com/forfudan/decimojo},
221-
version = {0.4.0},
221+
version = {0.4.1},
222222
note = {Computer Software}
223223
}
224224
```

docs/changelog.md

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,85 @@
11
# DeciMojo changelog
22

3-
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)**.
3+
This is a list of RELEASED changes for the DeciMojo Package.
4+
5+
## 01/07/2025 (v0.4.1)
6+
7+
Version 0.4.1 of DeciMojo introduces implicit type conversion between built-in integral types and arbitrary-precision types.
8+
9+
### ⭐️ New
10+
11+
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).
12+
13+
For example, you can now do the following:
14+
15+
```mojo
16+
from decimojo.prelude import *
17+
18+
fn main() raises:
19+
var a = BInt(Int256(-1234567890))
20+
var b = BigUInt(31415926)
21+
var c = BDec("3.14159265358979323")
22+
23+
print("a =", a)
24+
print("b =", b)
25+
print("c =", c)
26+
27+
print(a * b) # Merged to BInt
28+
print(a + c) # Merged to BDec
29+
print(b + c) # Merged to BDec
30+
print(a * Int(-128)) # Merged to BInt
31+
print(b * UInt(8)) # Merged to BUInt
32+
print(c * Int256(987654321123456789)) # Merged to BDec
33+
34+
var lst = [a, b, c, UInt8(255), Int64(22222), UInt256(1234567890)]
35+
# The list is of the type `List[BigDecimal]`
36+
for i in lst:
37+
print(i, end=", ")
38+
```
39+
40+
Running the code will give your the following results:
41+
42+
```console
43+
a = -1234567890
44+
b = 31415926
45+
c = 3.14159265358979323
46+
-38785093474216140
47+
-1234567886.85840734641020677
48+
31415929.14159265358979323
49+
158024689920
50+
251327408
51+
3102807559527666386.46423202534973847
52+
-1234567890, 31415926, 3.14159265358979323, 255, 22222, 1234567890,
53+
```
54+
55+
### 🦋 Changed
56+
57+
Optimize the case when you increase the value of a `BigInt` object in-place by 1, *i.e.*, `i += 1`. This allows you to iterate faster (PR #89). For example, we can compute the time taken to iterate from `0` to `1_000_000` using `BigInt` and compare it with the built-in `Int` type:
58+
59+
```mojo
60+
from decimojo.prelude import *
61+
62+
fn main() raises:
63+
i = BigInt(0)
64+
end = BigInt(1_000_000)
65+
while i < end:
66+
print(i)
67+
i += 1
68+
```
69+
70+
| scenario | Time taken |
71+
| --------------- | ---------- |
72+
| v0.4.0 `BigInt` | 1.102s |
73+
| v0.4.1 `BigInt` | 0.912s |
74+
| Built-in `Int` | 0.893s |
75+
76+
### 🛠️ Fixed
77+
78+
Fix a bug in `BigDecimal` where it cannot create a correct value from a integral scalar, e.g., `BDec(UInt16(0))` returns an unitialized `BigDecimal` object (PR #89).
79+
80+
### 📚 Documentation and testing
81+
82+
Update the `tests` module and refactor the test files for `BigUInt` (PR #88).
483

584
## 25/06/2025 (v0.4.0)
685

docs/changelog_unreleased.md

Lines changed: 0 additions & 3 deletions
This file was deleted.

docs/readme_zht.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ DeciMojo 可在 [modular-community](https://repo.prefix.dev/modular-community)
2020

2121
`pixi` CLI,只需運行 ```pixi add decimojo```。這會獲取最新版本並使其立即可用於導入。
2222

23-
對於帶有 `mojoproject.toml` 文件的項目,添加依賴 ```decimojo = "==0.4.0"```。然後運行 `pixi install` 來下載並安裝包。
23+
對於帶有 `mojoproject.toml` 文件的項目,添加依賴 ```decimojo = "==0.4.1"```。然後運行 `pixi install` 來下載並安裝包。
2424

2525
如需最新的開發版本,請克隆 [GitHub 倉庫](https://github.com/forfudan/decimojo) 並在本地構建包。
2626

@@ -30,7 +30,7 @@ DeciMojo 可在 [modular-community](https://repo.prefix.dev/modular-community)
3030
| v0.2.0 | >=25.2 | magic |
3131
| v0.3.0 | >=25.2 | magic |
3232
| v0.3.1 | >=25.2, <25.4 | pixi |
33-
| v0.4.0 | ==25.4 | pixi |
33+
| v0.4.x | ==25.4 | pixi |
3434

3535
## 快速入門
3636

@@ -218,7 +218,7 @@ DeciMojo 相較於 Python 的 `decimal` 模塊提供了卓越的性能,同時
218218
year = {2025},
219219
title = {DeciMojo: A fixed-point decimal arithmetic library in Mojo},
220220
url = {https://github.com/forfudan/decimojo},
221-
version = {0.4.0},
221+
version = {0.4.1},
222222
note = {Computer Software}
223223
}
224224
```

pixi.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ license = "Apache-2.0"
77
name = "decimojo"
88
platforms = ["osx-arm64", "linux-64"]
99
readme = "README.md"
10-
version = "0.4.0"
10+
version = "0.4.1"
1111

1212
[dependencies]
1313
max = "==25.4"

src/decimojo/bigdecimal/bigdecimal.mojo

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,13 @@ struct BigDecimal(
114114
"""
115115
self = Self.from_int(value)
116116

117+
@implicit
118+
fn __init__(out self, value: UInt):
119+
"""Constructs a BigDecimal from an `UInt` object.
120+
See `from_uint()` for more information.
121+
"""
122+
self = Self.from_uint(value)
123+
117124
@implicit
118125
fn __init__(out self, value: Scalar):
119126
"""Constructs a BigDecimal from an integral scalar.
@@ -179,6 +186,11 @@ struct BigDecimal(
179186

180187
return Self(coefficient=BigUInt(words^), scale=0, sign=sign)
181188

189+
@staticmethod
190+
fn from_uint(value: Int) -> Self:
191+
"""Creates a BigDecimal from an unsigned integer."""
192+
return Self(coefficient=BigUInt.from_uint(value), scale=0, sign=False)
193+
182194
@staticmethod
183195
fn from_integral_scalar[dtype: DType, //](value: SIMD[dtype, 1]) -> Self:
184196
"""Initializes a BigDecimal from an integral scalar.

src/decimojo/bigint/arithmetics.mojo

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,22 @@ fn add(x1: BigInt, x2: BigInt) raises -> BigInt:
5252
return BigInt(magnitude^, sign=x1.sign)
5353

5454

55+
fn add_inplace(mut x1: BigInt, x2: BigInt) raises -> None:
56+
"""Increments a BigInt number by another BigInt number in place.
57+
58+
Args:
59+
x1: The first BigInt operand.
60+
x2: The second BigInt operand.
61+
"""
62+
63+
# If signs are different, delegate to `subtract`
64+
if x1.sign != x2.sign:
65+
x1 = subtract(x1, -x2)
66+
67+
# Same sign: add magnitudes in place
68+
x1.magnitude += x2.magnitude
69+
70+
5571
fn subtract(x1: BigInt, x2: BigInt) raises -> BigInt:
5672
"""Returns the difference of two numbers.
5773

src/decimojo/bigint/bigint.mojo

Lines changed: 18 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,13 @@ struct BigInt(Absable, IntableRaising, Representable, Stringable, Writable):
149149
"""
150150
self = Self.from_int(value)
151151

152+
@implicit
153+
fn __init__(out self, value: UInt):
154+
"""Initializes a BigInt from an `UInt` object.
155+
See `from_uint()` for more information.
156+
"""
157+
self = Self.from_uint(value)
158+
152159
@implicit
153160
fn __init__(out self, value: Scalar):
154161
"""Constructs a BigInt from an integral scalar.
@@ -164,7 +171,6 @@ struct BigInt(Absable, IntableRaising, Representable, Stringable, Writable):
164171
#
165172
# from_words(*words: UInt32, sign: Bool) -> Self
166173
# from_int(value: Int) -> Self
167-
# from_uint128(value: UInt128, sign: Bool = False) -> Self
168174
# from_string(value: String) -> Self
169175
# ===------------------------------------------------------------------=== #
170176

@@ -251,6 +257,11 @@ struct BigInt(Absable, IntableRaising, Representable, Stringable, Writable):
251257

252258
return Self(BigUInt(words^), sign)
253259

260+
@staticmethod
261+
fn from_uint(value: UInt) -> Self:
262+
"""Creates a BigInt from an unsignd integer."""
263+
return Self(magnitude=BigUInt.from_uint(value), sign=False)
264+
254265
@staticmethod
255266
fn from_integral_scalar[dtype: DType, //](value: SIMD[dtype, 1]) -> Self:
256267
"""Initializes a BigInt from an integral scalar.
@@ -448,54 +459,26 @@ struct BigInt(Absable, IntableRaising, Representable, Stringable, Writable):
448459
fn __add__(self, other: Self) raises -> Self:
449460
return decimojo.bigint.arithmetics.add(self, other)
450461

451-
@always_inline
452-
fn __add__(self, other: Int) raises -> Self:
453-
return decimojo.bigint.arithmetics.add(self, Self.from_int(other))
454-
455462
@always_inline
456463
fn __sub__(self, other: Self) raises -> Self:
457464
return decimojo.bigint.arithmetics.subtract(self, other)
458465

459-
@always_inline
460-
fn __sub__(self, other: Int) raises -> Self:
461-
return decimojo.bigint.arithmetics.subtract(self, Self.from_int(other))
462-
463466
@always_inline
464467
fn __mul__(self, other: Self) raises -> Self:
465468
return decimojo.bigint.arithmetics.multiply(self, other)
466469

467-
@always_inline
468-
fn __mul__(self, other: Int) raises -> Self:
469-
return decimojo.bigint.arithmetics.multiply(self, Self.from_int(other))
470-
471470
@always_inline
472471
fn __floordiv__(self, other: Self) raises -> Self:
473472
return decimojo.bigint.arithmetics.floor_divide(self, other)
474473

475-
@always_inline
476-
fn __floordiv__(self, other: Int) raises -> Self:
477-
return decimojo.bigint.arithmetics.floor_divide(
478-
self, Self.from_int(other)
479-
)
480-
481474
@always_inline
482475
fn __mod__(self, other: Self) raises -> Self:
483476
return decimojo.bigint.arithmetics.floor_modulo(self, other)
484477

485-
@always_inline
486-
fn __mod__(self, other: Int) raises -> Self:
487-
return decimojo.bigint.arithmetics.floor_modulo(
488-
self, Self.from_int(other)
489-
)
490-
491478
@always_inline
492479
fn __pow__(self, exponent: Self) raises -> Self:
493480
return self.power(exponent)
494481

495-
@always_inline
496-
fn __pow__(self, exponent: Int) raises -> Self:
497-
return self.power(exponent)
498-
499482
# ===------------------------------------------------------------------=== #
500483
# Basic binary right-side arithmetic operation dunders
501484
# These methods are called to implement the binary arithmetic operations
@@ -535,48 +518,32 @@ struct BigInt(Absable, IntableRaising, Representable, Stringable, Writable):
535518

536519
@always_inline
537520
fn __iadd__(mut self, other: Self) raises:
538-
self = decimojo.bigint.arithmetics.add(self, other)
521+
decimojo.bigint.arithmetics.add_inplace(self, other)
539522

540523
@always_inline
541524
fn __iadd__(mut self, other: Int) raises:
542-
self = decimojo.bigint.arithmetics.add(self, Self.from_int(other))
525+
# Optimize the case `i += 1`
526+
if other == 1:
527+
self.magnitude.add_inplace_by_1()
528+
else:
529+
decimojo.bigint.arithmetics.add_inplace(self, other)
543530

544531
@always_inline
545532
fn __isub__(mut self, other: Self) raises:
546533
self = decimojo.bigint.arithmetics.subtract(self, other)
547534

548-
@always_inline
549-
fn __isub__(mut self, other: Int) raises:
550-
self = decimojo.bigint.arithmetics.subtract(self, Self.from_int(other))
551-
552535
@always_inline
553536
fn __imul__(mut self, other: Self) raises:
554537
self = decimojo.bigint.arithmetics.multiply(self, other)
555538

556-
@always_inline
557-
fn __imul__(mut self, other: Int) raises:
558-
self = decimojo.bigint.arithmetics.multiply(self, Self.from_int(other))
559-
560539
@always_inline
561540
fn __ifloordiv__(mut self, other: Self) raises:
562541
self = decimojo.bigint.arithmetics.floor_divide(self, other)
563542

564-
@always_inline
565-
fn __ifloordiv__(mut self, other: Int) raises:
566-
self = decimojo.bigint.arithmetics.floor_divide(
567-
self, Self.from_int(other)
568-
)
569-
570543
@always_inline
571544
fn __imod__(mut self, other: Self) raises:
572545
self = decimojo.bigint.arithmetics.floor_modulo(self, other)
573546

574-
@always_inline
575-
fn __imod__(mut self, other: Int) raises:
576-
self = decimojo.bigint.arithmetics.floor_modulo(
577-
self, Self.from_int(other)
578-
)
579-
580547
# ===------------------------------------------------------------------=== #
581548
# Basic binary comparison operation dunders
582549
# __gt__, __ge__, __lt__, __le__, __eq__, __ne__

0 commit comments

Comments
 (0)