-
Notifications
You must be signed in to change notification settings - Fork 0
Floats. #23
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
base: main
Are you sure you want to change the base?
Floats. #23
Conversation
📝 WalkthroughWalkthroughA new test file has been added that introduces two implementations for decomposing floats into significant digits and exponents: an original two-branch version and an improved single-branch version. The file includes a test harness that exercises both implementations across multiple scenarios including integers, decimals, and edge cases like zero and precision overflow. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 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
🤖 Fix all issues with AI agents
In `@test/float.test.mo`:
- Around line 11-33: In decompose_Original, use a single precise ln(10) constant
instead of the two differing literals and add handling for non-positive inputs:
define a single constant (e.g., ln10) used in both branches (replace occurrences
of 2.302_585_092_994_045 and 2.302_585_092_994_047), and at the start of
decompose_Original guard against price <= 0 by returning null (or an appropriate
sentinel) to avoid Float.log(0) and Float.log(negative) producing -infinity/NaN;
update references inside the function to use ln10 and ensure the early-return
covers zero and negative prices before any Float.log calls.
🧹 Nitpick comments (3)
test/float.test.mo (3)
38-60: Clarify negative price handling behavior.The comment on line 41 mentions handling negative prices "safely" with
abs(), but the current behavior silently converts negative prices to positive results viaInt.abs(Float.toInt(r))on line 56. This means-123.45would return?(12345, -2)— the same as123.45.If negative prices should be rejected, explicitly return
null. If they should be supported, consider how sign information should be preserved or documented.Option A: Reject negative prices
func decompose_Improved(price : Float) : ?(Nat32, Int) { if (price == 0) return ?(0, 0); + if (price < 0) return null; // Uses abs() to handle potential negative prices safely - let logPrice = Float.log(Float.abs(price)) / 2.302_585_092_994_045; + let logPrice = Float.log(price) / 2.302_585_092_994_045;Option B: Document the behavior
- // Uses abs() to handle potential negative prices safely + // Negative prices are treated as their absolute value (sign is discarded) let logPrice = Float.log(Float.abs(price)) / 2.302_585_092_994_045;
66-80: Consider adding assertion traps for CI integration.The current implementation prints pass/fail results but doesn't actually fail the test execution on assertion failures. In a CI/CD pipeline, test failures would be silent (process exits successfully).
Add trap on failure for CI
func assertEqual(name : Text, actual : ?(Nat32, Int), expected : ?(Nat32, Int)) { switch (actual, expected) { case (null, null) { Debug.print("✅ " # name # ": PASS (Both null)") }; case (?(a), ?(b)) { if (a.0 == b.0 and a.1 == b.1) { Debug.print("✅ " # name # ": PASS (" # debug_show (a) # ")"); } else { - Debug.print("❌ " # name # ": FAIL. Got " # debug_show (a) # ", Expected " # debug_show (b)); + Debug.trap("❌ " # name # ": FAIL. Got " # debug_show (a) # ", Expected " # debug_show (b)); }; }; case (_, _) { - Debug.print("❌ " # name # ": FAIL. Mismatch null vs value."); + Debug.trap("❌ " # name # ": FAIL. Mismatch null vs value."); }; }; };
82-111: Consider adding test cases for negative prices.Given the implementations handle (or don't handle) negative prices differently, adding explicit test cases would clarify the expected behavior and catch regressions.
Add negative price test cases
// 7. Power of 10 assertEqual("Power of 10", funcToTest(100.0), ?(10000, -2)); + + // 8. Negative price - define expected behavior + // Option A: If negatives should be rejected: + assertEqual("Negative Price", funcToTest(-123.45), null); + // Option B: If negatives should work like positives: + // assertEqual("Negative Price", funcToTest(-123.45), ?(12345, -2)); };
| func decompose_Original(price : Float) : ?(Nat32, Int) { | ||
| if (price >= 1) { | ||
| let e1 = Float.log(price) / 2.302_585_092_994_045; | ||
| let e = Float.trunc(e1); | ||
| let exp = e + 1 - Float.fromInt(maxSignificantDigits); | ||
| let m = 10 ** exp; | ||
| let n = price / m; | ||
| let r = Float.nearest(n); | ||
| if (Float.equalWithin(n, r, 1e-10)) { | ||
| ?(Nat32.fromNat(Int.abs(Float.toInt(r))), Float.toInt(exp)); | ||
| } else { null }; | ||
| } else { | ||
| let e1 = Float.log(price) / 2.302_585_092_994_047; | ||
| let e = Float.trunc(e1); | ||
| let exp = Float.fromInt(maxSignificantDigits) - e; | ||
| let m = 10 ** exp; | ||
| let n = price * m; | ||
| let r = Float.nearest(n); | ||
| if (Float.equalWithin(n, r, 1e-10)) { | ||
| ?(Nat32.fromNat(Int.abs(Float.toInt(r))), Float.toInt(-exp)); | ||
| } else { null }; | ||
| }; | ||
| }; |
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.
Inconsistent ln(10) constants and missing edge case handling.
Two issues identified:
-
Inconsistent constants: Line 13 uses
2.302_585_092_994_045while line 23 uses2.302_585_092_994_047. This appears to be a typo and could cause subtle precision differences between branches. -
Missing zero/negative handling: When
price == 0,Float.log(0)returns-infinity, causing undefined behavior. For negative prices,Float.log(negative)returnsNaN. The improved version handles zero but this original version does not.
Proposed fix
func decompose_Original(price : Float) : ?(Nat32, Int) {
+ if (price == 0) return ?(0, 0);
+ if (price < 0) return null; // or handle negative prices explicitly
+
if (price >= 1) {
let e1 = Float.log(price) / 2.302_585_092_994_045;
let e = Float.trunc(e1);
let exp = e + 1 - Float.fromInt(maxSignificantDigits);
let m = 10 ** exp;
let n = price / m;
let r = Float.nearest(n);
if (Float.equalWithin(n, r, 1e-10)) {
?(Nat32.fromNat(Int.abs(Float.toInt(r))), Float.toInt(exp));
} else { null };
} else {
- let e1 = Float.log(price) / 2.302_585_092_994_047;
+ let e1 = Float.log(price) / 2.302_585_092_994_045;
let e = Float.trunc(e1);🤖 Prompt for AI Agents
In `@test/float.test.mo` around lines 11 - 33, In decompose_Original, use a single
precise ln(10) constant instead of the two differing literals and add handling
for non-positive inputs: define a single constant (e.g., ln10) used in both
branches (replace occurrences of 2.302_585_092_994_045 and
2.302_585_092_994_047), and at the start of decompose_Original guard against
price <= 0 by returning null (or an appropriate sentinel) to avoid Float.log(0)
and Float.log(negative) producing -infinity/NaN; update references inside the
function to use ln10 and ensure the early-return covers zero and negative prices
before any Float.log calls.
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.