Skip to content

Conversation

@forfudan
Copy link
Owner

This pull request:

  • Re-writes Decimal constructor from String via the static method from_string(). This significantly improves the performance (x100) and can handle different errors.
  • Fixes a bug in the round_to_keep_first_n_digits function which may falsely round-up in round-half-to-even mode. This is caused by an implicit type conversion which makes the value unsigned.
  • Adds comprehensive tests for the Decimal.from_string() method.
  • Adds benchmarking tests for the Decimal.from_string() method.

Re-writes Decimal constructor from String:

  • Use a more sophisticated approach to scan characters in the string.
  • Can handle different mistakes and raise corresponding error messages.
  • This significantly improves the performance (x100).
  • Add a static method from_string(). Then __init__ calls this method.

Codebase improvements:

  • Modified the round_to_keep_first_n_digits function in src/decimojo/utility.mojo to use explicit type casting for cutoff_value.

Addition of comprehensive tests:

  • Added a new test file tests/test_from_string.mojo with 50 test cases to ensure proper conversion from string values to Decimal objects. This includes tests for basic integers, basic decimals, negative numbers, zero variants, scientific notation, formatting variants, special characters, invalid inputs, boundary cases, and special cases.

Updates to mojoproject.toml:

  • Added new test commands for test_from_float and test_from_string to the test section.
  • Added new benchmark commands for bench_from_float and bench_from_string to the bench section.

@forfudan forfudan merged commit 1ce242f into main Mar 13, 2025
1 check passed
@forfudan forfudan deleted the work branch March 13, 2025 08:01
@forfudan
Copy link
Owner Author

Before

=== DeciMojo String Constructor Benchmark ===
Time: 2025-03-12T15:24:09.361299
System: Darwin 24.3.0
Processor: arm
Python version: 3.12.9
Python decimal precision: 28
Mojo decimal precision: 28

Running string constructor benchmarks with 10000 iterations each

Benchmark:       Simple integer
String value:    123
Mojo result:     123
Python result:   123
Mojo Decimal:    1870.0 ns per iteration
Python Decimal:  7590.5 ns per iteration
Speedup factor:  4.0590909090909095

Benchmark:       Simple decimal with few places
String value:    123.45
Mojo result:     123.45
Python result:   123.45
Mojo Decimal:    3077.6 ns per iteration
Python Decimal:  7799.8 ns per iteration
Speedup factor:  2.5343774369638683

Benchmark:       Negative number
String value:    -123.45
Mojo result:     -123.45
Python result:   -123.45
Mojo Decimal:    3331.1 ns per iteration
Python Decimal:  7496.9 ns per iteration
Speedup factor:  2.2505778871844133

Benchmark:       Zero value
String value:    0
Mojo result:     0
Python result:   0
Mojo Decimal:    584.0 ns per iteration
Python Decimal:  7656.2 ns per iteration
Speedup factor:  13.109931506849314

Benchmark:       Long integer part
String value:    12345678901234567890
Mojo result:     12345678901234567890
Python result:   12345678901234567890
Mojo Decimal:    11541.3 ns per iteration
Python Decimal:  7933.2 ns per iteration
Speedup factor:  0.6873749057731798

Benchmark:       Long fractional part
String value:    0.12345678901234567890123456789
Mojo result:     0.1234567890123456789012345678
Python result:   0.12345678901234567890123456789
Mojo Decimal:    18428.3 ns per iteration
Python Decimal:  8041.1 ns per iteration
Speedup factor:  0.43634518647949083

Benchmark:       Scientific notation (positive exponent)
String value:    1.23456e5
Mojo result:     123456
Python result:   123456
Mojo Decimal:    4868.1 ns per iteration
Python Decimal:  7965.2 ns per iteration
Speedup factor:  1.6362030360921096

Benchmark:       Scientific notation (negative exponent)
String value:    1.23456e-10
Mojo result:     0.000000000123456
Python result:   1.23456E-10
Mojo Decimal:    5004.2 ns per iteration
Python Decimal:  7923.0 ns per iteration
Speedup factor:  1.5832700531553496

Benchmark:       Leading zeros
String value:    000123.45
Mojo result:     123.45
Python result:   123.45
Mojo Decimal:    5027.7 ns per iteration
Python Decimal:  8004.5 ns per iteration
Speedup factor:  1.5920798774787677

Benchmark:       Trailing zeros
String value:    123.45000
Mojo result:     123.45000
Python result:   123.45000
Mojo Decimal:    5224.2 ns per iteration
Python Decimal:  7670.7 ns per iteration
Speedup factor:  1.468301366716435

Benchmark:       Leading and trailing zeros
String value:    000123.45000
Mojo result:     123.45000
Python result:   123.45000
Mojo Decimal:    6433.9 ns per iteration
Python Decimal:  7813.0 ns per iteration
Speedup factor:  1.214348995166229

Benchmark:       Very small value
String value:    0.000000000000000000001
Mojo result:     0.000000000000000000001
Python result:   1E-21
Mojo Decimal:    10681.6 ns per iteration
Python Decimal:  7717.2 ns per iteration
Speedup factor:  0.7224760335530257

Benchmark:       Plus sign
String value:    123.45
Mojo result:     123.45
Python result:   123.45
Mojo Decimal:    3106.8 ns per iteration
Python Decimal:  7606.1 ns per iteration
Speedup factor:  2.4482103772370283

Benchmark:       Precision at boundary
String value:    0.1111111111111111111111111111
Mojo result:     0.1111111111111111111111111111
Python result:   0.1111111111111111111111111111
Mojo Decimal:    17692.6 ns per iteration
Python Decimal:  7891.4 ns per iteration
Speedup factor:  0.44602828301097636

Benchmark:       Exponent with sign
String value:    1.23e+10
Mojo result:     12300000000
Python result:   1.23E+10
Mojo Decimal:    3821.9 ns per iteration
Python Decimal:  7540.9 ns per iteration
Speedup factor:  1.9730762186347104

=== String Constructor Benchmark Summary ===
Benchmarked:      15 different string constructor cases
Each case ran:    10000 iterations
Average speedup:  2.4107794715590534×

Individual speedup factors:
Case 1: 4.06×
Case 2: 2.53×
Case 3: 2.25×
Case 4: 13.11×
Case 5: 0.69×
Case 6: 0.44×
Case 7: 1.64×
Case 8: 1.58×
Case 9: 1.59×
Case 10: 1.47×
Case 11: 1.21×
Case 12: 0.72×
Case 13: 2.45×
Case 14: 0.45×
Case 15: 1.97×

@forfudan
Copy link
Owner Author

After:

=== DeciMojo String Constructor Benchmark ===
Time: 2025-03-13T23:28:43.272586
System: Darwin 24.3.0
Processor: arm
Python version: 3.12.9
Python decimal precision: 28
Mojo decimal precision: 28

Running string constructor benchmarks with 10000 iterations each

Benchmark:       Simple integer
String value:    123
Mojo result:     123
Python result:   123
Mojo Decimal:    9.9 ns per iteration
Python Decimal:  7823.4 ns per iteration
Speedup factor:  790.2424242424241

Benchmark:       Simple decimal with few places
String value:    123.45
Mojo result:     123.45
Python result:   123.45
Mojo Decimal:    15.7 ns per iteration
Python Decimal:  7831.5 ns per iteration
Speedup factor:  498.8216560509554

Benchmark:       Negative number
String value:    -123.45
Mojo result:     -123.45
Python result:   -123.45
Mojo Decimal:    15.8 ns per iteration
Python Decimal:  7943.4 ns per iteration
Speedup factor:  502.7468354430379

Benchmark:       Zero value
String value:    0
Mojo result:     0
Python result:   0
Mojo Decimal:    7.6 ns per iteration
Python Decimal:  7788.3 ns per iteration
Speedup factor:  1024.7763157894738

Benchmark:       Long integer part
String value:    12345678901234567890
Mojo result:     12345678901234567890
Python result:   12345678901234567890
Mojo Decimal:    36.9 ns per iteration
Python Decimal:  8046.7 ns per iteration
Speedup factor:  218.06775067750678

Benchmark:       Long fractional part
String value:    0.12345678901234567890123456789
Mojo result:     0.1234567890123456789012345679
Python result:   0.12345678901234567890123456789
Mojo Decimal:    140.4 ns per iteration
Python Decimal:  8082.2 ns per iteration
Speedup factor:  57.565527065527064

Benchmark:       Scientific notation (positive exponent)
String value:    1.23456e5
Mojo result:     123456
Python result:   123456
Mojo Decimal:    21.0 ns per iteration
Python Decimal:  7898.1 ns per iteration
Speedup factor:  376.1

Benchmark:       Scientific notation (negative exponent)
String value:    1.23456e-10
Mojo result:     0.000000000123456
Python result:   1.23456E-10
Mojo Decimal:    25.3 ns per iteration
Python Decimal:  8228.5 ns per iteration
Speedup factor:  325.2371541501976

Benchmark:       Leading zeros
String value:    000123.45
Mojo result:     123.45
Python result:   123.45
Mojo Decimal:    18.4 ns per iteration
Python Decimal:  7650.3 ns per iteration
Speedup factor:  415.7771739130435

Benchmark:       Trailing zeros
String value:    123.45000
Mojo result:     123.45000
Python result:   123.45000
Mojo Decimal:    17.2 ns per iteration
Python Decimal:  7920.6 ns per iteration
Speedup factor:  460.50000000000006

Benchmark:       Leading and trailing zeros
String value:    000123.45000
Mojo result:     123.45000
Python result:   123.45000
Mojo Decimal:    22.2 ns per iteration
Python Decimal:  8019.4 ns per iteration
Speedup factor:  361.23423423423424

Benchmark:       Very small value
String value:    0.000000000000000000001
Mojo result:     0.000000000000000000001
Python result:   1E-21
Mojo Decimal:    33.7 ns per iteration
Python Decimal:  7886.7 ns per iteration
Speedup factor:  234.02670623145397

Benchmark:       Plus sign
String value:    123.45
Mojo result:     123.45
Python result:   123.45
Mojo Decimal:    16.4 ns per iteration
Python Decimal:  7945.4 ns per iteration
Speedup factor:  484.4756097560976

Benchmark:       Precision at boundary
String value:    0.1111111111111111111111111111
Mojo result:     0.1111111111111111111111111111
Python result:   0.1111111111111111111111111111
Mojo Decimal:    47.7 ns per iteration
Python Decimal:  8089.5 ns per iteration
Speedup factor:  169.59119496855345

Benchmark:       Exponent with sign
String value:    1.23e+10
Mojo result:     12300000000
Python result:   1.23E+10
Mojo Decimal:    17.6 ns per iteration
Python Decimal:  7796.1 ns per iteration
Speedup factor:  442.96022727272725

=== String Constructor Benchmark Summary ===
Benchmarked:      15 different string constructor cases
Each case ran:    10000 iterations
Average speedup:  424.1415206530155×

Individual speedup factors:
Case 1: 790.24×
Case 2: 498.82×
Case 3: 502.75×
Case 4: 1024.78×
Case 5: 218.07×
Case 6: 57.57×
Case 7: 376.1×
Case 8: 325.24×
Case 9: 415.78×
Case 10: 460.5×
Case 11: 361.23×
Case 12: 234.03×
Case 13: 484.48×
Case 14: 169.59×
Case 15: 442.96×

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants