From bb1a22abab2ba8a4f12d0c1966410c2b2e57625a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ZHU=20Yuhao=20=E6=9C=B1=E5=AE=87=E6=B5=A9?= Date: Tue, 16 Dec 2025 21:34:49 +0100 Subject: [PATCH 1/2] update recipe and readme --- recipes/decimojo/README.md | 289 +++++++++++++++++++++-------------- recipes/decimojo/recipe.yaml | 25 ++- 2 files changed, 187 insertions(+), 127 deletions(-) diff --git a/recipes/decimojo/README.md b/recipes/decimojo/README.md index fe0fd0ef..0f62abe4 100644 --- a/recipes/decimojo/README.md +++ b/recipes/decimojo/README.md @@ -18,119 +18,143 @@ The core types are: 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. -| type | alias | information | internal representation | -| ------------ | ------- | ------------------------------------ | ----------------------------------- | -| `BigUInt` | `BUInt` | arbitrary-precision unsigned integer | `List[UInt32]` | -| `BigInt` | `BInt` | arbitrary-precision integer | `BigUInt`, `Bool` | -| `Decimal` | `Dec` | 128-bit fixed-precision decimal | `UInt32`,`UInt32`,`UInt32`,`UInt32` | -| `BigDecimal` | `BDec` | arbitrary-precision decimal | `BigUInt`, `Int`, `Bool` | +| type | alias | information | internal representation | +| ------------ | ----------------- | ------------------------------------ | ----------------------------------- | +| `BigUInt` | `BUInt` | arbitrary-precision unsigned integer | `List[UInt32]` | +| `BigInt` | `BInt` | arbitrary-precision integer | `BigUInt`, `Bool` | +| `BigDecimal` | `BDec`, `Decimal` | arbitrary-precision decimal | `BigUInt`, `Int`, `Bool` | +| `Decimal128` | `Dec128` | 128-bit fixed-precision decimal | `UInt32`,`UInt32`,`UInt32`,`UInt32` | ## Installation DeciMojo is available in the [modular-community](https://repo.prefix.dev/modular-community) package repository. You can install it using any of these methods: -From the `magic` CLI, simply run ```magic add decimojo```. This fetches the latest version and makes it immediately available for import. +1. From the `pixi` CLI, run the command ```pixi add decimojo```. This fetches the latest version and makes it immediately available for import. -For projects with a `mojoproject.toml`file, add the dependency ```decimojo = ">=0.3.0"```. Then run `magic install` to download and install the package. +1. In the `mojoproject.toml` file of your project, add the following dependency: -For the latest development version, clone the [GitHub repository](https://github.com/forfudan/decimojo) and build the package locally. + ```toml + decimojo = "==0.6.0" + ``` -| `decimojo` | `mojo` | -| ---------- | ------ | -| v0.1.0 | >=25.1 | -| v0.2.0 | >=25.2 | -| v0.3.0 | >=25.2 | + Then run `pixi install` to download and install the package. + +1. For the latest development version in the `main` branch, clone [this GitHub repository](https://github.com/forfudan/decimojo) and build the package locally using the command `pixi run package`. + +The following table summarizes the package versions and their corresponding Mojo versions: + +| `decimojo` | `mojo` | package manager | +| ---------- | ------------- | --------------- | +| v0.1.0 | ==25.1 | magic | +| v0.2.0 | ==25.2 | magic | +| v0.3.0 | ==25.2 | magic | +| v0.3.1 | >=25.2, <25.4 | pixi | +| v0.4.x | ==25.4 | pixi | +| v0.5.0 | ==25.5 | pixi | +| v0.6.0 | ==0.25.7 | pixi | ## Quick start -Here is a comprehensive quick-start guide showcasing each major function of the `Decimal` type. +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. ```mojo -from decimojo import Decimal, RoundingMode - -fn main() raises: - # === Construction === - var a = Decimal("123.45") # From string - var b = Decimal(123) # From integer - var c = Decimal(123, 2) # Integer with scale (1.23) - var d = Decimal.from_float(3.14159) # From floating-point - - # === Basic Arithmetic === - print(a + b) # Addition: 246.45 - print(a - b) # Subtraction: 0.45 - print(a * b) # Multiplication: 15184.35 - print(a / b) # Division: 1.0036585365853658536585365854 - - # === Rounding & Precision === - print(a.round(1)) # Round to 1 decimal place: 123.5 - print(a.quantize(Decimal("0.01"))) # Format to 2 decimal places: 123.45 - print(a.round(0, RoundingMode.ROUND_DOWN)) # Round down to integer: 123 - - # === Comparison === - print(a > b) # Greater than: True - print(a == Decimal("123.45")) # Equality: True - print(a.is_zero()) # Check for zero: False - print(Decimal("0").is_zero()) # Check for zero: True - - # === Type Conversions === - print(Float64(a)) # To float: 123.45 - print(a.to_int()) # To integer: 123 - print(a.to_str()) # To string: "123.45" - print(a.coefficient()) # Get coefficient: 12345 - print(a.scale()) # Get scale: 2 - - # === Mathematical Functions === - print(Decimal("2").sqrt()) # Square root: 1.4142135623730950488016887242 - print(Decimal("100").root(3)) # Cube root: 4.641588833612778892410076351 - print(Decimal("2.71828").ln()) # Natural log: 0.9999993273472820031578910056 - print(Decimal("10").log10()) # Base-10 log: 1 - print(Decimal("16").log(Decimal("2"))) # Log base 2: 3.9999999999999999999999999999 - print(Decimal("10").exp()) # e^10: 22026.465794806716516957900645 - print(Decimal("2").power(10)) # Power: 1024 - - # === Sign Handling === - print(-a) # Negation: -123.45 - print(abs(Decimal("-123.45"))) # Absolute value: 123.45 - print(Decimal("123.45").is_negative()) # Check if negative: False - - # === Special Values === - print(Decimal.PI()) # π constant: 3.1415926535897932384626433833 - print(Decimal.E()) # e constant: 2.7182818284590452353602874714 - print(Decimal.ONE()) # Value 1: 1 - print(Decimal.ZERO()) # Value 0: 0 - print(Decimal.MAX()) # Maximum value: 79228162514264337593543950335 - - # === Convenience Methods === - print(Decimal("123.400").is_integer()) # Check if integer: False - print(a.number_of_significant_digits()) # Count significant digits: 5 - print(Decimal("12.34").to_str_scientific()) # Scientific notation: 1.234E+1 +from decimojo import * ``` -[Click here for 8 key examples](https://zhuyuhao.com/decimojo/docs/examples) highlighting the most important features of the `Decimal` type. +This will import the following types or aliases into your namespace: + +- `dm`: An alias for the `decimojo` module. +- `BigInt` (alias `BInt`): An arbitrary-precision signed integer type. +- `BigDecimal` (aliases `BDec` and `Decimal`): An arbitrary-precision decimal type. +- `Decimal128` (alias `Dec128`): A 128-bit fixed-precision decimal type. +- `RoundingMode` (alias `RM`): An enumeration for rounding modes. +- `ROUND_DOWN`, `ROUND_HALF_UP`, `ROUND_HALF_EVEN`, `ROUND_UP`: Constants for common rounding modes. -Here are some examples showcasing the arbitrary-precision feature of the `BigDecimal` type. +--- + +Here are some examples showcasing the arbitrary-precision feature of the `BigDecimal` type (aliases: `BDec` and `Decimal`). For some mathematical operations, the default precision (number of significant digits) is set to `36`. You can change the precision by passing the `precision` argument to the function. This default precision will be configurable globally in future when Mojo supports global variables. ```mojo -from decimojo import BDec, RM +from decimojo.prelude import * fn main() raises: - var PRECISION = 100 - var a = BDec("123456789.123456789") - var b = BDec("1234.56789") - print(a.sqrt(precision=PRECISION)) - # 11111.11106611111096943055498174930232833813065468909453818857935956641682120364106016272519460988485 - print(a.power(b, precision=PRECISION)) - # 3.346361102419080234023813540078946868219632448203078657310495672766009862564151996325555496759911131748170844123475135377098326591508239654961E+9989 - print(a.log(b, precision=PRECISION)) - # 2.617330026656548299907884356415293977170848626010103229392408225981962436022623783231699264341492663671325580092077394824180414301026578169909 + var a = BDec("123456789.123456789") # BDec is an alias for BigDecimal + var b = Decimal( + "1234.56789" + ) # Decimal is a Python-like alias for BigDecimal + + # === Basic Arithmetic === # + print(a + b) # 123458023.691346789 + print(a - b) # 123455554.555566789 + print(a * b) # 152415787654.32099750190521 + print(a.true_divide(b + 1)) # 99919.0656560820700835791386582569736 + + # === Exponential Functions === # + print(a.sqrt(precision=80)) + # 11111.111066111110969430554981749302328338130654689094538188579359566416821203641 + print(a.cbrt(precision=80)) + # 497.93385938415242742001134219007635925452951248903093962731782327785111102410518 + print(a.root(b, precision=80)) + # 1.0152058862996527138602610522640944903320735973237537866713119992581006582644107 + print(a.power(b, precision=80)) + # 3.3463611024190802340238135400789468682196324482030786573104956727660098625641520E+9989 + print(a.exp(precision=80)) + # 1.8612755889649587035842377856492201091251654136588338983610243887893287518637652E+53616602 + print(a.log(b, precision=80)) + # 2.6173300266565482999078843564152939771708486260101032293924082259819624360226238 + print(a.ln(precision=80)) + # 18.631401767168018032693933348296537542797015174553735308351756611901741276655161 + + # === Trigonometric Functions === # + print(a.sin(precision=200)) + # 0.99985093087193092464780008002600992896256609588456 + # 91036188395766389946401881352599352354527727927177 + # 79589259132243649550891532070326452232864052771477 + # 31418817041042336608522984511928095747763538486886 + print(b.cos(precision=1000)) + # -0.9969577603867772005841841569997528013669868536239849713029893885930748434064450375775817720425329394 + # 9756020177557431933434791661179643984869397089102223199519409695771607230176923201147218218258755323 + # 7563476302904118661729889931783126826250691820526961290122532541861737355873869924820906724540889765 + # 5940445990824482174517106016800118438405307801022739336016834311018727787337447844118359555063575166 + # 5092352912854884589824773945355279792977596081915868398143592738704592059567683083454055626123436523 + # 6998108941189617922049864138929932713499431655377552668020889456390832876383147018828166124313166286 + # 6004871998201597316078894718748251490628361253685772937806895692619597915005978762245497623003811386 + # 0913693867838452088431084666963414694032898497700907783878500297536425463212578556546527017688874265 + # 0785862902484462361413598747384083001036443681873292719322642381945064144026145428927304407689433744 + # 5821277763016669042385158254006302666602333649775547203560187716156055524418512492782302125286330865 + + # === Internal representation of the number === # + ( + Decimal( + "3.141592653589793238462643383279502884197169399375105820974944" + ).power(2, precision=60) + ).print_internal_representation() + # Internal Representation Details of BigDecimal + # ---------------------------------------------- + # number: 9.8696044010893586188344909998 + # 761511353136994072407906264133 + # 5 + # coefficient: 986960440108935861883449099987 + # 615113531369940724079062641335 + # negative: False + # scale: 59 + # word 0: 62641335 + # word 1: 940724079 + # word 2: 113531369 + # word 3: 99987615 + # word 4: 861883449 + # word 5: 440108935 + # word 6: 986960 + # ---------------------------------------------- ``` -Here is a comprehensive quick-start guide showcasing each major function of the `BigInt` type. +--- + +Here is a comprehensive quick-start guide showcasing each major function of the `BigInt` type (`BInt`). ```mojo -from decimojo import BigInt, BInt -# BInt is an alias for BigInt +from decimojo.prelude import * fn main() raises: # === Construction === @@ -157,7 +181,7 @@ fn main() raises: print(a.is_zero()) # Check for zero: False # === Type Conversions === - print(a.to_str()) # To string: "12345678901234567890" + print(String(a)) # To string: "12345678901234567890" # === Sign Handling === print(-a) # Negation: -12345678901234567890 @@ -169,6 +193,71 @@ fn main() raises: print(BInt("123456789" * 400) // BInt("987654321" * 200)) ``` +--- + +Here is a comprehensive quick-start guide showcasing each major function of the `Decimal128` type (`Dec128`). + +```mojo +from decimojo.prelude import * + +fn main() raises: + # === Construction === + var a = Dec128("123.45") # From string + var b = Dec128(123) # From integer + var c = Dec128(123, 2) # Integer with scale (1.23) + var d = Dec128.from_float(3.14159) # From floating-point + + # === Basic Arithmetic === + print(a + b) # Addition: 246.45 + print(a - b) # Subtraction: 0.45 + print(a * b) # Multiplication: 15184.35 + print(a / b) # Division: 1.0036585365853658536585365854 + + # === Rounding & Precision === + print(a.round(1)) # Round to 1 decimal place: 123.5 + print(a.quantize(Dec128("0.01"))) # Format to 2 decimal places: 123.45 + print(a.round(0, RoundingMode.ROUND_DOWN)) # Round down to integer: 123 + + # === Comparison === + print(a > b) # Greater than: True + print(a == Dec128("123.45")) # Equality: True + print(a.is_zero()) # Check for zero: False + print(Dec128("0").is_zero()) # Check for zero: True + + # === Type Conversions === + print(Float64(a)) # To float: 123.45 + print(a.to_int()) # To integer: 123 + print(a.to_str()) # To string: "123.45" + print(a.coefficient()) # Get coefficient: 12345 + print(a.scale()) # Get scale: 2 + + # === Mathematical Functions === + print(Dec128("2").sqrt()) # Square root: 1.4142135623730950488016887242 + print(Dec128("100").root(3)) # Cube root: 4.641588833612778892410076351 + print(Dec128("2.71828").ln()) # Natural log: 0.9999993273472820031578910056 + print(Dec128("10").log10()) # Base-10 log: 1 + print(Dec128("16").log(Dec128("2"))) # Log base 2: 3.9999999999999999999999999999 + print(Dec128("10").exp()) # e^10: 22026.465794806716516957900645 + print(Dec128("2").power(10)) # Power: 1024 + + # === Sign Handling === + print(-a) # Negation: -123.45 + print(abs(Dec128("-123.45"))) # Absolute value: 123.45 + print(Dec128("123.45").is_negative()) # Check if negative: False + + # === Special Values === + print(Dec128.PI()) # π constant: 3.1415926535897932384626433833 + print(Dec128.E()) # e constant: 2.7182818284590452353602874714 + print(Dec128.ONE()) # Value 1: 1 + print(Dec128.ZERO()) # Value 0: 0 + print(Dec128.MAX()) # Maximum value: 79228162514264337593543950335 + + # === Convenience Methods === + print(Dec128("123.400").is_integer()) # Check if integer: False + print(a.number_of_significant_digits()) # Count significant digits: 5 + print(Dec128("12.34").to_str_scientific()) # Scientific notation: 1.234E+1 +``` + ## Objective Financial calculations and data analysis require precise decimal arithmetic that floating-point numbers cannot reliably provide. As someone working in finance and credit risk model validation, I needed a dependable correctly-rounded, fixed-precision numeric type when migrating my personal projects from Python to Mojo. @@ -191,28 +280,6 @@ Rome wasn't built in a day. DeciMojo is currently under active development. It h Regular benchmarks against Python's `decimal` module are available in the `bench/` folder, documenting both the performance advantages and the few specific operations where different approaches are needed. -## Tests and benches - -After cloning the repo onto your local disk, you can: - -- Use `magic run test` to run tests. -- Use `magic run bench_decimal` to generate logs for benchmarking tests against `python.decimal` module. The log files are saved in `benches/decimal/logs/`. - -## Citation - -If you find DeciMojo useful for your research, consider listing it in your citations. - -```tex -@software{Zhu.2025, - author = {Zhu, Yuhao}, - year = {2025}, - title = {An arbitrary-precision decimal and integer mathematics library for Mojo}, - url = {https://github.com/forfudan/decimojo}, - version = {0.3.0}, - note = {Computer Software} -} -``` - ## License This repository and its contributions are licensed under the Apache License v2.0. diff --git a/recipes/decimojo/recipe.yaml b/recipes/decimojo/recipe.yaml index 4521f7bf..a4f46753 100644 --- a/recipes/decimojo/recipe.yaml +++ b/recipes/decimojo/recipe.yaml @@ -1,5 +1,6 @@ context: - version: "0.4.1" + version: "0.6.0" + mojo_version: "=0.25.7" package: name: "decimojo" @@ -7,29 +8,21 @@ package: source: - git: https://github.com/forFudan/decimojo.git - rev: 51c983229749df1caf490ccb9ddea19e7f220441 + rev: 0fab1a2349cf05b854c4c204ec2121f634396809 build: number: 0 script: + - mkdir -p ${PREFIX}/lib/mojo - mojo package src/decimojo -o ${{ PREFIX }}/lib/mojo/decimojo.mojopkg + requirements: host: - - max=25.4 + - mojo-compiler ${{ mojo_version }} + build: + - mojo-compiler ${{ mojo_version }} run: - - ${{ pin_compatible('max') }} - -tests: - - script: - - if: unix - then: - - mojo run tests.mojo - requirements: - run: - - max=25.4 - files: - recipe: - - tests.mojo + - ${{ pin_compatible('mojo-compiler') }} about: homepage: https://github.com/forFudan/decimojo.git From e234e606b141d92800f235840a3a21c904367f09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ZHU=20Yuhao=20=E6=9C=B1=E5=AE=87=E6=B5=A9?= Date: Tue, 16 Dec 2025 21:39:23 +0100 Subject: [PATCH 2/2] Update recipe --- recipes/decimojo/recipe.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/recipes/decimojo/recipe.yaml b/recipes/decimojo/recipe.yaml index a4f46753..db828cf7 100644 --- a/recipes/decimojo/recipe.yaml +++ b/recipes/decimojo/recipe.yaml @@ -22,7 +22,7 @@ requirements: build: - mojo-compiler ${{ mojo_version }} run: - - ${{ pin_compatible('mojo-compiler') }} + - mojo-compiler ${{ mojo_version }} about: homepage: https://github.com/forFudan/decimojo.git