Compare commits

...

199 Commits

Author SHA1 Message Date
Ed Hennis
c8c45b343e Add some tests for Numbers with large exponents 2026-06-30 19:51:10 -04:00
Ed Hennis
18de60c399 clang-tidy: missing headers 2026-06-30 18:11:39 -04:00
Ed Hennis
16f286553e Merge branch 'ximinez/number-round-maxrep-down' into ximinez/number-round-maxrep 2026-06-30 12:35:23 -04:00
Ed Hennis
ea6f08d301 Merge branch 'develop' into ximinez/number-round-maxrep-down 2026-06-30 12:26:31 -04:00
Ed Hennis
93f59bd707 Merge branch 'ximinez/number-round-maxrep-down' into ximinez/number-round-maxrep 2026-06-29 19:47:41 -04:00
Ed Hennis
9529fd97f6 Move MantissaScale NOLINTBEGIN/END to around the enum 2026-06-29 19:09:20 -04:00
Ed Hennis
72bc522fa7 Revert "fixup! Review feedback from @gregtatcam, plus a few extras"
This reverts commit 000cf9f489.
2026-06-29 19:08:03 -04:00
Ed Hennis
027c6f6e57 Merge branch 'develop' into ximinez/number-round-maxrep-down 2026-06-29 11:18:23 -04:00
Ed Hennis
c425c4ee0c Merge branch 'ximinez/number-round-maxrep-down' into ximinez/number-round-maxrep 2026-06-29 11:14:47 -04:00
Ed Hennis
394ea6b69c Merge branch 'develop' into ximinez/number-round-maxrep-down 2026-06-26 18:03:40 -04:00
Ed Hennis
4195c5b1ff Update include/xrpl/basics/Number.h
Bad constexpr

Co-authored-by: xrplf-ai-reviewer[bot] <266832837+xrplf-ai-reviewer[bot]@users.noreply.github.com>
2026-06-26 18:03:07 -04:00
Ed Hennis
000cf9f489 fixup! Review feedback from @gregtatcam, plus a few extras 2026-06-26 17:42:38 -04:00
Ed Hennis
4a1851ab47 Review feedback from @gregtatcam and AI
- Simplify the before and after comparison in numberFromJson
- Update tests
2026-06-26 17:37:24 -04:00
Ed Hennis
78c5471af6 Review feedback: STNumber parsing
- In numberFromJson(), if the result is 0, only the mantissa has to
  match, because there are many valid ways to represent 0.
- Update several broken tests that I missed earlier.
2026-06-26 14:08:53 -04:00
Ed Hennis
573bf2abe3 fixup! One more test case to show that big integers don't include "e0" 2026-06-26 10:36:00 -04:00
Ed Hennis
24ab150bdf One more test case to show that big integers don't include "e0" 2026-06-25 22:43:07 -04:00
Ed Hennis
78884b8b00 Do the Number parsing verification for all cases 2026-06-25 22:41:03 -04:00
Ed Hennis
f730af6e05 Review feedback from @gregtatcam: json string number parsing
- Change the parsing in numberFromJson so that if the resulting Number
  is not the same as the parsed NumberParts, then throw, because
  unexpected rounding occurred
- Add modified suggested test cases, and modify some existing ones,
  which test this behavior.
2026-06-25 22:35:25 -04:00
Ed Hennis
f0d7dd0117 Merge branch 'ximinez/number-round-maxrep-down' into ximinez/number-round-maxrep 2026-06-25 19:12:17 -04:00
Ed Hennis
2a5b1b4e17 Review feedback from @gregtatcam, plus a few extras
- Rename MantissaRange::Get to MantissaRange::Access. The name doesn't
  really matter, but since the intent of the class is to control access
  to one function, this works.
- Exclude the MantissaScale enum from linting for
  readability-enum-initial-value. clang-tidy was complaining that
  "error: initial values in enum 'xrpl::MantissaRange::MantissaScale'
  are not consistent, consider explicit initialization of all, none or
  only the first enumerator", but I don't care. I just need Large to
  match the last value.
- Add a static_assert in setCurrentTransactionRules, so that if another
  MantissaScale is added (and Large is properly updated), the engineer
  won't forget to add a case for it there.
2026-06-25 19:02:53 -04:00
Ed Hennis
ba44e98e0e Merge branch 'ximinez/number-round-maxrep-down' into ximinez/number-round-maxrep 2026-06-25 15:33:12 -04:00
Ed Hennis
4ff398ba2d Address AI review feedback
- Mostly just clarifying comments and reorganizing functions.
2026-06-25 15:31:51 -04:00
Ed Hennis
cc4c06cf86 Address AI review feedback: mantissa assertion, test resiliency
- Move code around and add comments in `bringIntoRange` to make it
  clearer that mantissas should not be 0, but fall back gracefully in
  non-debug builds if they are.
- Make a test that checks for Large330 future-proof if more scales are
  added later.
2026-06-25 14:26:59 -04:00
Ed Hennis
6906f47349 Apply suggestions from AI code review: typos
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-25 14:25:32 -04:00
Ed Hennis
d2c5d95032 Merge branch 'ximinez/number-round-maxrep-down' into ximinez/number-round-maxrep 2026-06-25 11:43:34 -04:00
Ed Hennis
6aa9031848 Merge branch 'develop' into ximinez/number-round-maxrep-down 2026-06-25 11:43:30 -04:00
Ed Hennis
817f541c69 Merge branch 'ximinez/number-round-maxrep-down' into ximinez/number-round-maxrep 2026-06-23 11:10:04 -04:00
Ed Hennis
e090083dc7 Merge branch 'develop' into ximinez/number-round-maxrep-down 2026-06-23 11:09:59 -04:00
Ed Hennis
f3849fc2d2 Merge branch 'ximinez/number-round-maxrep-down' into ximinez/number-round-maxrep 2026-06-22 16:42:02 -04:00
Ed Hennis
2c074d03c7 Merge branch 'develop' into ximinez/number-round-maxrep-down 2026-06-22 16:41:58 -04:00
Ed Hennis
08736639c5 Restore an incorrectly removed const 2026-06-22 11:33:41 -04:00
Ed Hennis
aadf3c66c9 Remove some overly verbose unit test log messages. 2026-06-18 21:05:03 -04:00
Ed Hennis
7cb224e50f Update some comments, and const correctness 2026-06-18 20:59:20 -04:00
Ed Hennis
4124a1dd2d Revert "Remove changes to Number.cpp"
This reverts commit 8250aa2375.
2026-06-18 20:16:39 -04:00
Ed Hennis
8250aa2375 Remove changes to Number.cpp
- Show that old behavior is not affected, and that the new tests fail
  without them.
2026-06-18 20:15:19 -04:00
Ed Hennis
da396070ce Address review feedback from @TimothyBanks and @gregtatcam
- Remove unnecessary if constexpr check
- Update scaling static_assert
- Remove unnecessary rounding logic from Number::Guard::doRound()
- Handle fractional rounding between kMaxRep and kMaxRepUp
2026-06-18 17:57:21 -04:00
Ed Hennis
6179b054c9 Merge remote-tracking branch 'XRPLF/ximinez/number-round-maxrep-down' into ximinez/number-round-maxrep
* XRPLF/ximinez/number-round-maxrep-down:
  clang-tidy: Remove unused header
2026-06-18 13:18:07 -04:00
Ed Hennis
d373cb5db8 clang-tidy: Remove unused header 2026-06-18 13:16:16 -04:00
Ed Hennis
39d85076c4 Merge branch 'ximinez/number-round-maxrep-down' into ximinez/number-round-maxrep 2026-06-17 21:50:55 -04:00
Ed Hennis
14a43cfc35 Merge branch 'develop' into ximinez/number-round-maxrep-down 2026-06-17 21:46:24 -04:00
Ed Hennis
6504042b8d Review feedback from @TimothyBanks and @gregtatcam
- Get rid of MantissaRange::getRanges() function. Rewrite
  getMantissaScale to use all constexpr values.
- In Number::Guard::round(), document the "intentional" shadowing of
  "mode", and simplify one of the checks. Add an assert.
2026-06-17 21:45:56 -04:00
Ed Hennis
069fc99b6a Merge branch 'ximinez/number-round-maxrep-down' into ximinez/number-round-maxrep 2026-06-17 15:01:40 -04:00
Ed Hennis
cebd4ecf15 Merge branch 'develop' into ximinez/number-round-maxrep-down 2026-06-17 15:01:35 -04:00
Ed Hennis
b16a761efc Merge branch 'ximinez/number-round-maxrep-down' into ximinez/number-round-maxrep 2026-06-16 18:56:17 -04:00
Ed Hennis
dbe34b4a29 Merge branch 'develop' into ximinez/number-round-maxrep-down 2026-06-16 18:56:13 -04:00
Ed Hennis
554bb62a65 Merge branch 'develop' into ximinez/number-round-maxrep-down 2026-06-15 20:31:43 -04:00
Ed Hennis
2f59cb0372 Merge branch 'ximinez/number-round-maxrep-down' into ximinez/number-round-maxrep 2026-06-15 15:28:59 -04:00
Ed Hennis
6bc267c112 Merge branch 'develop' into ximinez/number-round-maxrep-down 2026-06-15 15:28:55 -04:00
Ed Hennis
51781c4cae Merge remote-tracking branch 'XRPLF/ximinez/number-round-maxrep-down' into ximinez/number-round-maxrep
* XRPLF/ximinez/number-round-maxrep-down:
  Clean up some comments, coverage exclusions, and an assert
2026-06-15 15:01:47 -04:00
Ed Hennis
8f51891631 Use constexpr in one more place 2026-06-15 14:57:39 -04:00
Ed Hennis
a8f4d790bd Clean up some comments, coverage exclusions, and an assert 2026-06-15 14:05:44 -04:00
Ed Hennis
685367248c Merge branch 'ximinez/number-round-maxrep-down' into ximinez/number-round-maxrep 2026-06-15 12:12:15 -04:00
Ed Hennis
bebddb0e3e Merge branch 'develop' into ximinez/number-round-maxrep-down 2026-06-15 12:12:10 -04:00
Ed Hennis
22a7f5bb49 Fix assertion typo src/libxrpl/basics/Number.cpp
Co-authored-by: xrplf-ai-reviewer[bot] <266832837+xrplf-ai-reviewer[bot]@users.noreply.github.com>
2026-06-12 19:44:08 -04:00
Ed Hennis
a63bab2a5e Cleanups: Comments, variable names, one test case
- "ToNearest and Downward behavior Small" test case
2026-06-12 19:38:07 -04:00
Ed Hennis
095e14193e Apply suggestions from AI code review
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-12 19:29:01 -04:00
Ed Hennis
305977058b Merge remote-tracking branch 'XRPLF/ximinez/number-round-maxrep-down' into ximinez/number-round-maxrep
* XRPLF/ximinez/number-round-maxrep-down:
  Apply suggestions from AI code review
2026-06-12 19:08:04 -04:00
Ed Hennis
b30a70bbf8 Apply suggestions from AI code review
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-12 18:44:00 -04:00
Ed Hennis
1ef0c5ab44 Merge branch 'ximinez/number-round-maxrep-down' into ximinez/number-round-maxrep 2026-06-11 23:02:58 -04:00
Ed Hennis
463fb88cd8 Apply suggestions from AI code review
Co-authored-by: xrplf-ai-reviewer[bot] <266832837+xrplf-ai-reviewer[bot]@users.noreply.github.com>
2026-06-11 23:02:47 -04:00
Ed Hennis
353b3ae07d fixup! AI: Remove extranenous enum copy 2026-06-11 23:00:08 -04:00
Ed Hennis
b2790e2f50 AI: Remove extranenous enum copy
Co-authored-by: xrplf-ai-reviewer[bot] <266832837+xrplf-ai-reviewer[bot]@users.noreply.github.com>
2026-06-11 22:57:31 -04:00
Ed Hennis
f8f899dd8a Merge remote-tracking branch 'XRPLF/ximinez/number-round-maxrep-down' into ximinez/number-round-maxrep
* XRPLF/ximinez/number-round-maxrep-down:
  Change placeholder "fixNumberStuff" to "fixCleanup3_3_0"
  test: Add null check unit test for `Oracle::aggregatePrice` (7306)
  ci: Patch conan recipe for Nix to be able to use on macOS (7532)
  ci: Run sanitizers on release builds too (7527)
  fix: Correct hybrid offer deletion on credential expiry (6843)
  ci: Make sanitizer flags lists in the profile, not a string (7449)
  ci: Make configurations launch on certain event types (7447)
  fix: Add [[maybe_unused]] to fix320Enabled for assert=OFF builds (7446)
  ci: Add `gh` and `file` to nix packages (7444)
  fix: Disable transaction invariants (7409)
  perf: Dispatch "hasInvalidAmount()" on type tag instead of dynamic_cast (7402)
  refactor: Retire fixUniversalNumber amendment (5962)
  test: Do not create data directory for memory databases (7323)
  ci: Launch upload-conan-deps on profile change (7442)
2026-06-11 22:55:31 -04:00
Ed Hennis
520efde120 Merge commit '5703ca5' into ximinez/number-round-maxrep
* commit '5703ca5': (23 commits)
  Number improvements
  test: Add more Number edge case tests, showing failures
  Also fix local 3_2_0 variable names
  Future proofing: Rename Large and Enabled to Large330 and Enabled330
  clang-tidy: rename MantissaScale enums from "3_2_0" to "320"
  Clean up the "New" names
  Update to use a new amendment, since this PR will not be part of 3.2.0
  clang-tidy: Guard public member variable names; Missing include
  Clean up tests
  Reorganize the subtraction tests
  Fix formatting, add an assert
  Revert "Rollback Number class changes; show the fix works without side effects"
  Rollback Number class changes; show the fix works without side effects
  Include rounding in failed unit tests
  Improve comment descriptions
  Rework subtraction rounding (again) for more accuracy
  refactor: Construct Number::Guard from MantissaRange or relevant fields
  clang-tidy: template param names, const correctness, braces
  Remove the kMaxRep+1 rounding tests
  Improve accuracy of Number::operator+=
  ...
2026-06-11 22:55:18 -04:00
Ed Hennis
308e00f590 Merge commit 'b087545' into ximinez/number-round-maxrep
* commit 'b087545':
  Number improvements
  test: Add more Number edge case tests, showing failures
2026-06-11 22:48:22 -04:00
Ed Hennis
29d017c2a3 Change placeholder "fixNumberStuff" to "fixCleanup3_3_0" 2026-06-11 22:02:50 -04:00
Ed Hennis
764be7f20f Merge remote-tracking branch 'XRPLF/develop' into ximinez/number-round-maxrep-down
* XRPLF/develop:
  test: Add null check unit test for `Oracle::aggregatePrice` (7306)
  ci: Patch conan recipe for Nix to be able to use on macOS (7532)
  ci: Run sanitizers on release builds too (7527)
  fix: Correct hybrid offer deletion on credential expiry (6843)
  ci: Make sanitizer flags lists in the profile, not a string (7449)
  ci: Make configurations launch on certain event types (7447)
  fix: Add [[maybe_unused]] to fix320Enabled for assert=OFF builds (7446)
  ci: Add `gh` and `file` to nix packages (7444)
  fix: Disable transaction invariants (7409)
  perf: Dispatch "hasInvalidAmount()" on type tag instead of dynamic_cast (7402)
  refactor: Retire fixUniversalNumber amendment (5962)
  test: Do not create data directory for memory databases (7323)
  ci: Launch upload-conan-deps on profile change (7442)
2026-06-11 19:39:47 -04:00
Ed Hennis
5703ca527f Number improvements
- Expand documentation.
- Refactor Number::Guard::round() to simplify.
- Set the Guard sign correctly in += for numbers with the same exponent.
  - Only really relevant if both values are negative.
- In +=, when needed, expand one mantissa to a size large enough to have
  a few extra digits, which can be used to determine rounding.
  - If the exponents are still different, trim the other mantissa as
    before until the exponents match.
  - For subtraction (where the values' signs are different), pop digits
    out of the Guard as necessary, but go far enough to have a few extra
    digits again for rounding later.
  - Finally, don't discard any "leftover" digits in the Guard when
    normalizing, to avoid the 0.5....nnn problem.
2026-06-11 19:38:21 -04:00
Ed Hennis
a1cfa89e15 test: Add more Number edge case tests, showing failures
- NumberAddDirectedSignWrong
  - Addition of two negative numbers with the same exponent rounds
    ToNearest in the wrong direction.
  - Also include unit test cases with same exponent, and mixed signs.
    - No rounding issues in any combination, because the exponent can't
      change.
- NumberAddToNearestPicksFarther
  - In scenarios where the two operands have different signs, and
    significantly different exponents, you can end up in a situation
    where the rounding looks like 0.5, which may round down to even, but
    is actually 0.5....nnn, which should always round up, you get the
    wrong result.
2026-06-11 19:38:21 -04:00
Ed Hennis
318c2c2dd3 Also fix local 3_2_0 variable names 2026-06-11 19:38:21 -04:00
Ed Hennis
5c62c15ad8 Future proofing: Rename Large and Enabled to Large330 and Enabled330
- If more fixes need to be made in the future, they can be added after,
  instead of needing to do the "rename dance", I had to do with this PR.
2026-06-11 19:38:20 -04:00
Ed Hennis
772e0c30f7 clang-tidy: rename MantissaScale enums from "3_2_0" to "320" 2026-06-11 19:38:20 -04:00
Ed Hennis
182ca1c12f Clean up the "New" names 2026-06-11 19:38:20 -04:00
Ed Hennis
2e97056b40 Update to use a new amendment, since this PR will not be part of 3.2.0
- This requires creating yet another MantissaScale, and CuspRoundingFix
  option.
2026-06-11 19:38:03 -04:00
Ed Hennis
693e9015ab clang-tidy: Guard public member variable names; Missing include 2026-06-11 19:38:01 -04:00
Ed Hennis
1162ccf7f4 Clean up tests 2026-06-11 19:37:57 -04:00
Ed Hennis
07ae5fa867 Reorganize the subtraction tests 2026-06-11 19:37:56 -04:00
Ed Hennis
6cc45297d7 Fix formatting, add an assert 2026-06-11 19:37:56 -04:00
Ed Hennis
b263f442be Revert "Rollback Number class changes; show the fix works without side effects"
This reverts commit 8743be8eae.
2026-06-11 19:37:56 -04:00
Ed Hennis
7191574499 Rollback Number class changes; show the fix works without side effects 2026-06-11 19:37:56 -04:00
Ed Hennis
e77b154edc Include rounding in failed unit tests 2026-06-11 19:37:56 -04:00
Ed Hennis
184f936362 Improve comment descriptions 2026-06-11 19:37:55 -04:00
Ed Hennis
64cb53629d Rework subtraction rounding (again) for more accuracy
- Go back to the old method of computing the mantissa, but when post
  processing, expand the mantissa to slightly larger than maxMantissa,
  then in doRoundDown, if the result is not exact, subtract one.
  Finally, let doNormalize figure out the rounding of the result.
2026-06-11 19:37:55 -04:00
Ed Hennis
8ca90e7d01 refactor: Construct Number::Guard from MantissaRange or relevant fields
- Simplifies the function signatures in Guard, because it doesn't need
  to have those values passed in constantly.
- Also simplifies some of the functions because they don't need to store
  values just to pass them to Guard functions.
2026-06-11 19:37:55 -04:00
Ed Hennis
0a24023797 clang-tidy: template param names, const correctness, braces 2026-06-11 19:37:55 -04:00
Ed Hennis
73bd964917 Remove the kMaxRep+1 rounding tests 2026-06-11 19:37:54 -04:00
Ed Hennis
48e0ca72b0 Improve accuracy of Number::operator+=
- Use more of the available range of the uint128 operands.
- Also refactor Number::Guard::round() to return an enum.
2026-06-11 19:37:54 -04:00
Ed Hennis
6d89fbef7a Experimental: Scale addition operands up to preserve accuracy 2026-06-11 19:37:54 -04:00
Ed Hennis
b624b2eeae Include upward, write tests based on "expected" behavior 2026-06-11 19:37:54 -04:00
Ed Hennis
f255ef6214 test: Add another rounding unit test 2026-06-11 19:37:53 -04:00
Ed Hennis
b087545755 Number improvements
- Expand documentation.
- Refactor Number::Guard::round() to simplify.
- Set the Guard sign correctly in += for numbers with the same exponent.
  - Only really relevant if both values are negative.
- In +=, when needed, expand one mantissa to a size large enough to have
  a few extra digits, which can be used to determine rounding.
  - If the exponents are still different, trim the other mantissa as
    before until the exponents match.
  - For subtraction (where the values' signs are different), pop digits
    out of the Guard as necessary, but go far enough to have a few extra
    digits again for rounding later.
  - Finally, don't discard any "leftover" digits in the Guard when
    normalizing, to avoid the 0.5....nnn problem.
2026-06-11 18:59:59 -04:00
Ed Hennis
1bb1d71565 test: Add more Number edge case tests, showing failures
- NumberAddDirectedSignWrong
  - Addition of two negative numbers with the same exponent rounds
    ToNearest in the wrong direction.
  - Also include unit test cases with same exponent, and mixed signs.
    - No rounding issues in any combination, because the exponent can't
      change.
- NumberAddToNearestPicksFarther
  - In scenarios where the two operands have different signs, and
    significantly different exponents, you can end up in a situation
    where the rounding looks like 0.5, which may round down to even, but
    is actually 0.5....nnn, which should always round up, you get the
    wrong result.
2026-06-11 18:27:25 -04:00
Ed Hennis
d8e03a8018 Update more names due to prior merge 2026-06-10 13:26:34 -04:00
Ed Hennis
e4dafa3171 Update names due to prior merge 2026-06-09 19:14:40 -04:00
Ed Hennis
1477747c37 Merge remote-tracking branch 'XRPLF/ximinez/number-round-maxrep-down' into ximinez/number-round-maxrep
* XRPLF/ximinez/number-round-maxrep-down:
  Also fix local 3_2_0 variable names
  Future proofing: Rename Large and Enabled to Large330 and Enabled330
  clang-tidy: rename MantissaScale enums from "3_2_0" to "320"
2026-06-09 19:09:39 -04:00
Ed Hennis
902fbdc3ad Also fix local 3_2_0 variable names 2026-06-09 19:06:38 -04:00
Ed Hennis
48d1c70b6c Future proofing: Rename Large and Enabled to Large330 and Enabled330
- If more fixes need to be made in the future, they can be added after,
  instead of needing to do the "rename dance", I had to do with this PR.
2026-06-09 18:44:46 -04:00
Ed Hennis
66ff72f56d clang-tidy: rename MantissaScale enums from "3_2_0" to "320" 2026-06-09 18:38:03 -04:00
Ed Hennis
4139ebbe6b Fix issues introduced by prior merge, and new MantissaRange 2026-06-09 18:31:59 -04:00
Ed Hennis
9924b6ce65 Merge remote-tracking branch 'XRPLF/ximinez/number-round-maxrep-down' into ximinez/number-round-maxrep
* XRPLF/ximinez/number-round-maxrep-down:
  Clean up the "New" names
  Update to use a new amendment, since this PR will not be part of 3.2.0
  fix: Fix Number comparison operator (7406)
  feat: Use C++ 23 standard (7431)
  refactor: Introduce XRPL_ASSERT_IF for amendment-gated assertions (7378)
  refactor: Change config section and key string literals into constants (7095)
  refactor: Use `std::move` and `std::string_view` where possible (7424)
  refactor: Use const function arguments where possible (7423)
  ci: Use XRPLF/actions build-multiarch-image workflow (7428)
  ci: Use new packaging images and don't cancel develop builds (7417)
  ci: [DEPENDABOT] bump codecov/codecov-action from 6.0.1 to 7.0.0 (7426)
2026-06-09 18:08:04 -04:00
Ed Hennis
63f1a40b77 Clean up the "New" names 2026-06-09 17:06:27 -04:00
Ed Hennis
5bafceefaf Merge remote-tracking branch 'XRPLF/develop' into ximinez/number-round-maxrep-down
* XRPLF/develop:
  fix: Fix Number comparison operator (7406)
  feat: Use C++ 23 standard (7431)
  refactor: Introduce XRPL_ASSERT_IF for amendment-gated assertions (7378)
2026-06-09 16:51:38 -04:00
Ed Hennis
44f3011b9b Merge remote-tracking branch 'XRPLF/develop' into ximinez/number-round-maxrep-down
* XRPLF/develop:
  refactor: Change config section and key string literals into constants (7095)
  refactor: Use `std::move` and `std::string_view` where possible (7424)
  refactor: Use const function arguments where possible (7423)
  ci: Use XRPLF/actions build-multiarch-image workflow (7428)
  ci: Use new packaging images and don't cancel develop builds (7417)
  ci: [DEPENDABOT] bump codecov/codecov-action from 6.0.1 to 7.0.0 (7426)
2026-06-09 16:45:02 -04:00
Ed Hennis
d9c63cb9bc Update to use a new amendment, since this PR will not be part of 3.2.0
- This requires creating yet another MantissaScale, and CuspRoundingFix
  option.
2026-06-09 16:44:13 -04:00
Ed Hennis
12741a247a Cleanups, mostly from previous merge, plus a few outdated comments 2026-06-08 20:21:38 -04:00
Ed Hennis
85980aec6a Fix broken unit tests 2026-06-08 19:44:44 -04:00
Ed Hennis
6da1f224f8 Merge remote-tracking branch 'XRPLF/ximinez/number-round-maxrep-down' into ximinez/number-round-maxrep
* XRPLF/ximinez/number-round-maxrep-down:
  clang-tidy: Guard public member variable names; Missing include
  Clean up tests
2026-06-08 19:43:20 -04:00
Ed Hennis
5bccfe2b6d clang-tidy: Guard public member variable names; Missing include 2026-06-08 19:03:53 -04:00
Ed Hennis
308e46d0da Clean up tests 2026-06-08 19:00:24 -04:00
Ed Hennis
f9183aa34a Merge remote-tracking branch 'XRPLF/ximinez/number-round-maxrep-down' into ximinez/number-round-maxrep
* XRPLF/ximinez/number-round-maxrep-down:
  Reorganize the subtraction tests
2026-06-07 16:21:45 -04:00
Ed Hennis
fe64b99147 Reorganize the subtraction tests 2026-06-06 16:20:51 -04:00
Ed Hennis
121786aada Merge remote-tracking branch 'XRPLF/ximinez/number-round-maxrep-down' into ximinez/number-round-maxrep
* XRPLF/ximinez/number-round-maxrep-down:
  Fix formatting, add an assert
2026-06-06 16:02:59 -04:00
Ed Hennis
d7ce0e2dd3 Fix formatting, add an assert 2026-06-06 15:38:29 -04:00
Ed Hennis
b2cea736d0 Merge branch 'ximinez/number-round-maxrep-down' into ximinez/number-round-maxrep
* ximinez/number-round-maxrep-down:
  Revert "Rollback Number class changes; show the fix works without side effects"
  Rollback Number class changes; show the fix works without side effects
  Include rounding in failed unit tests
  Improve comment descriptions
  Rework subtraction rounding (again) for more accuracy
  build: Create single test binary xrpl_tests (7327)
  ci: [DEPENDABOT] bump actions/checkout from 6.0.2 to 6.0.3 (7414)
  ci: Refactor build-related nix / docker / workflows (7408)
  refactor: Construct Number::Guard from MantissaRange or relevant fields
  ci: Use multiple directories in dependabot config (7413)
  ci: Update clang-tidy to nix-based v22 (7412)
  clang-tidy: template param names, const correctness, braces
2026-06-06 15:25:56 -04:00
Ed Hennis
c165af497e Revert "Rollback Number class changes; show the fix works without side effects"
This reverts commit 8743be8eae.
2026-06-06 14:35:35 -04:00
Ed Hennis
8743be8eae Rollback Number class changes; show the fix works without side effects 2026-06-06 14:34:31 -04:00
Ed Hennis
3a5c85067a Include rounding in failed unit tests 2026-06-06 14:33:31 -04:00
Ed Hennis
2f701121b4 Merge remote-tracking branch 'upstream/develop' into ximinez/number-round-maxrep-down
* upstream/develop:
  build: Create single test binary xrpl_tests (7327)
  ci: [DEPENDABOT] bump actions/checkout from 6.0.2 to 6.0.3 (7414)
  ci: Refactor build-related nix / docker / workflows (7408)
  ci: Use multiple directories in dependabot config (7413)
  ci: Update clang-tidy to nix-based v22 (7412)
2026-06-06 14:04:24 -04:00
Ed Hennis
961ac6671e Improve comment descriptions 2026-06-06 13:09:52 -04:00
Ed Hennis
012c67a7eb Rework subtraction rounding (again) for more accuracy
- Go back to the old method of computing the mantissa, but when post
  processing, expand the mantissa to slightly larger than maxMantissa,
  then in doRoundDown, if the result is not exact, subtract one.
  Finally, let doNormalize figure out the rounding of the result.
2026-06-06 01:04:26 -04:00
Ed Hennis
74c66d0944 refactor: Construct Number::Guard from MantissaRange or relevant fields
- Simplifies the function signatures in Guard, because it doesn't need
  to have those values passed in constantly.
- Also simplifies some of the functions because they don't need to store
  values just to pass them to Guard functions.
2026-06-05 12:06:41 -04:00
Ed Hennis
f1bb4ded21 clang-tidy: template param names, const correctness, braces 2026-06-04 20:05:53 -04:00
Ed Hennis
e1295d1ce8 Merge branch 'ximinez/number-round-maxrep-down' into ximinez/number-round-maxrep 2026-06-04 19:03:43 -04:00
Ed Hennis
aa8888732e Merge branch 'develop' into ximinez/number-round-maxrep-down 2026-06-04 19:03:32 -04:00
Ed Hennis
50c0d9f2b0 Handle a whole bunch of edge cases
- Add more tests
2026-06-04 18:53:50 -04:00
Ed Hennis
b5574baa36 Add line numbers to Number::to_string test 2026-06-04 14:40:39 -04:00
Ed Hennis
015d9a6cb9 Round mantissas between kMaxRep and kMaxRepUp
- Treat values in between kMaxRep (2^63-1) and kMaxRepUp (((kMaxRep
  / 10) + 1) * 10, which is the next multiple of 10 above kMaxRep) as if
  those values were sequential, and values in between were "fractional".
- This results in values above the midpoint rounding up to kMaxRepUp,
  and below the midpoint to kMaxRep when rounding to nearest. Other
  rounding modes act along the same lines.
- Also refactor "Number::Guard::round()` to return an enum making it
  clearer what's going on.
2026-06-04 14:14:01 -04:00
Ed Hennis
51902cd6b4 Revert "Remove the kMaxRep+1 rounding tests"
This reverts commit c84939ccea.
2026-06-02 16:03:08 -04:00
Ed Hennis
c84939ccea Remove the kMaxRep+1 rounding tests 2026-06-02 15:59:26 -04:00
Ed Hennis
9e8c3caef4 Improve accuracy of Number::operator+=
- Use more of the available range of the uint128 operands.
- Also refactor Number::Guard::round() to return an enum.
2026-06-02 15:35:42 -04:00
Ed Hennis
35bee87909 Experimental: Scale addition operands up to preserve accuracy 2026-06-01 21:52:31 -04:00
Ed Hennis
261508a0ec Merge remote-tracking branch 'XRPLF/develop' into ximinez/number-round-maxrep-down
* XRPLF/develop:
  ci: Check binaries separately from building them (7355)
  ci: [DEPENDABOT] bump eps1lon/actions-label-merge-conflict from 3.0.3 to 3.1.0 (7375)
  refactor: Use `STLedgerEntry` type aliases instead of `std::shared_ptr` (7282)
  fix: Adjust xrpld systemd service and update timer (7374)
  release: Bump version to 3.2.0-rc3 (7371)
  fix: Pin overpayment principal reduction to exact on-grid value (7360)
  fix: Improve upward rounding edge cases for Number::operator/= (7328)
  refactor: Revert "perf: Remove unnecessary caches (5439)" (7359)
  fix: Add zero domainID check for permissionedDomain (7362)
2026-06-01 15:33:34 -04:00
Ed Hennis
9f872f2179 Include upward, write tests based on "expected" behavior 2026-05-29 19:46:50 -04:00
Ed Hennis
d1af39d9dd test: Add another rounding unit test 2026-05-29 19:03:34 -04:00
Ed Hennis
f6a26ca34f CI feedback: Add test cases covering other rounding modes
- Downward with a negative result, and ToNearest with a remainder
  slightly larger than 0.5.
2026-05-29 18:54:08 -04:00
Ed Hennis
c0569037f8 Apply suggestions from code review
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-29 16:34:51 -04:00
Ed Hennis
be9ae88d48 Accept AI suggestion
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-28 23:38:54 -04:00
Ed Hennis
cd21d74538 Update the testUpwardRoundsDown test to run under all scales
- Demonstrates the incorrect "before" behavior
2026-05-28 19:58:52 -04:00
Ed Hennis
2fdfd2b420 Review feedback from Tapanito and AI
- Add missing headers.
- Improve code coverage exclusions.
- Clean up several variable names.
- Improve explanatory comments.
- Remove the switch statement from MantissaRange::getMin. Change it to
  a straight power of ten lookup.
2026-05-28 18:41:19 -04:00
Ed Hennis
06a3f76ccd Skip clang-tidy false positive: misc-include-cleaner 2026-05-28 17:47:41 -04:00
Ed Hennis
dadf4d737d Merge branch 'develop' into ximinez/number-division-accuracy 2026-05-28 17:46:40 -04:00
Ed Hennis
7b66b42713 Merge branch 'develop' into ximinez/number-division-accuracy 2026-05-27 17:00:59 -04:00
Ed Hennis
18ac8a0583 Merge branch 'develop' into ximinez/number-division-accuracy 2026-05-27 15:18:17 -04:00
Ed Hennis
de2efa5cb9 Remove TMax entirely from normalizeToRange; check type matching directly 2026-05-27 13:06:52 -04:00
Ed Hennis
8dcd88e83c Tweak how the denominator is handled in division
- Removes one int64 to 128 conversion
2026-05-27 12:34:49 -04:00
Ed Hennis
5333422402 Merge remote-tracking branch 'XRPLF/develop' into ximinez/number-division-accuracy
* XRPLF/develop:
  fix: Fix a rounding error at the `Number::maxRep` cusp (7051)
2026-05-27 12:17:30 -04:00
Ed Hennis
4ec049e727 Minor fixes: missing include, variable init, typo 2026-05-27 12:09:13 -04:00
Ed Hennis
ae9c72bb7c Test optimization: Number_test::pow10 2026-05-27 00:36:38 -04:00
Ed Hennis
5abecb9fcb Significant rewrite
- Simplify Number::operator/= to use more constexpr values, and fewer
  variations.
  - Most significantly, rounding up doesn't need more precision, it only
    needs to know if there's a remainder after the current precision work
    is done. Tracked similarly Guard::xbit_.
- Build a constexpr lookup array for powers of 10. Only a handful of
  values are used, but since it's built at compile time, and constexpr,
  unused values do not affect memory or performance.
2026-05-27 00:07:48 -04:00
Ed Hennis
12670b0c3f Merge branch 'ximinez/number-fix-maxrepcusp' into ximinez/number-division-accuracy 2026-05-26 19:21:05 -04:00
Ed Hennis
1e7876a03c Merge branch 'develop' into ximinez/number-fix-maxrepcusp 2026-05-26 19:21:01 -04:00
Ed Hennis
e851e80de0 Merge branch 'ximinez/number-fix-maxrepcusp' into ximinez/number-division-accuracy 2026-05-26 16:56:47 -04:00
Ed Hennis
a963035f76 Merge branch 'develop' into ximinez/number-fix-maxrepcusp 2026-05-26 16:56:43 -04:00
Ed Hennis
8ab904de57 Merge branch 'ximinez/number-fix-maxrepcusp' into ximinez/number-division-accuracy 2026-05-26 16:01:44 -04:00
Ed Hennis
100ec464d9 Merge branch 'develop' into ximinez/number-fix-maxrepcusp 2026-05-26 16:01:40 -04:00
Ed Hennis
e89e6f50e8 Merge remote-tracking branch 'XRPLF/ximinez/number-fix-maxrepcusp' into ximinez/number-division-accuracy
* XRPLF/ximinez/number-fix-maxrepcusp:
  clang-tidy: implicit bool conversion
  Address some AI review feedback: predeclare, include, format, comment
  fix: Fix `VaultInvariant` and `VaultDeposit` precision bugs at IOU scale boundaries (7272)
  ci: Add clang to nix images (7308)
  fix: Include management-fee delta in doOverpayment assertion (7039)
  fix: Fix clang-tidy pre-commit hook to locate compile_commands.json from repo root (7325)
  fix: Use consistent scale for `debtTotal` (7093)
  fix: Skip deleted book directories and non-root modifications in `ValidBookDirectory` invariant (7312)
  fix: Address review feedback on FD/handle guarding (5823 follow-up) (7310)
  fix: Fix non-canonical MPT amount (7117)
2026-05-26 15:53:36 -04:00
Ed Hennis
27456fa439 Use the local range instead of calling a function 2026-05-26 15:52:25 -04:00
Ed Hennis
d6844311c0 clang-tidy: missing header 2026-05-26 15:48:29 -04:00
Ed Hennis
fbee0349f5 clang-tidy: implicit bool conversion 2026-05-26 15:21:42 -04:00
Ed Hennis
84ca271d95 Address some AI review feedback: predeclare, include, format, comment
- Predeclare type reference in Rules.h
- Remove an unneeded include in EscrowToken_test
- Number_test will format negative BigInts correctly (unused)
- Remove an inaccurate comment
2026-05-26 13:51:06 -04:00
Ed Hennis
75dfc65f5f Merge branch 'develop' into ximinez/number-fix-maxrepcusp 2026-05-26 13:47:33 -04:00
Ed Hennis
48b1716e6f Make Number::operator/= significantly more accurate
- Prevents extreme dust rounding from getting lost, especially when
  rounding away from zero. (Upward for positive, downward for negative.)
2026-05-23 19:02:03 +01:00
Ed Hennis
4ab886bcbc Merge branch 'develop' into ximinez/number-fix-maxrepcusp 2026-05-22 17:56:15 -04:00
Ed Hennis
7f64c337d8 Merge branch 'develop' into ximinez/number-fix-maxrepcusp 2026-05-21 14:25:50 -04:00
Ed Hennis
61bdd6fb78 Merge branch 'develop' into ximinez/number-fix-maxrepcusp 2026-05-21 10:10:00 -04:00
Ed Hennis
8e06e78f11 Merge branch 'develop' into ximinez/number-fix-maxrepcusp 2026-05-21 07:11:49 -04:00
Ed Hennis
42fda85fbc Fix more AMM tests, and to not exclude fixCleanup3_2_0 2026-05-21 12:04:29 +01:00
Ed Hennis
3a4b92b050 Change the priority of the amendments for large mantissas
- Order the checks so that large mantissa is only enabled if SAV or LP
  are enabled. fixCleanup3_2_0 only enables the rounding fix.
- Fix tests, and don't exclude fixCleanup3_2_0 in AMM tests
- Also fix formatting
2026-05-21 00:53:07 +01:00
Ed Hennis
aea19df3c1 Apply suggestions from @Tapanito code review
Co-authored-by: Vito Tumas <5780819+Tapanito@users.noreply.github.com>
2026-05-20 18:46:17 -04:00
Ed Hennis
8b56749ca3 Apply suggestions from Copilot code review
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-20 18:39:46 -04:00
Ed Hennis
71cf996fc6 Review feedback from @tapanito: lambda checks condition in doRoundUp 2026-05-20 23:28:26 +01:00
Ed Hennis
4c7ea64b6c Merge branch 'develop' into ximinez/number-fix-maxrepcusp 2026-05-19 16:53:33 -04:00
Ed Hennis
c8947c6f75 Merge branch 'develop' into ximinez/number-fix-maxrepcusp 2026-05-19 10:15:10 -04:00
Ed Hennis
09ae5b719f Merge branch 'develop' into ximinez/number-fix-maxrepcusp 2026-05-19 05:15:47 -04:00
Ed Hennis
09f2d06dd4 Merge branch 'develop' into ximinez/number-fix-maxrepcusp 2026-05-15 21:32:09 -04:00
Ed Hennis
6964013941 Merge remote-tracking branch 'XRPLF/develop' into ximinez/number-fix-maxrepcusp
* XRPLF/develop:
  release: Set version to 3.3.0-b0 (7280)
  refactor: Rename static constants (7120)
  refactor: Use `isFlag` where possible instead of bitwise math (7278)
  ci: Update XRPLF/actions (7281)
2026-05-15 21:14:34 -04:00
Ed Hennis
70c6e01d7e clang-tidy: this is ridiculous 2026-05-14 21:02:49 -04:00
Ed Hennis
ddfb7ee69c clang-tidy: {} 2026-05-14 20:48:34 -04:00
Ed Hennis
b69b9242e2 fixup! clang-tidy: Avoid nested "?:" in global Rules initialization 2026-05-14 20:33:15 -04:00
Ed Hennis
cd2fcf0a5e Merge branch 'develop' into ximinez/number-fix-maxrepcusp 2026-05-14 18:25:17 -04:00
Ed Hennis
69656d6b67 clang-tidy: Avoid nested "?:" in global Rules initialization 2026-05-14 18:24:05 -04:00
Ed Hennis
46b946b22e clang-tidy: missing include 2026-05-13 22:32:37 -04:00
Ed Hennis
ae03b30f29 Merge branch 'develop' into ximinez/number-fix-maxrepcusp 2026-05-13 20:16:19 -04:00
Ed Hennis
4c7c019add Merge branch 'develop' into ximinez/number-fix-maxrepcusp 2026-05-13 12:03:35 -04:00
Ed Hennis
47f30c913d Fix broken unit tests: EscrowToken
- Some EscrowToken tests used a hard-coded list of amendments to
  determine whether to expect large mantissa logic. That ignored the
  effects of fixCleanup3_2_0, especially as applied to the previous fix
  affecting preflight, preclaim, etc.
- Add a helper function, useRulesGuards, which will return the same
  decision as createGuards and setCurrentRulesImpl. Use that helper
  function in the relevant tests.
- Also remove an #include that clang-tidy was complaining about.
2026-05-12 21:26:54 -04:00
Ed Hennis
d7d5b83f6d Merge branch 'develop' into ximinez/number-fix-maxrepcusp 2026-05-12 19:18:45 -04:00
Ed Hennis
e22938d69f AI review feedback: createGuards
- Refactor the Guard decision in withTxnType into createGuards, which
  lives in Rules.cpp. It is physically located near
  setCurrentTransactionRules, and documented to explain that changes
  need to be synchronized.
2026-05-12 18:44:05 -04:00
Ed Hennis
7c9a56ff24 Merge branch 'develop' into ximinez/number-fix-maxrepcusp 2026-05-12 16:26:09 -04:00
Ed Hennis
5a40416673 Merge branch 'develop' into ximinez/number-fix-maxrepcusp 2026-05-12 15:59:19 -04:00
Ed Hennis
30334cd1f4 Merge branch 'develop' into ximinez/number-fix-maxrepcusp 2026-05-11 13:34:11 -04:00
Ed Hennis
5558e1b522 Merge branch 'develop' into ximinez/number-fix-maxrepcusp 2026-05-07 18:10:03 -04:00
Ed Hennis
cd0f49a003 Address more nitpicky AI comments 2026-05-07 17:05:10 -04:00
Ed Hennis
22d2703ce8 What is it going to take to get clang-tidy to shut up? 2026-05-07 16:50:43 -04:00
Ed Hennis
d03274b731 Merge branch 'develop' into ximinez/number-fix-maxrepcusp 2026-05-07 14:18:28 -04:00
Ed Hennis
1b6047afe1 More clang-tidy changes: AMMExtended_test member and Number_test includes 2026-05-07 13:51:48 -04:00
Ed Hennis
175a04160d Merge branch 'develop' into ximinez/number-fix-maxrepcusp 2026-05-07 13:28:33 -04:00
Ed Hennis
b050c151f8 Fix clang-tidy issues, and more AI complaints 2026-05-06 23:26:11 -04:00
Ed Hennis
a2b21d75ce Fix AI-identified mistakes 2026-05-06 22:56:07 -04:00
Ed Hennis
b40d2a8e7d fix: Fix a rounding error at the Number::maxRep cusp
- Add helper function, doDropDigit, to wrap the common pattern:
    push(mantissa % 10);
    mantissa /= 10;
    ++exponent;
- Might have been helpful to catch this issue when developing.
2026-05-06 22:49:44 -04:00
9 changed files with 1872 additions and 530 deletions

View File

@@ -51,37 +51,43 @@ namespace detail {
* compile time. Doing it at runtime would be pretty wasteful and
* inefficient.
*/
constexpr std::size_t kInt64Digits = 20;
consteval std::array<std::uint64_t, kInt64Digits>
constexpr std::size_t kUint64Digits = 20;
[[maybe_unused]] constexpr std::size_t kUint128Digits = 39;
template <typename T, std::size_t Digits>
consteval std::array<T, Digits>
buildPowersOfTen()
{
std::array<std::uint64_t, kInt64Digits> result{};
std::array<T, Digits> result{};
std::uint64_t power = 1;
T power = 1;
std::size_t exponent = 0;
// end the loop early so it doesn't overflow;
for (; exponent < result.size() - 1; ++exponent, power *= 10)
{
result[exponent] = power;
if (power > std::numeric_limits<std::uint64_t>::max() / 10)
if (power > std::numeric_limits<T>::max() / 10)
throw std::logic_error("Power of 10 table is too big");
}
result[exponent] = power;
if (power < std::numeric_limits<std::uint64_t>::max() / 10)
throw std::logic_error("Power of 10 table is not big enough for the uint64_t type");
if (power < std::numeric_limits<T>::max() / 10)
throw std::logic_error("Power of 10 table is not big enough for the given type");
return result;
}
} // namespace detail
constexpr std::array<std::uint64_t, detail::kInt64Digits> kPowerOfTen = detail::buildPowersOfTen();
template <typename T = std::uint64_t, std::size_t Digits = detail::kUint64Digits>
constexpr std::array<T, Digits> kPowerOfTenImpl = detail::buildPowersOfTen<T, Digits>();
constexpr auto kPowerOfTen = kPowerOfTenImpl<std::uint64_t, detail::kUint64Digits>;
static_assert(kPowerOfTen[0] == 1);
static_assert(kPowerOfTen[1] == 10);
static_assert(kPowerOfTen[10] == 10'000'000'000);
static_assert(
isPowerOfTen(kPowerOfTen.back()) && *logTen(kPowerOfTen.back()) == detail::kInt64Digits - 1);
isPowerOfTen(kPowerOfTen.back()) && *logTen(kPowerOfTen.back()) == detail::kUint64Digits - 1);
/** MantissaRange defines a range for the mantissa of a normalized Number.
*
@@ -120,17 +126,37 @@ struct MantissaRange final
{
using rep = std::uint64_t;
// NOLINTBEGIN(readability-enum-initial-value)
// The values don't matter, except for Large
enum class MantissaScale {
// Small can be removed when either featureSingleAssetVault or featureLendingProtocol are
// retired
Small,
// LargeLegacy can be removed when fixCleanup3_2_0 is retired
LargeLegacy,
Large,
// Large320 can be removed when fixCleanup3_3_0 is retired
Large320,
// If Large330 is ever the only remaining "Large*" entry, it can be renamed to just "Large".
Large330,
// Large is a de-facto alias for "the latest", and is only here for backward compatibility
// in the extremely unlikely case that a downstream project made use of it. Note that
// because the behavior changed, this may still be a breaking change.
Large = Large330,
};
// NOLINTEND(readability-enum-initial-value)
// This entire enum can be removed when fixCleanup3_2_0 is retired
enum class CuspRoundingFix : bool {
Disabled = false,
Enabled = true,
// This entire enum can be removed when the last relevant amendment is retired
enum class CuspRoundingFix : std::uint8_t {
// Disabled can be removed when fixCleanup3_2_0 is retired
Disabled = 0,
// Enabled320 can be removed when fixCleanup3_3_0 is retired
Enabled320 = 1,
// If we ever get to the point that there's only one entry, remove the entire enum
Enabled330 = 2,
// Enabled is a de-facto alias for "the latest", and is only here for backward compatibility
// in the extremely unlikely case that a downstream project made use of it. Note that
// because the behavior changed, this may still be a breaking change.
Enabled = Enabled330,
};
explicit constexpr MantissaRange(MantissaScale sc) : scale(sc)
@@ -141,13 +167,27 @@ struct MantissaRange final
int const log{getExponent(scale)};
rep const min{getMin(scale, log)};
rep const max{(min * 10) - 1};
CuspRoundingFix const cuspRoundingFixEnabled{isCuspFixEnabled(scale)};
static MantissaRange const&
getMantissaRange(MantissaScale scale);
CuspRoundingFix const cuspRoundingFix{isCuspFixEnabled(scale)};
static std::set<MantissaScale> const&
getAllScales();
getAllScales()
{
static std::set<MantissaRange::MantissaScale> const kScales = {
MantissaRange::MantissaScale::Small,
MantissaRange::MantissaScale::LargeLegacy,
MantissaRange::MantissaScale::Large320,
MantissaRange::MantissaScale::Large330,
};
return kScales;
}
class Access
{
static constexpr MantissaRange const&
mantissaRange(MantissaScale scale);
friend Number;
};
private:
static constexpr int
@@ -158,7 +198,8 @@ private:
case MantissaScale::Small:
return 15;
case MantissaScale::LargeLegacy:
case MantissaScale::Large:
case MantissaScale::Large320:
case MantissaScale::Large330:
return 18;
// LCOV_EXCL_START
default:
@@ -187,17 +228,16 @@ private:
case MantissaScale::Small:
case MantissaScale::LargeLegacy:
return CuspRoundingFix::Disabled;
case MantissaScale::Large:
return CuspRoundingFix::Enabled;
case MantissaScale::Large320:
return CuspRoundingFix::Enabled320;
case MantissaScale::Large330:
return CuspRoundingFix::Enabled330;
default:
// If called in a constexpr context, this throw assures that the build fails if an
// invalid scale is used.
throw std::runtime_error("Unknown mantissa scale"); // LCOV_EXCL_LINE
}
}
static std::unordered_map<MantissaScale, MantissaRange> const&
getRanges();
};
// Like std::integral, but only 64-bit integral types.
@@ -319,6 +359,8 @@ public:
static constexpr internalrep kMaxRep = std::numeric_limits<rep>::max();
static_assert(kMaxRep == 9'223'372'036'854'775'807);
static_assert(-kMaxRep == std::numeric_limits<rep>::min() + 1);
static constexpr internalrep kMaxRepUp = ((kMaxRep / 10) + 1) * 10;
static_assert(kMaxRepUp == 9'223'372'036'854'775'810ULL);
// May need to make unchecked private
struct Unchecked
@@ -541,6 +583,13 @@ public:
std::pair<T, int>
normalizeToRange() const;
// Safely convert rep (int64) mantissa to internalrep (uint64). If the rep
// is negative, returns the positive value. This takes a little extra work
// because converting std::numeric_limits<std::int64_t>::min() flirts with
// UB, and can vary across compilers.
static internalrep
externalToInternal(rep mantissa);
private:
static thread_local RoundingMode mode;
// The available ranges for mantissa
@@ -550,9 +599,15 @@ private:
// changing the values inside the range.
static thread_local std::reference_wrapper<MantissaRange const> kRange;
class Guard;
void
normalize(MantissaRange const& range);
// Guard has the fields that we need, as well as MantissaRange, so if we have a guard, use that
void
normalize(Guard const& guard);
/** Normalize Number components to an arbitrary range.
*
* min/maxMantissa are parameters because this function is used by both
@@ -567,7 +622,7 @@ private:
int& exponent,
internalrep const& minMantissa,
internalrep const& maxMantissa,
MantissaRange::CuspRoundingFix cuspRoundingFixEnabled);
MantissaRange::CuspRoundingFix cuspRoundingFix);
template <class T>
friend void
@@ -577,7 +632,7 @@ private:
int& exponent,
MantissaRange::rep const& minMantissa,
MantissaRange::rep const& maxMantissa,
MantissaRange::CuspRoundingFix cuspRoundingFixEnabled,
MantissaRange::CuspRoundingFix cuspRoundingFix,
bool dropped);
[[nodiscard]] bool
@@ -588,15 +643,6 @@ private:
// exponent could go out of range, so it will be checked.
[[nodiscard]] Number
shiftExponent(int exponentDelta) const;
// Safely convert rep (int64) mantissa to internalrep (uint64). If the rep
// is negative, returns the positive value. This takes a little extra work
// because converting std::numeric_limits<std::int64_t>::min() flirts with
// UB, and can vary across compilers.
static internalrep
externalToInternal(rep mantissa);
class Guard;
};
constexpr Number::Number(bool negative, internalrep mantissa, int exponent, Unchecked) noexcept
@@ -858,21 +904,11 @@ squelch(Number const& x, Number const& limit) noexcept
return x;
}
inline std::string
to_string(MantissaRange::MantissaScale const& scale)
{
switch (scale)
{
case MantissaRange::MantissaScale::Small:
return "small";
case MantissaRange::MantissaScale::LargeLegacy:
return "largeLegacy";
case MantissaRange::MantissaScale::Large:
return "large";
default:
throw std::runtime_error("Bad scale");
}
}
std::string
to_string(MantissaRange::MantissaScale const& scale);
std::string
to_string(Number::RoundingMode const& round);
class SaveNumberRoundMode
{

File diff suppressed because it is too large Load Diff

View File

@@ -39,22 +39,30 @@ setCurrentTransactionRules(std::optional<Rules> r)
// Push the appropriate setting, instead of having the class pull every time
// the value is needed. That could get expensive fast.
// If any new conditions with new amendments are added, those amendments must also be added to
// useRulesGuards.
bool const enableVaultNumbers =
!r || (r->enabled(featureSingleAssetVault) || r->enabled(featureLendingProtocol));
bool const enableCuspRoundingFix = !r || r->enabled(fixCleanup3_2_0);
XRPL_ASSERT(
!r || useRulesGuards(*r) == (enableCuspRoundingFix || enableVaultNumbers),
"setCurrentTransactionRules : rule decisions match");
// Declare the range this way to keep clang-tidy from complaining
auto const range = [enableCuspRoundingFix, enableVaultNumbers]() {
if (enableVaultNumbers)
auto const range = [&r]() {
// If any new conditions with new amendments are added to "enableLargeNumbers", those
// amendments must also be added to useRulesGuards.
bool const enableLargeNumbers =
!r || (r->enabled(featureSingleAssetVault) || r->enabled(featureLendingProtocol));
// If enableLargeNumbers is true, then useRulesGuard must also return true.
// However, the reverse is not true. Other amendments can cause the rules guard to be used,
// even though large numbers are _not_ used.
XRPL_ASSERT(
!r || !enableLargeNumbers || useRulesGuards(*r),
"setCurrentTransactionRules : rule decisions match");
if (enableLargeNumbers)
{
if (enableCuspRoundingFix)
static_assert(
MantissaRange::MantissaScale::Large == MantissaRange::MantissaScale::Large330);
if (!r || r->enabled(fixCleanup3_3_0))
{
return MantissaRange::MantissaScale::Large;
return MantissaRange::MantissaScale::Large330;
}
if (r->enabled(fixCleanup3_2_0))
{
return MantissaRange::MantissaScale::Large320;
}
return MantissaRange::MantissaScale::LargeLegacy;
}
@@ -69,14 +77,14 @@ bool
useRulesGuards(Rules const& rules)
{
// The list of amendments used here - to decide whether to create a RulesGuard - must be a
// superset of the list used to figure out which mantissa scale to use in
// setCurrentTransactionRules. Additional amendments can be added if desired.
// superset of the list used to determine "enableLargeNumbers" in setCurrentTransactionRules.
// Additional amendments can be added if desired.
//
// As soon as any one of these amendments is retired, this whole function can be removed, along
// with createGuards, and any other callers, and the first set of guards can be created directly
// at the call site, without using optional.
return rules.enabled(fixCleanup3_2_0) || rules.enabled(featureSingleAssetVault) ||
rules.enabled(featureLendingProtocol);
return rules.enabled(featureSingleAssetVault) || rules.enabled(featureLendingProtocol) ||
rules.enabled(fixCleanup3_2_0) || rules.enabled(fixCleanup3_3_0);
}
void
@@ -87,7 +95,8 @@ createGuards(
{
if (useRulesGuards(rules))
{
// raii classes for the current ledger rules.
// raii classes for the current ledger rules. If the rules are set, the MantissaRange will
// be updated, too.
rulesGuard.emplace(rules);
}
else

View File

@@ -88,7 +88,6 @@ STNumber::add(Serializer& s) const
}
else
{
#if !NDEBUG
// There are circumstances where an already-rounded Number is
// serialized without being touched by a transactor, and thus
// without an asset. We can't know if it's rounded, because it could
@@ -96,11 +95,9 @@ STNumber::add(Serializer& s) const
// Json. Regardless, the only time we should be serializing an
// STNumber is when the scale is large.
XRPL_ASSERT_PARTS(
Number::getMantissaScale() == MantissaRange::MantissaScale::LargeLegacy ||
Number::getMantissaScale() == MantissaRange::MantissaScale::Large,
Number::getMantissaScale() != MantissaRange::MantissaScale::Small,
"xrpl::STNumber::add",
"STNumber only used with large mantissa scale");
#endif
}
}
@@ -258,8 +255,47 @@ numberFromJson(SField const& field, json::Value const& value)
Throw<std::runtime_error>("not a number");
}
return STNumber{
field, Number{parts.negative, parts.mantissa, parts.exponent, Number::Normalized{}}};
Number const num{parts.negative, parts.mantissa, parts.exponent, Number::Normalized{}};
// Canonicalize "parts" and "num" with each other by getting rid of trailing 0s until either the
// exponents match, or there are no more 0s. If the two results don't match exactly, then the
// value has been rounded one way or another, and should not be used, because it may lead to an
// unexpected result. canonicalizeParts is not to be confused with Number::canonicalize, because
// they're have completely different goals.
auto canonicalizeParts = [](NumberParts p, int otherExponent) {
if (p.mantissa == 0)
return NumberParts{};
while (p.exponent < otherExponent && p.mantissa % 10 == 0)
{
p.mantissa /= 10;
++p.exponent;
}
return p;
};
auto const numberMantissa = num.mantissa();
auto const numberExponent = num.exponent();
auto const canonicalParts = canonicalizeParts(parts, numberExponent);
auto const canonicalNum = canonicalizeParts(
NumberParts{
.mantissa = Number::externalToInternal(numberMantissa),
.exponent = numberExponent,
.negative = numberMantissa < 0,
},
canonicalParts.exponent);
if (canonicalParts.mantissa != canonicalNum.mantissa ||
canonicalParts.exponent != canonicalNum.exponent ||
canonicalParts.negative != canonicalNum.negative)
{
Throw<std::runtime_error>("number cannot be represented");
}
return STNumber{field, num};
}
} // namespace xrpl

View File

@@ -5059,11 +5059,10 @@ class Invariants_test : public beast::unit_test::Suite
std::vector<ValidVault::DeltaInfo> values;
};
for (auto const mantissaScale : {
MantissaRange::MantissaScale::LargeLegacy,
MantissaRange::MantissaScale::Large,
})
for (auto const mantissaScale : MantissaRange::getAllScales())
{
if (mantissaScale == MantissaRange::MantissaScale::Small)
continue;
NumberMantissaScaleGuard const g{mantissaScale};
auto makeDelta = [&vaultAsset](Number const& n) -> ValidVault::DeltaInfo {

View File

@@ -52,6 +52,7 @@
#include <array>
#include <cstdint>
#include <exception>
#include <functional>
#include <memory>
#include <optional>
@@ -1436,18 +1437,77 @@ class LoanBroker_test : public beast::unit_test::Suite
env(tx2, Ter(temINVALID));
}
env.setParseFailureExpected(true);
try
{
auto const dm = power(2, 63) - 1;
BEAST_EXPECTS(dm > kMaxMpTokenAmount, to_string(dm));
tx2[sfDebtMaximum] = dm;
tx2[sfDebtMaximum] = "9223372036854775808";
env(tx2, Ter(temINVALID));
// should throw in parser
fail();
}
catch (std::exception const& e)
{
auto const dm = power(2, 63) - 3;
BEAST_EXPECTS(dm == kMaxMpTokenAmount, to_string(dm));
tx2[sfDebtMaximum] = dm;
env(tx2, Ter(tesSUCCESS));
BEAST_EXPECT(
std::string(e.what()) ==
"invalidParamsField 'tx_json.DebtMaximum' has invalid data.");
}
env.setParseFailureExpected(false);
if (Number::getMantissaScale() >= MantissaRange::MantissaScale::Large330)
{
// For the Large330 scale, 2^63 rounds _down_ to Number::kMaxRep
{
auto const dm = power(2, 63);
BEAST_EXPECTS(dm == kMaxMpTokenAmount, to_string(dm));
tx2[sfDebtMaximum] = dm;
env(tx2, Ter(tesSUCCESS));
}
{
auto const dm = power(2, 63) + Number{1, -1};
BEAST_EXPECTS(dm == kMaxMpTokenAmount, to_string(dm));
tx2[sfDebtMaximum] = dm;
env(tx2, Ter(tesSUCCESS));
}
{
auto const dm = power(2, 63) - 1;
BEAST_EXPECTS(dm < kMaxMpTokenAmount, to_string(dm));
tx2[sfDebtMaximum] = dm;
env(tx2, Ter(tesSUCCESS));
}
{
auto const dm = power(2, 63) - 3;
BEAST_EXPECTS(dm < kMaxMpTokenAmount, to_string(dm));
tx2[sfDebtMaximum] = dm;
env(tx2, Ter(tesSUCCESS));
}
{
auto const dm = power(2, 63) + 3;
BEAST_EXPECTS(dm > kMaxMpTokenAmount, to_string(dm));
tx2[sfDebtMaximum] = dm;
env(tx2, Ter(temINVALID));
}
}
else
{
// For other scales, 2^63 rounds _up_ to Number::kMaxRepUp. Subtracting 1 rounds up
// again.
{
auto const dm = power(2, 63) - 1;
BEAST_EXPECTS(dm > kMaxMpTokenAmount, to_string(dm));
tx2[sfDebtMaximum] = dm;
env(tx2, Ter(temINVALID));
}
{
auto const dm = power(2, 63) - 3;
BEAST_EXPECTS(dm == kMaxMpTokenAmount, to_string(dm));
tx2[sfDebtMaximum] = dm;
env(tx2, Ter(tesSUCCESS));
}
}
{

View File

@@ -60,6 +60,7 @@
#include <chrono>
#include <cstdint>
#include <exception>
#include <functional>
#include <limits>
#include <memory>
@@ -5252,11 +5253,15 @@ class Vault_test : public beast::unit_test::Suite
auto const maxInt64 = std::to_string(std::numeric_limits<std::int64_t>::max());
BEAST_EXPECT(maxInt64 == "9223372036854775807");
// Naming things is hard
auto const maxInt64Plus1 = std::to_string(
static_cast<std::uint64_t>(std::numeric_limits<std::int64_t>::max()) + 1);
BEAST_EXPECT(maxInt64Plus1 == "9223372036854775808");
// Naming things is hard
auto const maxInt64Plus2 = std::to_string(
static_cast<std::uint64_t>(std::numeric_limits<std::int64_t>::max()) + 2);
BEAST_EXPECT(maxInt64Plus2 == "9223372036854775809");
auto const initialXRP = to_string(kInitialXrp);
BEAST_EXPECT(initialXRP == "100000000000000000");
@@ -5283,25 +5288,58 @@ class Vault_test : public beast::unit_test::Suite
env(tx);
env.close();
tx[sfAssetsMaximum] = maxInt64Plus1;
env(tx, Ter(tefEXCEPTION));
env.close();
// There are several parse failures expected in this function, so just disable it once.
env.setParseFailureExpected(true);
try
{
tx[sfAssetsMaximum] = maxInt64Plus1;
env(tx, Ter(tefEXCEPTION));
env.close();
// should throw in parser
fail();
}
catch (std::exception const& e)
{
BEAST_EXPECT(
std::string(e.what()) ==
"invalidParamsField 'tx_json.AssetsMaximum' has invalid data.");
}
try
{
tx[sfAssetsMaximum] = maxInt64Plus2;
env(tx, Ter(tefEXCEPTION));
// should throw in parser
fail();
}
catch (std::exception const& e)
{
BEAST_EXPECT(
std::string(e.what()) ==
"invalidParamsField 'tx_json.AssetsMaximum' has invalid data.");
}
// This value will be rounded
auto const insertAt = maxInt64Plus1.size() - 3;
auto const decimalTest = maxInt64Plus1.substr(0, insertAt) + "." +
maxInt64Plus1.substr(insertAt); // (max int64+1) / 1000
BEAST_EXPECT(decimalTest == "9223372036854775.808");
tx[sfAssetsMaximum] = decimalTest;
auto const newKeylet = keylet::vault(owner.id(), env.seq(owner));
env(tx);
env.close();
try
{
auto const insertAt = maxInt64Plus2.size() - 3;
auto const decimalTest = maxInt64Plus2.substr(0, insertAt) + "." +
maxInt64Plus2.substr(insertAt); // (max int64+2) / 1000
BEAST_EXPECT(decimalTest == "9223372036854775.809");
tx[sfAssetsMaximum] = decimalTest;
env(tx);
// should throw in parser
fail();
}
catch (std::exception const& e)
{
BEAST_EXPECT(
std::string(e.what()) ==
"invalidParamsField 'tx_json.AssetsMaximum' has invalid data.");
}
auto const vaultSle = env.le(newKeylet);
if (!BEAST_EXPECT(vaultSle))
return;
BEAST_EXPECT(vaultSle->at(sfAssetsMaximum) == 9223372036854776);
BEAST_EXPECT(!vaultSle);
}
{
@@ -5335,25 +5373,41 @@ class Vault_test : public beast::unit_test::Suite
env(tx);
env.close();
tx[sfAssetsMaximum] = maxInt64Plus1;
env(tx, Ter(tefEXCEPTION));
env.close();
try
{
tx[sfAssetsMaximum] = maxInt64Plus2;
env(tx, Ter(tefEXCEPTION));
// should throw in parser
fail();
}
catch (std::exception const& e)
{
BEAST_EXPECT(
std::string(e.what()) ==
"invalidParamsField 'tx_json.AssetsMaximum' has invalid data.");
}
// This value will be rounded
auto const insertAt = maxInt64Plus1.size() - 1;
auto const decimalTest = maxInt64Plus1.substr(0, insertAt) + "." +
maxInt64Plus1.substr(insertAt); // (max int64+1) / 10
BEAST_EXPECT(decimalTest == "922337203685477580.8");
tx[sfAssetsMaximum] = decimalTest;
auto const newKeylet = keylet::vault(owner.id(), env.seq(owner));
env(tx);
env.close();
try
{
auto const insertAt = maxInt64Plus2.size() - 1;
auto const decimalTest = maxInt64Plus2.substr(0, insertAt) + "." +
maxInt64Plus2.substr(insertAt); // (max int64+2) / 10
BEAST_EXPECT(decimalTest == "922337203685477580.9");
tx[sfAssetsMaximum] = decimalTest;
env(tx);
// should throw in parser
fail();
}
catch (std::exception const& e)
{
BEAST_EXPECT(
std::string(e.what()) ==
"invalidParamsField 'tx_json.AssetsMaximum' has invalid data.");
}
auto const vaultSle = env.le(newKeylet);
if (!BEAST_EXPECT(vaultSle))
return;
BEAST_EXPECT(vaultSle->at(sfAssetsMaximum) == 922337203685477581);
BEAST_EXPECT(!vaultSle);
}
{
@@ -5380,9 +5434,22 @@ class Vault_test : public beast::unit_test::Suite
env(tx);
env.close();
tx[sfAssetsMaximum] = maxInt64Plus1;
env(tx);
env.close();
// Since several tests are expected to have parser failures, leave this flag set for the
// remainder of this function.
env.setParseFailureExpected(true);
try
{
tx[sfAssetsMaximum] = maxInt64Plus2;
env(tx);
// should throw in parser
fail();
}
catch (std::exception const& e)
{
BEAST_EXPECT(
std::string(e.what()) ==
"invalidParamsField 'tx_json.AssetsMaximum' has invalid data.");
}
tx[sfAssetsMaximum] = "1000000000000000e80";
env.close();
@@ -5392,22 +5459,27 @@ class Vault_test : public beast::unit_test::Suite
// These values will be rounded to 15 significant digits
{
auto const insertAt = maxInt64Plus1.size() - 1;
auto const decimalTest = maxInt64Plus1.substr(0, insertAt) + "." +
maxInt64Plus1.substr(insertAt); // (max int64+1) / 10
BEAST_EXPECT(decimalTest == "922337203685477580.8");
tx[sfAssetsMaximum] = decimalTest;
auto const newKeylet = keylet::vault(owner.id(), env.seq(owner));
env(tx);
env.close();
try
{
auto const insertAt = maxInt64Plus2.size() - 1;
auto const decimalTest = maxInt64Plus2.substr(0, insertAt) + "." +
maxInt64Plus2.substr(insertAt); // (max int64+2) / 10
BEAST_EXPECT(decimalTest == "922337203685477580.9");
tx[sfAssetsMaximum] = decimalTest;
env(tx);
// should throw in parser
fail();
}
catch (std::exception const& e)
{
BEAST_EXPECT(
std::string(e.what()) ==
"invalidParamsField 'tx_json.AssetsMaximum' has invalid data.");
}
auto const vaultSle = env.le(newKeylet);
if (!BEAST_EXPECT(vaultSle))
return;
BEAST_EXPECT(
(vaultSle->at(sfAssetsMaximum) ==
Number{9223372036854776, 2, Number::Normalized{}}));
BEAST_EXPECT(!vaultSle);
}
{
tx[sfAssetsMaximum] = "9223372036854775807e40"; // max int64 * 10^40

File diff suppressed because it is too large Load Diff

View File

@@ -9,6 +9,7 @@
#include <xrpl/protocol/Serializer.h>
#include <cstdint>
#include <exception>
#include <initializer_list>
#include <limits>
#include <stdexcept>
@@ -101,6 +102,39 @@ struct STNumber_test : public beast::unit_test::Suite
BEAST_EXPECT(numberFromJson(sfNumber, "-0.000e6") == STNumber(sfNumber, 0));
{
auto const parseNumber = [](std::string const& boundary) {
return numberFromJson(sfNumber, boundary);
};
auto const expectParseThrows = [this, &parseNumber](std::string const& boundary) {
try
{
auto const bad = parseNumber(boundary);
fail();
}
catch (std::exception const& e)
{
BEAST_EXPECT(std::string(e.what()) == "number cannot be represented");
}
};
// Small rejects this; large scales parse it as 9223372036854775800e-1.
auto constexpr positiveBoundary = "922337203685477580";
auto constexpr negativeBoundary = "-922337203685477580";
if (Number::getMantissaScale() == MantissaRange::MantissaScale::Small)
{
expectParseThrows(positiveBoundary);
expectParseThrows(negativeBoundary);
}
else
{
BEAST_EXPECT(
parseNumber(positiveBoundary) ==
STNumber(sfNumber, Number{922'337'203'685'477'580, 0}));
BEAST_EXPECT(
parseNumber(negativeBoundary) ==
STNumber(sfNumber, Number{-922'337'203'685'477'580, 0}));
}
NumberRoundModeGuard const mg(Number::RoundingMode::TowardsZero);
// maxint64 9,223,372,036,854,775,807
auto const maxInt = std::to_string(std::numeric_limits<std::int64_t>::max());
@@ -108,23 +142,19 @@ struct STNumber_test : public beast::unit_test::Suite
auto const minInt = std::to_string(std::numeric_limits<std::int64_t>::min());
if (Number::getMantissaScale() == MantissaRange::MantissaScale::Small)
{
BEAST_EXPECT(
numberFromJson(sfNumber, maxInt) ==
STNumber(sfNumber, Number{9'223'372'036'854'775, 3}));
BEAST_EXPECT(
numberFromJson(sfNumber, minInt) ==
STNumber(sfNumber, Number{-9'223'372'036'854'775, 3}));
// min/maxInt can't be exactly represented with the small mantissa, so they
// don't parse, and are expected to throw.
expectParseThrows(maxInt);
expectParseThrows(minInt);
}
else
{
// with large mantissas, maxint is fine
BEAST_EXPECT(
numberFromJson(sfNumber, maxInt) ==
parseNumber(maxInt) ==
STNumber(sfNumber, Number{9'223'372'036'854'775'807, 0}));
BEAST_EXPECT(
numberFromJson(sfNumber, minInt) ==
STNumber(
sfNumber,
Number{true, 9'223'372'036'854'775'808ULL, 0, Number::Normalized{}}));
// but minint's mantissa is > kMaxRep, and so rounds, and thus can't be parsed
expectParseThrows(minInt);
}
}