Skip to content

Commit a195b9e

Browse files
authored
[decimal] Implement arctan() and pi() for BigDecimal (#94)
### `BigDecimal` Enhancements: * Introduced unsafe methods for constructing `BigDecimal` from raw components (`from_raw_components`), with detailed documentation explaining their usage and risks. * Added new mathematical operations to `BigDecimal`, including `arctan`, `power`, and constants like `pi` and `e`. These additions expand the library's capabilities for advanced calculations. [[1]](diffhunk://#diff-be2f9b2702fd15952546e17fe94a64c22902a0e4678bfd9cb925269647923db4R682-R711) [[2]](diffhunk://#diff-be2f9b2702fd15952546e17fe94a64c22902a0e4678bfd9cb925269647923db4R748-R762) * Improved methods for printing `BigDecimal` representations, including `print_representation_as_components`, which provides a detailed breakdown of the number's components. [[1]](diffhunk://#diff-be2f9b2702fd15952546e17fe94a64c22902a0e4678bfd9cb925269647923db4L870-R921) [[2]](diffhunk://#diff-be2f9b2702fd15952546e17fe94a64c22902a0e4678bfd9cb925269647923db4L900-R985) ### Mathematical Constants and Functions: * Added a precomputed constant `PI_1024` for π to 1024 digits of precision and implemented a function `pi(precision)` to calculate π using Machin's formula for higher precision. * Updated the `exp_taylor_series` function in `exponential.mojo` to clarify the precision handling for natural exponential calculations. ### Documentation Enhancements: * Added a detailed quick-start guide in `README.md` to showcase the `decimojo.prelude` module, including examples of basic arithmetic, mathematical functions, and internal representation of numbers. [[1]](diffhunk://#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5R56-R131) [[2]](diffhunk://#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5R175-R176) * Created a new example file `docs/examples_on_bdec.mojo` to demonstrate the usage of `BigDecimal` for arithmetic, mathematical, and internal representation operations.
1 parent 48a9395 commit a195b9e

9 files changed

Lines changed: 742 additions & 37 deletions

File tree

README.md

Lines changed: 68 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,24 +53,82 @@ For the latest development version, clone the [GitHub repository](https://github
5353

5454
## Quick start
5555

56+
You can start using DeciMojo by importing the `decimojo` module. An easy way to do this is to import everything from the `prelude` module, which provides the most commonly used types.
57+
58+
```mojo
59+
from decimojo import *
60+
```
61+
62+
This will import the following types or aliases into your namespace:
63+
64+
- `dm`: An alias for the `decimojo` module.
65+
- `BigInt` (alias `BInt`): An arbitrary-precision signed integer type.
66+
- `BigDecimal` (alias `BDec`): An arbitrary-precision decimal type.
67+
- `Decimal` (alias `Dec`): A 128-bit fixed-precision decimal type.
68+
- `RoundingMode` (alias `RM`): An enumeration for rounding modes.
69+
- `ROUND_DOWN`, `ROUND_HALF_UP`, `ROUND_HALF_EVEN`, `ROUND_UP`: Constants for common rounding modes.
70+
71+
---
72+
5673
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.
5774

5875
```mojo
59-
from decimojo import BDec, RM
76+
from decimojo.prelude import *
6077
6178
6279
fn main() raises:
63-
var PRECISION = 100
6480
var a = BDec("123456789.123456789")
6581
var b = BDec("1234.56789")
66-
print(a.sqrt(precision=PRECISION))
67-
# 11111.11106611111096943055498174930232833813065468909453818857935956641682120364106016272519460988485
68-
print(a.power(b, precision=PRECISION))
69-
# 3.346361102419080234023813540078946868219632448203078657310495672766009862564151996325555496759911131748170844123475135377098326591508239654961E+9989
70-
print(a.log(b, precision=PRECISION))
71-
# 2.617330026656548299907884356415293977170848626010103229392408225981962436022623783231699264341492663671325580092077394824180414301026578169909
82+
83+
# === Basic Arithmetic === #
84+
print(a + b) # 123458023.691346789
85+
print(a - b) # 123455554.555566789
86+
print(a * b) # 152415787654.32099750190521
87+
print(a.true_divide(b, precision=80)) # 100000.0001
88+
89+
# === Mathematical Functions === #
90+
print(a.sqrt(precision=80))
91+
# 11111.111066111110969430554981749302328338130654689094538188579359566416821203641
92+
print(a.cbrt(precision=80))
93+
# 497.93385938415242742001134219007635925452951248903093962731782327785111102410518
94+
print(a.root(b, precision=80))
95+
# 1.0152058862996527138602610522640944903320735973237537866713119992581006582644107
96+
print(a.power(b, precision=80))
97+
# 3.3463611024190802340238135400789468682196324482030786573104956727660098625641520E+9989
98+
print(a.exp(precision=80))
99+
# 1.8612755889649587035842377856492201091251654136588338983610243887893287518637652E+53616602
100+
print(a.log(b, precision=80))
101+
# 2.6173300266565482999078843564152939771708486260101032293924082259819624360226238
102+
print(a.ln(precision=80))
103+
# 18.631401767168018032693933348296537542797015174553735308351756611901741276655161
104+
105+
# === Internal representation of the number === #
106+
(
107+
BDec(
108+
"3.141592653589793238462643383279502884197169399375105820974944"
109+
).power(2, precision=60)
110+
).print_internal_representation()
111+
# Internal Representation Details of BigDecimal
112+
# ----------------------------------------------
113+
# number: 9.8696044010893586188344909998
114+
# 761511353136994072407906264133
115+
# 5
116+
# coefficient: 986960440108935861883449099987
117+
# 615113531369940724079062641335
118+
# negative: False
119+
# scale: 59
120+
# word 0: 62641335
121+
# word 1: 940724079
122+
# word 2: 113531369
123+
# word 3: 99987615
124+
# word 4: 861883449
125+
# word 5: 440108935
126+
# word 6: 986960
127+
# ----------------------------------------------
72128
```
73129

130+
---
131+
74132
Here is a comprehensive quick-start guide showcasing each major function of the `BigInt` type.
75133

76134
```mojo
@@ -114,6 +172,8 @@ fn main() raises:
114172
print(BInt("123456789" * 400) // BInt("987654321" * 200))
115173
```
116174

175+
---
176+
117177
Here is a comprehensive quick-start guide showcasing each major function of the `Decimal` type.
118178

119179
```mojo

docs/examples_on_bdec.mojo

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
from decimojo.prelude import *
2+
3+
4+
fn main() raises:
5+
var a = BDec("123456789.123456789")
6+
var b = BDec("1234.56789")
7+
8+
# === Basic Arithmetic === #
9+
print(a + b) # 123458023.691346789
10+
print(a - b) # 123455554.555566789
11+
print(a * b) # 152415787654.32099750190521
12+
print(a.true_divide(b, precision=80)) # 100000.0001
13+
14+
# === Mathematical Functions === #
15+
print(a.sqrt(precision=80))
16+
# 11111.111066111110969430554981749302328338130654689094538188579359566416821203641
17+
print(a.cbrt(precision=80))
18+
# 497.93385938415242742001134219007635925452951248903093962731782327785111102410518
19+
print(a.root(b, precision=80))
20+
# 1.0152058862996527138602610522640944903320735973237537866713119992581006582644107
21+
print(a.power(b, precision=80))
22+
# 3.3463611024190802340238135400789468682196324482030786573104956727660098625641520E+9989
23+
print(a.exp(precision=80))
24+
# 1.8612755889649587035842377856492201091251654136588338983610243887893287518637652E+53616602
25+
print(a.log(b, precision=80))
26+
# 2.6173300266565482999078843564152939771708486260101032293924082259819624360226238
27+
print(a.ln(precision=80))
28+
# 18.631401767168018032693933348296537542797015174553735308351756611901741276655161
29+
30+
# === Internal representation of the number === #
31+
(
32+
BDec(
33+
"3.141592653589793238462643383279502884197169399375105820974944"
34+
).power(2, precision=60)
35+
).print_internal_representation()
36+
# Internal Representation Details of BigDecimal
37+
# ----------------------------------------------
38+
# number: 9.8696044010893586188344909998
39+
# 761511353136994072407906264133
40+
# 5
41+
# coefficient: 986960440108935861883449099987
42+
# 615113531369940724079062641335
43+
# negative: False
44+
# scale: 59
45+
# word 0: 62641335
46+
# word 1: 940724079
47+
# word 2: 113531369
48+
# word 3: 99987615
49+
# word 4: 861883449
50+
# word 5: 440108935
51+
# word 6: 986960
52+
# ----------------------------------------------

src/decimojo/bigdecimal/bigdecimal.mojo

Lines changed: 106 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -154,17 +154,36 @@ struct BigDecimal(
154154

155155
@staticmethod
156156
fn from_raw_components(
157-
coefficient: BigUInt, scale: Int = 0, sign: Bool = False
157+
owned words: List[UInt32], scale: Int = 0, sign: Bool = False
158158
) -> Self:
159-
"""Creates a BigDecimal from its raw components."""
160-
return Self(coefficient, scale, sign)
159+
"""**UNSAFE** Creates a BigDecimal from its raw components.
160+
The raw components are words, scale, and sign.
161+
162+
Args:
163+
words: The raw words of the coefficient.
164+
scale: The scale of the BigDecimal.
165+
sign: The sign of the BigDecimal.
166+
167+
Returns:
168+
A BigDecimal object constructed from the raw components.
169+
170+
Notes:
171+
172+
This method is unsafe because it does not check the validity of the
173+
words. It is the caller's responsibility to ensure that the words
174+
represent a valid BigUInt.
175+
"""
176+
var coefficient = BigUInt(words=words^)
177+
return Self(coefficient^, scale, sign)
161178

162179
@staticmethod
163180
fn from_raw_components(
164-
coefficient: UInt32, scale: Int = 0, sign: Bool = False
181+
word: UInt32, scale: Int = 0, sign: Bool = False
165182
) -> Self:
166-
"""Creates a BigDecimal from its raw components."""
167-
return Self(BigUInt(List[UInt32](coefficient)), scale, sign)
183+
"""**UNSAFE** Creates a BigDecimal from its raw components.
184+
The raw components are a single word, scale, and sign.
185+
"""
186+
return Self(BigUInt(words=List[UInt32](word)), scale, sign)
168187

169188
@staticmethod
170189
fn from_int(value: Int) -> Self:
@@ -644,6 +663,8 @@ struct BigDecimal(
644663
# Mathematical methods that do not implement a trait (not a dunder)
645664
# ===------------------------------------------------------------------=== #
646665

666+
# === Comparisons === #
667+
647668
@always_inline
648669
fn compare(self, other: Self) raises -> Int8:
649670
"""Compares two BigDecimal numbers.
@@ -658,6 +679,36 @@ struct BigDecimal(
658679
"""
659680
return decimojo.bigdecimal.comparison.compare_absolute(self, other)
660681

682+
# === Extrema === #
683+
684+
@always_inline
685+
fn max(self, other: Self) raises -> Self:
686+
"""Returns the maximum of two BigDecimal numbers."""
687+
return decimojo.bigdecimal.comparison.max(self, other)
688+
689+
@always_inline
690+
fn min(self, other: Self) raises -> Self:
691+
"""Returns the minimum of two BigDecimal numbers."""
692+
return decimojo.bigdecimal.comparison.min(self, other)
693+
694+
# === Constants === #
695+
696+
@always_inline
697+
@staticmethod
698+
fn pi(precision: Int) raises -> Self:
699+
"""Returns the mathematical constant pi to the specified precision."""
700+
return decimojo.bigdecimal.constants.pi(precision=precision)
701+
702+
@always_inline
703+
@staticmethod
704+
fn e(precision: Int) raises -> Self:
705+
"""Returns the mathematical constant e to the specified precision."""
706+
return decimojo.bigdecimal.exponential.exp(
707+
x=BigDecimal(BigUInt.ONE), precision=precision
708+
)
709+
710+
# === Exponentional operations === #
711+
661712
@always_inline
662713
fn exp(self, precision: Int = 28) raises -> Self:
663714
"""Returns the exponential of the BigDecimal number."""
@@ -679,16 +730,6 @@ struct BigDecimal(
679730
"""Returns the base-10 logarithm of the BigDecimal number."""
680731
return decimojo.bigdecimal.exponential.log10(self, precision)
681732

682-
@always_inline
683-
fn max(self, other: Self) raises -> Self:
684-
"""Returns the maximum of two BigDecimal numbers."""
685-
return decimojo.bigdecimal.comparison.max(self, other)
686-
687-
@always_inline
688-
fn min(self, other: Self) raises -> Self:
689-
"""Returns the minimum of two BigDecimal numbers."""
690-
return decimojo.bigdecimal.comparison.min(self, other)
691-
692733
@always_inline
693734
fn root(self, root: Self, precision: Int = 28) raises -> Self:
694735
"""Returns the root of the BigDecimal number."""
@@ -704,6 +745,21 @@ struct BigDecimal(
704745
"""Returns the cube root of the BigDecimal number."""
705746
return decimojo.bigdecimal.exponential.cbrt(self, precision)
706747

748+
@always_inline
749+
fn power(self, exponent: Self, precision: Int) raises -> Self:
750+
"""Returns the result of exponentiation with the given precision.
751+
See `exponential.power()` for more information.
752+
"""
753+
return decimojo.bigdecimal.exponential.power(self, exponent, precision)
754+
755+
# === Trigonometric operations === #
756+
@always_inline
757+
fn arctan(self, precision: Int = 28) raises -> Self:
758+
"""Returns the arctangent of the BigDecimal number."""
759+
return decimojo.bigdecimal.trigonometric.arctan(self, precision)
760+
761+
# === Arithmetic operations === #
762+
707763
@always_inline
708764
fn true_divide(self, other: Self, precision: Int) raises -> Self:
709765
"""Returns the result of true division of two BigDecimal numbers.
@@ -731,12 +787,7 @@ struct BigDecimal(
731787
"""
732788
return decimojo.bigdecimal.arithmetics.truncate_divide(self, other)
733789

734-
@always_inline
735-
fn power(self, exponent: Self, precision: Int) raises -> Self:
736-
"""Returns the result of exponentiation with the given precision.
737-
See `exponential.power()` for more information.
738-
"""
739-
return decimojo.bigdecimal.exponential.power(self, exponent, precision)
790+
# === Rounding operations === #
740791

741792
@always_inline
742793
fn round(
@@ -867,7 +918,7 @@ struct BigDecimal(
867918
)
868919

869920
@always_inline
870-
fn print_internal_representation(self) raises:
921+
fn print_internal_representation(self):
871922
"""Prints the internal representation of the BigDecimal."""
872923
var line_width = 30
873924
var string_of_number = self.to_string(line_width=line_width).split("\n")
@@ -897,14 +948,41 @@ struct BigDecimal(
897948
else:
898949
ndigits = 3
899950
print(
900-
String("word {}:{}{}")
901-
.format(
902-
i, " " * (10 - ndigits), String(self.coefficient.words[i])
903-
)
904-
.rjust(9, fillchar="0")
951+
String(
952+
"word ",
953+
i,
954+
":",
955+
" " * (10 - ndigits),
956+
self.coefficient.words[i],
957+
).rjust(9, fillchar="0")
905958
)
906959
print("----------------------------------------------")
907960

961+
@always_inline
962+
fn print_representation_as_components(self):
963+
"""Prints the representation of the BigDecimal as components."""
964+
print(
965+
(
966+
"BigDecimal(\n coefficient=BigUInt(\n "
967+
" words=List[UInt32](\n "
968+
),
969+
end="",
970+
)
971+
ref words = self.coefficient.words
972+
for i in range(len(words)):
973+
if i != len(words) - 1:
974+
print(words[i], end=",\n ")
975+
else:
976+
print(words[i], end=",\n ),\n ),\n")
977+
print(
978+
" scale=",
979+
self.scale,
980+
",\n sign=",
981+
self.sign,
982+
")",
983+
sep="",
984+
)
985+
908986
@always_inline
909987
fn is_integer(self) -> Bool:
910988
"""Returns True if this number represents an integer value."""

0 commit comments

Comments
 (0)