diff --git a/src/test/protocol/STAmount_test.cpp b/src/test/protocol/STAmount_test.cpp index 7c37d3e9bc..475b8d41c2 100644 --- a/src/test/protocol/STAmount_test.cpp +++ b/src/test/protocol/STAmount_test.cpp @@ -1209,6 +1209,72 @@ public: //-------------------------------------------------------------------------- + void + testIsZeroAtScale() + { + testcase("isZeroAtScale"); + + Issue const usd{Currency(0x5553440000000000), AccountID(0x4985601)}; + + // IOU: 10 IOU — mantissa = kMIN_VALUE (10^15), exponent = -14. + // One ULP at this scale is 10^-14; half-ULP is 5*10^-15. + { + STAmount const ref{usd, STAmount::kMIN_VALUE, -14}; + int const refScale = ref.exponent(); // -14 + BEAST_EXPECT(refScale == -14); + + // Zero rounds to zero at any scale. + STAmount const iouZero{usd, 0}; + BEAST_EXPECT(iouZero.isZeroAtScale(refScale)); + + // Sub-ULP: 1e-16 IOU (mantissa = kMIN_VALUE, exponent = -31). + // Far below half-ULP → rounds to zero. + STAmount const subUlp{usd, STAmount::kMIN_VALUE, -31}; + BEAST_EXPECT(subUlp.isZeroAtScale(refScale)); + + // One ULP: 1e-14 IOU (mantissa = kMIN_VALUE, exponent = -29). + // Exactly the smallest representable unit at refScale → not zero. + STAmount const oneUlp{usd, STAmount::kMIN_VALUE, -29}; + BEAST_EXPECT(!oneUlp.isZeroAtScale(refScale)); + + // The reference value itself: exponent == scale → returned + // unchanged → not zero. + BEAST_EXPECT(!ref.isZeroAtScale(refScale)); + + // A much larger value: certainly not zero at this scale. + STAmount const large{usd, STAmount::kMIN_VALUE, 0}; // 1e15 IOU + BEAST_EXPECT(!large.isZeroAtScale(refScale)); + + // When scale equals the value's own exponent, roundToScale + // short-circuits and returns the value unchanged. + BEAST_EXPECT(!subUlp.isZeroAtScale(subUlp.exponent())); + BEAST_EXPECT(!oneUlp.isZeroAtScale(oneUlp.exponent())); + } + + // XRP is integral — roundToScale short-circuits, value is preserved. + { + STAmount const xrp{XRPAmount{1}}; + BEAST_EXPECT(!xrp.isZeroAtScale(-14)); + BEAST_EXPECT(!xrp.isZeroAtScale(0)); + + STAmount const xrpZero{XRPAmount{0}}; + BEAST_EXPECT(xrpZero.isZeroAtScale(-14)); + } + + // MPT is integral — same short-circuit behaviour as XRP. + { + MPTIssue const mpt{makeMptID(1, AccountID(0x4985601))}; + STAmount const mptAmt{mpt, 1}; + BEAST_EXPECT(!mptAmt.isZeroAtScale(0)); + BEAST_EXPECT(!mptAmt.isZeroAtScale(-14)); + + STAmount const mptZero{mpt, 0}; + BEAST_EXPECT(mptZero.isZeroAtScale(0)); + } + } + + //-------------------------------------------------------------------------- + void run() override { @@ -1227,6 +1293,7 @@ public: testCanSubtractXRP(); testCanSubtractIOU(); testCanSubtractMPT(); + testIsZeroAtScale(); } };