-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Fix ldexp to prevent math range errors #6216
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Implemented IEEE 754-compliant handling for large numbers to avoid overflow and represent results using scientific notation. Fixes RustPython#5471 Signed-off-by: Yash Suthar <[email protected]>
WalkthroughReplaced the previous ldexp path in Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Caller
participant ldexp as ldexp(x, exp)
participant Decompose as decompose_float
participant BitLogic as bit-level logic
participant Assemble as assemble_float
Note over ldexp,BitLogic:#eef3ff: IEEE-754 bit-manipulation flow
Caller->>ldexp: call(x, exp)
ldexp->>Decompose: decompose mantissa, exponent, sign
Decompose-->>ldexp: components
ldexp->>BitLogic: adjust exponent, handle subnormal, apply shifts
alt exponent > max
BitLogic-->>ldexp: return +/-inf
else exponent < min
BitLogic->>Assemble: construct denormal or signed zero (with rounding)
Assemble-->>ldexp: result
else in-range
BitLogic->>Assemble: assemble normal float bits
Assemble-->>ldexp: result
end
ldexp-->>Caller: return final float
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: Path: .coderabbit.yml Review profile: CHILL Plan: Pro ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (1)
🧰 Additional context used📓 Path-based instructions (2)**/*.rs📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
{vm,stdlib}/**/*.rs📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
🧬 Code graph analysis (1)stdlib/src/math.rs (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
🔇 Additional comments (8)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (1)
stdlib/src/math.rs (1)
576-582: Simplify overflow check by removing redundant condition.The condition
mant == 1.0on line 580 never evaluates to true becausedecompose_floatreturns a mantissa in the range [0.5, 1.0) (never exactly 1.0). The overflow check on line 577 is sufficient.Apply this diff to simplify:
let overflow_bound = BigInt::from(1024_i32 - exp0); // i > 1024 - exp0 => overflow if i_big > &overflow_bound { return Err(vm.new_overflow_error("math range error")); } -if i_big == &overflow_bound && mant == 1.0 { - return Err(vm.new_overflow_error("math range error")); -}
📜 Review details
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
stdlib/src/math.rs(2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.rs
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.rs: Format Rust code with the default rustfmt style (runcargo fmt)
Run clippy and fix any warnings or lints introduced by your changes
Follow Rust best practices for error handling and memory management
Files:
stdlib/src/math.rs
{vm,stdlib}/**/*.rs
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use RustPython macros (
pyclass,pymodule,pyfunction, etc.) when implementing Python functionality in Rust
Files:
stdlib/src/math.rs
🧬 Code graph analysis (1)
stdlib/src/math.rs (1)
common/src/float_ops.rs (1)
decompose_float(5-14)
🔇 Additional comments (4)
stdlib/src/math.rs (4)
15-15: LGTM: Import is required for the new implementation.The
ToPrimitivetrait import is used for the.to_i32()conversion on line 589.
564-573: LGTM: Early return and IEEE 754 approach are sound.The early return for special values is correct, and the IEEE 754 bit-manipulation approach properly avoids intermediate overflow that would occur with simple multiplication.
593-605: LGTM: Normal float construction is correct.The IEEE 754 bit construction for normal floats is implemented correctly, including proper sign handling, exponent calculation (
exp + 1022), and fraction bits.
607-624: LGTM: Denormalized float construction with rounding is correct.The implementation correctly handles denormalized results using proper bit shifting, round-to-nearest-even rounding, and the edge case where rounding produces the smallest normal float.
Detect denormalized input values below f64::MIN_POSITIVE Scale subnormal inputs by 2^54 to bring them into normalized range Signed-off-by: Yash Suthar <[email protected]>
|
@arihant2math @youknowone Could you please take a look and review the changes when you get a chance? |
|
Do no new tests pass because of this? |
|
@arihant2math heres the test.py which are failing . import math
# Test 1
x = 6993274598585239
result = math.ldexp(x, -1126)
expected = 1e-323
print("Test 1:", result, expected, result == expected)
# Test 2 (same as #5471 issue)
exponent = 1023
significand = 4503599627370495
mantissa = 1 + significand / 2**52
positive = True
x2 = (mantissa / 2.0) * (1 if positive else -1) # input value
result2 = math.ldexp(x2, exponent + 1)
expected2 = 1.7976931348623157e+308 # maximum finite float64
print("Test 2:", result2, expected2, result2 == expected2)Cpython Python 3.12.3 (main, Aug 14 2025, 17:47:21) [GCC 13.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import math
>>>
>>> # Test 1
>>> x = 6993274598585239
>>> result = math.ldexp(x, -1126)
>>> expected = 1e-323
>>> print("Test 1:", result, expected, result == expected)
Test 1: 1e-323 1e-323 True
>>>
>>> # Test 2 (rewritten like Test 1)
>>> exponent = 1023
>>> significand = 4503599627370495
>>> mantissa = 1 + significand / 2**52
>>> positive = True
>>>
>>> x2 = (mantissa / 2.0) * (1 if positive else -1) # input value
>>> result2 = math.ldexp(x2, exponent + 1)
>>> expected2 = 1.7976931348623157e+308 # maximum finite float64
>>> print("Test 2:", result2, expected2, result2 == expected2)
Test 2: 1.7976931348623157e+308 1.7976931348623157e+308 True
>>>
>>> Rustpython (before fix) Compiling rustpython-stdlib v0.4.0 (/home/yash/contri/RustPython/stdlib)
Compiling rustpython v0.4.0 (/home/yash/contri/RustPython)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 3.60s
Running `target/debug/rustpython`
Welcome to the magnificent Rust Python 0.4.0 interpreter 😱 🖖
RustPython 3.13.0
Type "help", "copyright", "credits" or "license" for more information.
>>>>> import math
# Test 1
x = 6993274598585239
result = math.ldexp(x, -1126)
expected = 1e-323
print("Test 1:", result, expected, result == expected)
# Test 2 (rewritten like Test 1)
exponent = 1023
significand = 4503599627370495
mantissa = 1 + significand / 2**52
positive = True
x2 = (mantissa / 2.0) * (1 if positive else -1) # input value
result2 = math.ldexp(x2, exponent + 1)
expected2 = 1.7976931348623157e+308 # maximum finite float64
print("Test 2:", result2, expected2, result2 == expected2)
Test 1: 0.0 1e-323 False
Traceback (most recent call last):
File "<stdin>", line 16, in <module>
OverflowError: math range error
>>>>> Rustpython (after fix) Compiling rustpython-stdlib v0.4.0 (/home/yash/contri/RustPython/stdlib)
Compiling rustpython v0.4.0 (/home/yash/contri/RustPython)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 3.87s
Running `target/debug/rustpython`
Welcome to the magnificent Rust Python 0.4.0 interpreter 😱 🖖
RustPython 3.13.0
Type "help", "copyright", "credits" or "license" for more information.
>>>>> import math
# Test 1
x = 6993274598585239
result = math.ldexp(x, -1126)
expected = 1e-323
print("Test 1:", result, expected, result == expected)
# Test 2 (same as #5471 issue)
exponent = 1023
significand = 4503599627370495
mantissa = 1 + significand / 2**52
positive = True
x2 = (mantissa / 2.0) * (1 if positive else -1) # input value
result2 = math.ldexp(x2, exponent + 1)
expected2 = 1.7976931348623157e+308 # maximum finite float64
print("Test 2:", result2, expected2, result2 == expected2)
Test 1: 1e-323 1e-323 True
Test 2: 1.7976931348623157e+308 1.7976931348623157e+308 True
>>>>> |
As now we have implemented IEEE 754 format , so this will pass. Signed-off-by: Yash Suthar <[email protected]>
youknowone
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for contributing!
Because I have no ability to decide it is mathematically correct, it is good as long as the more tests pass.
Please check about the lint failure and the change suggestion on the test file to keep the code style.
Additionally, if you are interested in implementing more precise python math algorithms, could you also check if this function can be implemented in pymath? It is a subproject holding bit-perfect python math library compatible functions. (This suggestion is not related to approve of this PR.)
Co-authored-by: Jeong, YunWon <[email protected]>
|
Running |
Signed-off-by: Yash Suthar <[email protected]>
|
@youknowone don't know why this test_httplib failing. |
youknowone
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No idea, probably flaky because it was unexpected success. It doesn't look related to this patch.
Thank you!
Implemented IEEE 754-compliant handling for large numbers to avoid overflow and represent results using scientific notation.
Fixes #5471
Summary by CodeRabbit