Skip to content

Round a value below the smallest subnormal in each rounding mode - #1477

Merged
mborland merged 1 commit into
boostorg:developfrom
ibmibmibm:worktree-ctor-subnormal
Sep 30, 2026
Merged

mborland merged 1 commit into
boostorg:developfrom
ibmibmibm:worktree-ctor-subnormal

Conversation

@ibmibmibm

@ibmibmibm ibmibmibm commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor
  • The constructors of decimal32_t, decimal64_t and decimal128_t had a special branch for
    a significand of one digit below the smallest exponent. It rounded a tie up, and it ignored
    the upward and the downward modes below the first step. The branch is removed. The general
    branch after it rounds these values correctly in each mode.
  • If all digits of the coefficient drop, coefficient_rounding set it to zero, and it did not
    round. It now rounds a zero with a sticky bit through fenv_round. A directed mode can then
    give the smallest subnormal, and a zero input still gives zero.
  • decimal128_t called coefficient_rounding only for a coefficient type of 128 bits or more.
    A narrower coefficient far below the smallest exponent read past the end of the pow10
    table, and decimal128_t{10, -6231} stopped the program with SIGFPE. It now takes the same
    path.
  • For a 128-bit coefficient with few digits, coefficient_rounding divided in the narrow
    significand type. A large shift did not fit in that type. The quotient was wrong, or the
    divide by zero stopped the program, for example in a conversion from decimal128_t to
    decimal32_t. The narrow divide now also needs a shift that fits.
  • The new test github_issue_1476.cpp checks the four cases for the three types, in the
    modes with a defect. Before the change it stops with SIGFPE.

A sweep checks values below the smallest exponent through the constructor, the string parser
and the multiply. After the change, it gives no wrong result in the five rounding modes. Issue #1476
gives the values, the sweeps, the comparison with develop and the benchmark.

Fixes #1476

@ibmibmibm
ibmibmibm force-pushed the worktree-ctor-subnormal branch from 4d970cc to bd11148 Compare September 29, 2026 07:34
@codecov

codecov Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.7%. Comparing base (5b03306) to head (4d43e0f).

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff            @@
##           develop   #1477     +/-   ##
=========================================
+ Coverage     98.6%   98.7%   +0.1%     
=========================================
  Files          312     313      +1     
  Lines        26265   26251     -14     
  Branches      2264    2249     -15     
=========================================
+ Hits         25892   25896      +4     
+ Misses         373     355     -18     
Files with missing lines Coverage Δ
include/boost/decimal/decimal128_t.hpp 97.2% <100.0%> (+1.0%) ⬆️
include/boost/decimal/decimal32_t.hpp 97.4% <100.0%> (+0.9%) ⬆️
include/boost/decimal/decimal64_t.hpp 97.8% <100.0%> (+1.0%) ⬆️
include/boost/decimal/detail/fenv_rounding.hpp 97.4% <100.0%> (+0.1%) ⬆️
test/github_issue_1476.cpp 100.0% <100.0%> (ø)

Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 5b03306...4d43e0f. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@ibmibmibm
ibmibmibm marked this pull request as ready for review September 29, 2026 12:10

@mborland mborland left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

In the test file you only have testing for 3 of the 5 rounding modes. Can you please add paths so that all are hit? Thanks

@mborland mborland left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

In the test file you only have testing for 3 of the 5 rounding modes. Can you please add paths so that all are hit? Thanks

- The constructors of decimal32_t, decimal64_t and decimal128_t had a special
  branch for a significand of one digit below the smallest exponent. It
  rounded a tie up, and it ignored the upward and the downward modes below
  the first step. The branch is removed. The general branch after it rounds
  these values correctly.
- coefficient_rounding set the coefficient to zero when all its digits drop,
  and it did not round. It now rounds a zero with a sticky bit through
  fenv_round, thus a directed mode can give the smallest subnormal. A zero
  input still gives zero.
- decimal128_t called coefficient_rounding only for a coefficient type of
  128 bits or more. A narrower coefficient far below the smallest exponent
  read past the end of the pow10 table. This is undefined behavior, and it
  can stop the program with SIGFPE. It now takes the same path.
- For a 128-bit coefficient with few digits, coefficient_rounding divided in
  the narrow significand type. A shift of more than 9 or 19 digits did not
  fit in that type, thus the power of ten lost its high bits. The quotient
  was wrong, or the divide by zero stopped the program. The narrow divide now
  also needs a shift that fits.
- Add a test for the four cases.
@ibmibmibm
ibmibmibm force-pushed the worktree-ctor-subnormal branch from bd11148 to 4d43e0f Compare September 30, 2026 02:10
@ibmibmibm

Copy link
Copy Markdown
Contributor Author

All rounding mode tests were added.

@ibmibmibm
ibmibmibm requested a review from mborland September 30, 2026 07:19

@mborland mborland left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks good! Thanks!

@mborland
mborland merged commit 26445a9 into boostorg:develop Sep 30, 2026
75 checks passed
@ibmibmibm
ibmibmibm deleted the worktree-ctor-subnormal branch October 1, 2026 04:12
mborland pushed a commit that referenced this pull request Oct 2, 2026
- The `decimal128_t` multiply has a separate path for an exponent sum
below `-6142`. It does not
  expand the significands, so the product can have 1 to 68 digits.
- The path rounded the product only above 38 digits, the `digits10` of
the 128-bit significand
type. A product of 35 to 38 digits then went to `pack_in_range`
unrounded. The encoder wrote
  its extra digits into bits outside the significand field.
- The limit is now `detail::precision_v<ReturnType>`, 34 digits for
`decimal128_t`.
- The new test `github_issue_1482.cpp` multiplies products of 35 to 38
digits in each rounding
mode. Before the change, each check fails except one product that rounds
to zero.

A sweep of 20000 such products gives 962 wrong results in each of the
five rounding modes on
`develop`, and none after the change. The change needs #1477, which is
in `develop`. Issue
#1482 gives the values, the sweep, the comparison with `develop` and the
benchmark.

Fixes #1482
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The constructor rounds some values below the smallest subnormal wrongly, and some of them stop the program

2 participants