Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 5 additions & 7 deletions src/currency/btc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,17 +89,15 @@ mod tests {
}

#[test]
fn round_value_has_correct_decimal_places(
fn round_value_is_idempotent(
amount in 0.0_f64..1.0e8,
unit in arb_btc_unit(),
) {
let rounded = unit.round_value(amount);
let factor = 10_f64.powi(unit.decimal_places().into());
let check = (rounded * factor).round() / factor;
let diff = (rounded - check).abs();
let once = unit.round_value(amount);
let twice = unit.round_value(once);
prop_assert!(
diff < 1.0e-10,
"round_value produced too many decimal places: {rounded} (expected {check})"
(once - twice).abs() < f64::EPSILON,
"round_value is not idempotent: round({amount}) = {once}, round({once}) = {twice}"
);
}
Comment on lines 91 to 102
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

The f64::EPSILON tolerance is too strict and causes test failures.

The pipeline shows this test is still flaky: round(37515032.691102825) = 37515032.69110283, round(37515032.69110283) = 37515032.69110284.

f64::EPSILON (~2.2e-16) is the precision near 1.0, but for values around 3.7e7, the actual precision is roughly 3.7e7 * 2.2e-16 ≈ 8e-9. When round_value divides by the factor, the result may not be exactly representable, and re-rounding can shift by 1 ULP—which is far larger than f64::EPSILON.

Use a tolerance proportional to the value's magnitude and the decimal precision being tested:

Proposed fix using relative tolerance
     #[test]
     fn round_value_is_idempotent(
         amount in 0.0_f64..1.0e8,
         unit in arb_btc_unit(),
     ) {
         let once = unit.round_value(amount);
         let twice = unit.round_value(once);
+        // Tolerance must account for f64 representation limits at this magnitude.
+        // For 8 decimal places, 1e-7 provides margin for 1-ULP drift.
+        let tol = 1.0e-7_f64.max(once.abs() * 1.0e-14);
         prop_assert!(
-            (once - twice).abs() < f64::EPSILON,
+            (once - twice).abs() < tol,
             "round_value is not idempotent: round({amount}) = {once}, round({once}) = {twice}"
         );
     }

Alternatively, if strict idempotence is required, consider using fixed-point or decimal types for currency calculations.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#[test]
fn round_value_has_correct_decimal_places(
fn round_value_is_idempotent(
amount in 0.0_f64..1.0e8,
unit in arb_btc_unit(),
) {
let rounded = unit.round_value(amount);
let factor = 10_f64.powi(unit.decimal_places().into());
let check = (rounded * factor).round() / factor;
let diff = (rounded - check).abs();
let once = unit.round_value(amount);
let twice = unit.round_value(once);
prop_assert!(
diff < 1.0e-10,
"round_value produced too many decimal places: {rounded} (expected {check})"
(once - twice).abs() < f64::EPSILON,
"round_value is not idempotent: round({amount}) = {once}, round({once}) = {twice}"
);
}
#[test]
fn round_value_is_idempotent(
amount in 0.0_f64..1.0e8,
unit in arb_btc_unit(),
) {
let once = unit.round_value(amount);
let twice = unit.round_value(once);
// Tolerance must account for f64 representation limits at this magnitude.
// For 8 decimal places, 1e-7 provides margin for 1-ULP drift.
let tol = 1.0e-7_f64.max(once.abs() * 1.0e-14);
prop_assert!(
(once - twice).abs() < tol,
"round_value is not idempotent: round({amount}) = {once}, round({once}) = {twice}"
);
}
🧰 Tools
🪛 GitHub Actions: Build and Test

[error] 98-98: Assertion context for failing proptest 'currency::btc::tests::round_value_is_idempotent' (minimal failing input: amount=37515032.691102825, unit=BTC).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/currency/btc.rs` around lines 91 - 102, The test
round_value_is_idempotent uses an absolute tolerance f64::EPSILON which is too
strict for large magnitudes; update the assertion in the
round_value_is_idempotent test to use a relative or magnitude-scaled tolerance
instead of f64::EPSILON: compute an allowed_delta as something like
(once.abs().max(1.0) * unit.precision_factor_or_decimal_places * f64::EPSILON)
or derive it from the unit's rounding factor, then assert (once - twice).abs()
<= allowed_delta; reference the test function round_value_is_idempotent and the
method unit.round_value to locate where to change the assertion.

}
Expand Down
Loading