Skip to content

Commit 053934d

Browse files
authored
[decimal] Implement sin() and cos() for BigDecimal (#96)
This pull request introduces significant enhancements to the `BigDecimal` module, focusing on trigonometric functionality, precision handling, and modular arithmetic. It also updates documentation and test cases to support these changes. The most important changes include the addition of `sin` and `cos` functions, improvements to rounding precision methods, and modular arithmetic support. ### Trigonometric Functionality Enhancements: * Added `sin` and `cos` functions to `struct BigDecimal`, with range reduction and Taylor series implementations for accurate calculations. These functions handle edge cases for π-related values and ensure high precision. (`src/decimojo/bigdecimal/bigdecimal.mojo` [src/decimojo/bigdecimal/bigdecimal.mojoR765-R774](diffhunk://#diff-be2f9b2702fd15952546e17fe94a64c22902a0e4678bfd9cb925269647923db4R765-R774), `src/decimojo/bigdecimal/trigonometric.mojo` F4fb1110L30R321) * Updated `arctan` function to use `round_to_precision` for consistent precision handling and added intermediate result handling for better readability. (`src/decimojo/bigdecimal/trigonometric.mojo` F4fb1110L56R355) ### Precision Handling Improvements: * Replaced `round` with `round_to_precision` across multiple functions, adding options to remove extra digits due to rounding and prevent trailing zeros. (`src/decimojo/bigdecimal/constants.mojo` F7b2c265L166R322, `src/decimojo/bigdecimal/rounding.mojo` [src/decimojo/bigdecimal/rounding.mojoL102-R104](diffhunk://#diff-2d08304d5c7273c47523b98f68714e1c980a59b7c4f0a9bf444ffca9435afa17L102-R104)) * Added detailed notes in the `round_to_precision` method to clarify the behavior of precision-related parameters. (`src/decimojo/bigdecimal/bigdecimal.mojo` [src/decimojo/bigdecimal/bigdecimal.mojoR848-R854](diffhunk://#diff-be2f9b2702fd15952546e17fe94a64c22902a0e4678bfd9cb925269647923db4R848-R854)) ### Modular Arithmetic Support: * Introduced the `__mod__` method in `struct BigDecimal` to support modulo operations, leveraging `truncate_modulo`. (`src/decimojo/bigdecimal/bigdecimal.mojo` [src/decimojo/bigdecimal/bigdecimal.mojoR526-R534](diffhunk://#diff-be2f9b2702fd15952546e17fe94a64c22902a0e4678bfd9cb925269647923db4R526-R534)) ### Documentation Updates: * Expanded documentation in `docs/internal_notes.md` to include examples and precision comparisons for `sin` functionality, highlighting discrepancies between Decimojo, WolframAlpha, and `mpmath`. (`docs/internal_notes.md` [docs/internal_notes.mdL5-R16](diffhunk://#diff-6da6b2e1a170d4269cd7863de3fd61fd4098c67fc788deb68bbe6f0f18b98d24L5-R16)) ### Test Case Enhancements: * Refactored trigonometric tests to include `sin` and `arctan` functions, enabling modular test execution through a reusable `run_test` function. (`tests/bigdecimal/test_bigdecimal_trigonometric.mojo` [tests/bigdecimal/test_bigdecimal_trigonometric.mojoL14-R45](diffhunk://#diff-e8f272193f827af6eee86da91ce756ed1739f4e178b6a5c19c1d10165d7fd17aL14-R45))
1 parent b9c526e commit 053934d

9 files changed

Lines changed: 542 additions & 46 deletions

File tree

README.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ fn main() raises:
8686
print(a * b) # 152415787654.32099750190521
8787
print(a.true_divide(b, precision=80)) # 100000.0001
8888
89-
# === Mathematical Functions === #
89+
# === Exponential Functions === #
9090
print(a.sqrt(precision=80))
9191
# 11111.111066111110969430554981749302328338130654689094538188579359566416821203641
9292
print(a.cbrt(precision=80))
@@ -102,6 +102,24 @@ fn main() raises:
102102
print(a.ln(precision=80))
103103
# 18.631401767168018032693933348296537542797015174553735308351756611901741276655161
104104
105+
# === Trigonometric Functions === #
106+
print(a.sin(precision=200))
107+
# 0.99985093087193092464780008002600992896256609588456
108+
# 91036188395766389946401881352599352354527727927177
109+
# 79589259132243649550891532070326452232864052771477
110+
# 31418817041042336608522984511928095747763538486886
111+
print(b.cos(precision=1000))
112+
# -0.9969577603867772005841841569997528013669868536239849713029893885930748434064450375775817720425329394
113+
# 9756020177557431933434791661179643984869397089102223199519409695771607230176923201147218218258755323
114+
# 7563476302904118661729889931783126826250691820526961290122532541861737355873869924820906724540889765
115+
# 5940445990824482174517106016800118438405307801022739336016834311018727787337447844118359555063575166
116+
# 5092352912854884589824773945355279792977596081915868398143592738704592059567683083454055626123436523
117+
# 6998108941189617922049864138929932713499431655377552668020889456390832876383147018828166124313166286
118+
# 6004871998201597316078894718748251490628361253685772937806895692619597915005978762245497623003811386
119+
# 0913693867838452088431084666963414694032898497700907783878500297536425463212578556546527017688874265
120+
# 0785862902484462361413598747384083001036443681873292719322642381945064144026145428927304407689433744
121+
# 5821277763016669042385158254006302666602333649775547203560187716156055524418512492782302125286330865
122+
105123
# === Internal representation of the number === #
106124
(
107125
BDec(

docs/examples_on_bdec.mojo

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ fn main() raises:
1111
print(a * b) # 152415787654.32099750190521
1212
print(a.true_divide(b, precision=80)) # 100000.0001
1313

14-
# === Mathematical Functions === #
14+
# === Exponential Functions === #
1515
print(a.sqrt(precision=80))
1616
# 11111.111066111110969430554981749302328338130654689094538188579359566416821203641
1717
print(a.cbrt(precision=80))
@@ -27,6 +27,24 @@ fn main() raises:
2727
print(a.ln(precision=80))
2828
# 18.631401767168018032693933348296537542797015174553735308351756611901741276655161
2929

30+
# === Trigonometric Functions === #
31+
print(a.sin(precision=200))
32+
# 0.99985093087193092464780008002600992896256609588456
33+
# 91036188395766389946401881352599352354527727927177
34+
# 79589259132243649550891532070326452232864052771477
35+
# 31418817041042336608522984511928095747763538486886
36+
print(b.cos(precision=1000))
37+
# -0.9969577603867772005841841569997528013669868536239849713029893885930748434064450375775817720425329394
38+
# 9756020177557431933434791661179643984869397089102223199519409695771607230176923201147218218258755323
39+
# 7563476302904118661729889931783126826250691820526961290122532541861737355873869924820906724540889765
40+
# 5940445990824482174517106016800118438405307801022739336016834311018727787337447844118359555063575166
41+
# 5092352912854884589824773945355279792977596081915868398143592738704592059567683083454055626123436523
42+
# 6998108941189617922049864138929932713499431655377552668020889456390832876383147018828166124313166286
43+
# 6004871998201597316078894718748251490628361253685772937806895692619597915005978762245497623003811386
44+
# 0913693867838452088431084666963414694032898497700907783878500297536425463212578556546527017688874265
45+
# 0785862902484462361413598747384083001036443681873292719322642381945064144026145428927304407689433744
46+
# 5821277763016669042385158254006302666602333649775547203560187716156055524418512492782302125286330865
47+
3048
# === Internal representation of the number === #
3149
(
3250
BDec(

docs/internal_notes.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,18 @@
22

33
## Values and results
44

5-
- For power functionality: `BigDecimal.power()`, Python's decimal, WolframAlpha give the same result, but `mpmath` gives a different result. Eample: `0.123456789 ** 1000`, `1234523894766789 ** 1098.1209848`.
5+
- For power functionality: `BigDecimal.power()`, Python's decimal, WolframAlpha give the same result, but `mpmath` gives a different result. Eamples:
6+
- `0.123456789 ** 1000`
7+
- `1234523894766789 ** 1098.1209848`
8+
- For sin functionality: `BigDecimal.sin()` and WolframAlpha give the same results, but `mpmath` gives a different result. This occurs mainly for pi-related values. Examples:
9+
- `sin(3.1415926535897932384626433833)`, precision 50:
10+
- Decimojo: -2.0497115802830600624894179025055407692183593713791E-29
11+
- WolframAlpha: -2.0497115802830600624894179025055407692183593713791 x 10-29
12+
- mpmath: -2.049711580283060062489453928920860542175349360102e-29
13+
- `sin(6.2831853071795864769252867666)`, precision 50:
14+
- Decimojo: 4.4.0994231605661201249788358050110815384367187427582E-29
15+
- WolframAlpha: 4.4.0994231605661201249788358050110815384367187427582 x 10-29
16+
- mpmath: 4.0994231605661201249789078578417210843506987202039e-29
617

718
## Time complexity
819

src/decimojo/bigdecimal/bigdecimal.mojo

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,15 @@ struct BigDecimal(
523523
"""
524524
return decimojo.bigdecimal.arithmetics.truncate_divide(self, other)
525525

526+
@always_inline
527+
fn __mod__(self, other: Self) raises -> Self:
528+
"""Returns the result of modulo operation.
529+
See `arithmetics.truncate_modulo()` for more information.
530+
"""
531+
return decimojo.bigdecimal.arithmetics.truncate_modulo(
532+
self, other, precision=28
533+
)
534+
526535
@always_inline
527536
fn __pow__(self, exponent: Self) raises -> Self:
528537
"""Returns the result of exponentiation."""
@@ -753,6 +762,16 @@ struct BigDecimal(
753762
return decimojo.bigdecimal.exponential.power(self, exponent, precision)
754763

755764
# === Trigonometric operations === #
765+
@always_inline
766+
fn sin(self, precision: Int = 28) raises -> Self:
767+
"""Returns the sine of the BigDecimal number."""
768+
return decimojo.bigdecimal.trigonometric.sin(self, precision)
769+
770+
@always_inline
771+
fn cos(self, precision: Int = 28) raises -> Self:
772+
"""Returns the cosine of the BigDecimal number."""
773+
return decimojo.bigdecimal.trigonometric.cos(self, precision)
774+
756775
@always_inline
757776
fn arctan(self, precision: Int = 28) raises -> Self:
758777
"""Returns the arctangent of the BigDecimal number."""
@@ -826,6 +845,13 @@ struct BigDecimal(
826845
fill_zeros_to_precision: Bool,
827846
) raises:
828847
"""Rounds the number to the specified precision in-place.
848+
849+
Notes:
850+
851+
Note that precision is the number of significant digits,
852+
not the number of decimal places. If you want to round to a
853+
specific number of decimal places, use `round()` instead.
854+
829855
See `rounding.round_to_precision()` for more information.
830856
"""
831857
decimojo.bigdecimal.rounding.round_to_precision(

src/decimojo/bigdecimal/constants.mojo

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,14 @@ fn pi(precision: Int) raises -> BigDecimal:
166166

167167
# Use precomputed value for precision ≤ 1024
168168
if precision <= 1024:
169-
return PI_1024.round(precision, RoundingMode.ROUND_HALF_EVEN)
169+
var result = PI_1024
170+
result.round_to_precision(
171+
precision,
172+
RoundingMode.ROUND_HALF_EVEN,
173+
remove_extra_digit_due_to_rounding=True,
174+
fill_zeros_to_precision=False,
175+
)
176+
return result^
170177

171178
# Use Chudnovsky with binary splitting for maximum speed
172179
return pi_chudnovsky_binary_split(precision)
@@ -212,7 +219,13 @@ fn pi_chudnovsky_binary_split(precision: Int) raises -> BigDecimal:
212219
# Final formula: π = 426880 * √10005 / sum_series
213220
var result = bdec_426880 * bdec_10005.sqrt(working_precision) * sum_series
214221

215-
return result.round(precision, RoundingMode.ROUND_HALF_EVEN)
222+
result.round_to_precision(
223+
precision,
224+
RoundingMode.ROUND_HALF_EVEN,
225+
remove_extra_digit_due_to_rounding=True,
226+
fill_zeros_to_precision=False,
227+
)
228+
return result^
216229

217230

218231
fn chudnovsky_split(a: Int, b: Int, precision: Int) raises -> Rational:
@@ -309,4 +322,10 @@ fn pi_machin(precision: Int) raises -> BigDecimal:
309322
var pi_over_4 = term1 - term2
310323
var result = bdec_4 * pi_over_4
311324

312-
return result.round(precision, RoundingMode.ROUND_HALF_EVEN)
325+
result.round_to_precision(
326+
precision,
327+
RoundingMode.ROUND_HALF_EVEN,
328+
remove_extra_digit_due_to_rounding=True,
329+
fill_zeros_to_precision=False,
330+
)
331+
return result^

src/decimojo/bigdecimal/rounding.mojo

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,9 @@ fn round_to_precision(
9999
RoundingMode.ROUND_UP: Round up.
100100
RoundingMode.ROUND_HALF_UP: Round half up.
101101
RoundingMode.ROUND_HALF_EVEN: Round half even.
102-
remove_extra_digit_due_to_rounding: If True, remove an trailing.
103-
digit if the rounding mode result in an extra digit.
104-
fill_zeros_to_precision: If True, fill zeros to the precision.
102+
remove_extra_digit_due_to_rounding: If True, remove a trailing digit if
103+
the rounding mode result in an extra leading digit.
104+
fill_zeros_to_precision: If True, fill trailing zeros to the precision.
105105
"""
106106

107107
var ndigits_coefficient = number.coefficient.number_of_digits()

0 commit comments

Comments
 (0)