Skip to content

Merge to master for 1.93 - #1527

Merged
mborland merged 171 commits into
masterfrom
develop
Oct 23, 2026
Merged

mborland merged 171 commits into
masterfrom
develop

Conversation

@mborland

Copy link
Copy Markdown
Member

No description provided.

mborland and others added 30 commits July 16, 2026 20:42
Fix module rot and match other boost libraries implementations
Add new `cohort_preserving_fixed` charconv mode and fix non-rejection in `cohort_preserving_scientific`
ckormanyos and others added 27 commits October 1, 2026 09:35
Fix the accuracy and monotonicity of asin, acos, atan and atan2
This PR follows the review of #1479.

`test_cos()` in `test/test_sin_cos.cpp` now uses a tolerance of 16 float
eps, the same value that #1479 uses for `test_sin()`. The old value was
35 eps. Over 200000 random points in [-2π, 2π], the largest cos error
for all decimal types is 5 eps.

`test_asin_edge()` in `test/test_asin.cpp` now seeds its generator with
a fixed value. The old code seeded it from the clock, so a failure was
not reproducible. This change also removes the unused `time_point()`
helper, `std::random_device`, and `<chrono>`.

This PR does not change `test/test_tan.cpp` because #1479 already
changes the seed there.

Both tests pass with GCC and Clang.
- 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
…r significand (#1486)

- ceil and round gave +0 for a negative x that rounds to zero, e.g.
ceil(-0.5), round(-0.4)
- trunc and modf gave +0 too, because they use ceil
- In the IEEE types, the result kept a negative exponent: ceil(1.5) was
2.0, and
  floor(2.0) was 2.0, not 2
- rint and nearbyint gave +-0 for each |x| < 0.1, which is wrong in the
upward and the
  downward modes, e.g. rint(1e-30) was 0 in the upward mode
- lrint and llrint gave 0 for each |x| < 0.1, with the same defect
- lrint and llrint of +-9.223372036854776e18 (LLONG_MAX and LLONG_MIN
rounded to 16
  digits) wrapped to the other sign in decimal64_t and decimal_fast64_t
- round used modf, a subtraction and a compare, and was 2.6-4.9x slower
than floor
- Now one division of the significand by 10^-e gives the integer and the
remainder
- The rounding mode picks the increment: a constant for ceil, floor,
round, roundeven and
  trunc, and the current mode for rint, nearbyint, lrint and llrint
- pack_in_range packs the result, which has the exponent max(q(x), 0)
and the sign of x
- lrint and llrint check the range on the integer significand, not with
decimal compares
- Add tests for -0 results, the quantum, rint, lrint and llrint in each
mode, and the
  limits of llrint
- The -0 tests in test_cmath.cpp made +0, so they now make -0 and read
the sign

Fixes #1485
frexp now gives the correctly rounded result for all finite inputs in
the current rounding mode, and it is 16 to 39 times faster.

The old frexp_impl has four defects:

- The result is not correctly rounded. frexp_impl multiplies by 2^-t
from pow_2_impl and then divides by powers of 2. Each step rounds, so
the error is up to 852 ulp.
- The loop does not stop below 1e-91 (decimal32), 1e-369 (decimal64) and
1e-6111 (decimal128). The fast types return 0.
- A subnormal input gives 0.
- -0 gives +0.

<details>
<summary>The new frexp_impl</summary>

- Writes v * 10^n as s * 5^K * 2^K, and gets s * 5^K as a binary value
of 64, 128 or 256 bits. The value comes from a table of 5^(+-d * 16^j),
so that |K| needs at most four products.
- Gets the digits with one shift. fenv_round rounds them once in the
current rounding mode, so that the constructor gets p digits.
- Uses n = p + 1 digits for decimal32 and decimal64. decimal128 uses n =
p digits. fenv_round then gets the parity of the last digit and a digit
for the bits below, so that it does not divide 128-bit digits by 10.
- First computes s * 5^K in a word of 64 or 128 bits for decimal64 and
decimal128. This is half of the width that an exact result can need.
After nm products, that M is below the exact value by less than 6 * nm +
1 units. If the bits below the p digits are not that close to 0, to one
half or to a carry, they give the result. Else the wide pass runs.
- Knows that M is exact if s and the table entries fit in the word
together.
- Gets the products of decimal128 from umul256_hi and umul512_hi. For K
> 0, the first narrow product of decimal128 uses 64 bits of the entry,
because 5^1 to 5^15 are exact in 64 bits. A static_assert stops a radix
with a digit above 27.
- Removes the factors of 5 only when K >= -1.5 * p, because for a
smaller K no result is exact. A product with the inverse of 5 modulo 2^w
removes each of them without a division.
- Finds NaN, infinity and zero without fpclassify, which counts the
digits. A zero is returned as it is, so that it keeps its sign.
- Makes the result with pack_in_range, because the result has p digits
and an exponent in range. pack_in_range does not do the checks of the
constructor.
- Ends all finite paths in one pack_in_range. With one return for each
path, GCC merges the results of the fast types with a memory copy, and
that copy stalls on the stores of the single members.
- Cuts the table entries to the word width at compile time, so that a
program keeps only the widths that it uses. decimal32 keeps a table of
736 bytes and decimal64 a table of 1472 bytes instead of 2944 bytes.

</details>

<details>
<summary>Other changes</summary>

- countl_zero gives the correct count for types of fewer than 64 bits on
compilers without __builtin_clz, for example MSVC. Before, the fallback
counted the leading zeros of a 64-bit value, so frexp of decimal32 gave
0.
- frexp computes in the type of the value, also when
BOOST_DECIMAL_DEC_EVAL_METHOD is 1 or 2. Before, frexp computed in the
wider evaluation type, and the cast back to the type of the value can
round the fraction to 1. For example, frexp(9223372e12_DF) gave 1 * 2^63
and not 0.5 * 2^64.
- A first commit adds umul256_hi, the high 128 bits of the product of
two uint128_t, and computes umul512_hi with a schoolbook product of
64-bit limbs. The old umul512_hi used four umul256 products, with
branches and carry compares. The value is the same. frexp uses both
functions.

</details>

<details>
<summary>Results</summary>

- Accuracy: a GMP reference computes the correctly rounded result in the
five rounding modes. The inputs are random values over the full range,
powers of 2 and their neighbors, subnormals, short significands and
significands with factors of 5. Seeds 7 and 11 give 1104710 results
each. All results are correctly rounded, in the range [0.5, 1), and have
the correct exponent.
- Proof for the wide pass: its M is below the exact value by less than 6
* nm units. A search over all K and all s finds the exact values nearest
to a rounding boundary. The nearest one is 38.7 units away for decimal32
(bound 13), 703.6 units for decimal64 (bound 19) and 169830 units for
decimal128 (bound 25). A brute-force scan gives the same cases for all s
of decimal32 and for s < 10^9 of decimal64.
- Speed (GCC 15, -O3, |v| in [1e-20, 1e20], ns for each call, develop to
this PR): decimal32 176.9 to 6.8, decimal64 272.0 to 9.8, decimal128
736.0 to 18.7, decimal_fast32 130.4 to 8.0, decimal_fast64 323.1 to
16.1, decimal_fast128 693.4 to 27.1. This is 16 to 39 times faster.
- Speed of umul512_hi (GCC 15, -O3, ns for each call):
divmod_pow10_u256, which rounds decimal128 results, goes from 74 to 78
ns to 63 to 64 ns. Multiplication, division and fma of decimal128 and
decimal_fast128 do not change more than the noise.
- Code size (GCC, -O2, .text of one frexp call): decimal32 10449 to 986
bytes, decimal64 15692 to 2243 bytes, decimal128 33508 to 3484 bytes.

</details>

Fixes #1487
Bumps the all-dependencies group in /doc with 3 updates:
[@antora/cli](https://gitlab.com/antora/antora/tree/HEAD/packages/cli),
[@antora/site-generator](https://gitlab.com/antora/antora/tree/HEAD/packages/site-generator)
and
[antora](https://gitlab.com/antora/antora/tree/HEAD/packages/antora).

Updates `@antora/cli` from 3.2.0 to 3.2.1
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://gitlab.com/antora/antora/blob/main/CHANGELOG.adoc">@​antora/cli's
changelog</a>.</em></p>
<blockquote>
<p>== 3.2.1 (2026-10-01)</p>
<p>=== Changed</p>
<ul>
<li><em>logger</em>: Handle non-git origin when url is defined; print
url as source</li>
</ul>
<p>=== Fixed</p>
<ul>
<li><em>logger</em>: Ignore unknown origin type (without url) when
reshaping file for log (<a
href="https://gitlab.com/antora/antora/tree/HEAD/packages/cli/-/work_items/1247">#1247</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://gitlab.com/antora/antora/commit/fcc5d507ecf5944a0ea3fc8048160cf4c26da7f7"><code>fcc5d50</code></a>
release 3.2.1</li>
<li><a
href="https://gitlab.com/antora/antora/commit/2023710b5bf360500ba21d6aee92c77d3f34d8ff"><code>2023710</code></a>
refresh dependency lock file</li>
<li><a
href="https://gitlab.com/antora/antora/commit/0d19349325164b2063825666c37404996fe91af5"><code>0d19349</code></a>
handle non-git origin when url is defined; print url as source</li>
<li><a
href="https://gitlab.com/antora/antora/commit/de9467011b08cd9e5d53054d7be2dec52899cd8c"><code>de94670</code></a>
add test to verify TERM variable is checked for color support in pretty
log i...</li>
<li><a
href="https://gitlab.com/antora/antora/commit/5afad380eb084c5bb74949777efa272808d899dc"><code>5afad38</code></a>
merge !1154</li>
<li><a
href="https://gitlab.com/antora/antora/commit/21ccb5f6a0b90d977b215a2e2c70b3032a835511"><code>21ccb5f</code></a>
resolves <a
href="https://gitlab.com/antora/antora/tree/HEAD/packages/cli/-/work_items/1247">#1247</a>
ignore unknown origin type when reshaping file for log</li>
<li><a
href="https://gitlab.com/antora/antora/commit/29125a573d87e22ae03a37150eed9beb375a821c"><code>29125a5</code></a>
ensure optional deps are enabled when installing biome [skip ci]</li>
<li><a
href="https://gitlab.com/antora/antora/commit/06cddb459a94e861a26c5237514e30d372e532b6"><code>06cddb4</code></a>
npm fix doesn't allow blank lines in package.json</li>
<li><a
href="https://gitlab.com/antora/antora/commit/334ff565a1a4c2eb34ba4da644a4acb01300f97c"><code>334ff56</code></a>
separate tasks in package file to make it easier to see pre/post tasks
[skip ci]</li>
<li><a
href="https://gitlab.com/antora/antora/commit/9e31aea346b50b15178845fe749a5343e7514720"><code>9e31aea</code></a>
slightly optimize heredoc function in test harness</li>
<li>Additional commits viewable in <a
href="https://gitlab.com/antora/antora/compare/v3.2.0...v3.2.1">compare
view</a></li>
</ul>
</details>
<br />

Updates `@antora/site-generator` from 3.2.0 to 3.2.1
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://gitlab.com/antora/antora/blob/main/CHANGELOG.adoc">@​antora/site-generator's
changelog</a>.</em></p>
<blockquote>
<p>== 3.2.1 (2026-10-01)</p>
<p>=== Changed</p>
<ul>
<li><em>logger</em>: Handle non-git origin when url is defined; print
url as source</li>
</ul>
<p>=== Fixed</p>
<ul>
<li><em>logger</em>: Ignore unknown origin type (without url) when
reshaping file for log (<a
href="https://gitlab.com/antora/antora/tree/HEAD/packages/site-generator/-/work_items/1247">#1247</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://gitlab.com/antora/antora/commit/fcc5d507ecf5944a0ea3fc8048160cf4c26da7f7"><code>fcc5d50</code></a>
release 3.2.1</li>
<li><a
href="https://gitlab.com/antora/antora/commit/2023710b5bf360500ba21d6aee92c77d3f34d8ff"><code>2023710</code></a>
refresh dependency lock file</li>
<li><a
href="https://gitlab.com/antora/antora/commit/0d19349325164b2063825666c37404996fe91af5"><code>0d19349</code></a>
handle non-git origin when url is defined; print url as source</li>
<li><a
href="https://gitlab.com/antora/antora/commit/de9467011b08cd9e5d53054d7be2dec52899cd8c"><code>de94670</code></a>
add test to verify TERM variable is checked for color support in pretty
log i...</li>
<li><a
href="https://gitlab.com/antora/antora/commit/5afad380eb084c5bb74949777efa272808d899dc"><code>5afad38</code></a>
merge !1154</li>
<li><a
href="https://gitlab.com/antora/antora/commit/21ccb5f6a0b90d977b215a2e2c70b3032a835511"><code>21ccb5f</code></a>
resolves <a
href="https://gitlab.com/antora/antora/tree/HEAD/packages/site-generator/-/work_items/1247">#1247</a>
ignore unknown origin type when reshaping file for log</li>
<li><a
href="https://gitlab.com/antora/antora/commit/29125a573d87e22ae03a37150eed9beb375a821c"><code>29125a5</code></a>
ensure optional deps are enabled when installing biome [skip ci]</li>
<li><a
href="https://gitlab.com/antora/antora/commit/06cddb459a94e861a26c5237514e30d372e532b6"><code>06cddb4</code></a>
npm fix doesn't allow blank lines in package.json</li>
<li><a
href="https://gitlab.com/antora/antora/commit/334ff565a1a4c2eb34ba4da644a4acb01300f97c"><code>334ff56</code></a>
separate tasks in package file to make it easier to see pre/post tasks
[skip ci]</li>
<li><a
href="https://gitlab.com/antora/antora/commit/9e31aea346b50b15178845fe749a5343e7514720"><code>9e31aea</code></a>
slightly optimize heredoc function in test harness</li>
<li>Additional commits viewable in <a
href="https://gitlab.com/antora/antora/compare/v3.2.0...v3.2.1">compare
view</a></li>
</ul>
</details>
<br />

Updates `antora` from 3.2.0 to 3.2.1
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://gitlab.com/antora/antora/blob/main/CHANGELOG.adoc">antora's
changelog</a>.</em></p>
<blockquote>
<p>== 3.2.1 (2026-10-01)</p>
<p>=== Changed</p>
<ul>
<li><em>logger</em>: Handle non-git origin when url is defined; print
url as source</li>
</ul>
<p>=== Fixed</p>
<ul>
<li><em>logger</em>: Ignore unknown origin type (without url) when
reshaping file for log (<a
href="https://gitlab.com/antora/antora/tree/HEAD/packages/antora/-/work_items/1247">#1247</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://gitlab.com/antora/antora/commit/fcc5d507ecf5944a0ea3fc8048160cf4c26da7f7"><code>fcc5d50</code></a>
release 3.2.1</li>
<li><a
href="https://gitlab.com/antora/antora/commit/2023710b5bf360500ba21d6aee92c77d3f34d8ff"><code>2023710</code></a>
refresh dependency lock file</li>
<li><a
href="https://gitlab.com/antora/antora/commit/0d19349325164b2063825666c37404996fe91af5"><code>0d19349</code></a>
handle non-git origin when url is defined; print url as source</li>
<li><a
href="https://gitlab.com/antora/antora/commit/de9467011b08cd9e5d53054d7be2dec52899cd8c"><code>de94670</code></a>
add test to verify TERM variable is checked for color support in pretty
log i...</li>
<li><a
href="https://gitlab.com/antora/antora/commit/5afad380eb084c5bb74949777efa272808d899dc"><code>5afad38</code></a>
merge !1154</li>
<li><a
href="https://gitlab.com/antora/antora/commit/21ccb5f6a0b90d977b215a2e2c70b3032a835511"><code>21ccb5f</code></a>
resolves <a
href="https://gitlab.com/antora/antora/tree/HEAD/packages/antora/-/work_items/1247">#1247</a>
ignore unknown origin type when reshaping file for log</li>
<li><a
href="https://gitlab.com/antora/antora/commit/29125a573d87e22ae03a37150eed9beb375a821c"><code>29125a5</code></a>
ensure optional deps are enabled when installing biome [skip ci]</li>
<li><a
href="https://gitlab.com/antora/antora/commit/06cddb459a94e861a26c5237514e30d372e532b6"><code>06cddb4</code></a>
npm fix doesn't allow blank lines in package.json</li>
<li><a
href="https://gitlab.com/antora/antora/commit/334ff565a1a4c2eb34ba4da644a4acb01300f97c"><code>334ff56</code></a>
separate tasks in package file to make it easier to see pre/post tasks
[skip ci]</li>
<li><a
href="https://gitlab.com/antora/antora/commit/9e31aea346b50b15178845fe749a5343e7514720"><code>9e31aea</code></a>
slightly optimize heredoc function in test harness</li>
<li>Additional commits viewable in <a
href="https://gitlab.com/antora/antora/compare/v3.2.0...v3.2.1">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
+ Dragonbox's simple variant replaces the generic 128-bit Ryu for float,
double and 64-bit long double
  + about 17 times faster for `double` and 12 times for `float`
+ fixes the last digit Ryu got wrong for some values, such as
2.8380164753330115e19 and 6.2946755e10f
+ 80-bit and 128-bit long doubles still go through Ryu, which Dragonbox
does not cover

 uses code from https://github.com/jk-jeon/dragonbox
ldexp_impl uses the frexp machinery to write v * 2^e as s * 5^k5 *
2^(k5 + e). It gets the digits from the table of powers of 5 and
rounds them once with fenv_round in the current rounding mode. The
old code multiplied v by a rounded 2^e, so it rounded twice and gave
infinity or zero when 2^e was out of range but v * 2^e was not.

An estimate of log10(2) * (log2(s) + e) gives the number of digits
before the decimal point, or one less. If the result has one more digit,
ldexp drops the last digit before it rounds. decimal64 and decimal128
first try a word of half the width, as frexp does. For an IEEE type,
a short significand and a small e give an exact result with one shift
or one multiplication by a power of 5.

frexp_impl and ldexp_impl share the helpers that remove factors of 5,
multiply by a power of 5 and round the narrow word. The frexp table
has two more rows, so that |k5| <= 16383, and a table of 5^0 to 5^27.
ldexp keeps the sign of an infinity and computes in the type of the
value, also when BOOST_DECIMAL_DEC_EVAL_METHOD is 1 or 2. Add tests
for exact results, ties in both modes to nearest, subnormal results,
overflow and underflow, and run them with each evaluation method.

Fixes #1497
- The old `cbrt` used a Pade start and Newton steps in decimal
  arithmetic, and it multiplied by the rounded `cbrt10_v` for the
  exponent. Each step rounded, so many results were not correctly
  rounded, also for exact cubes and powers of ten.
- The new `cbrt` finds the root in integer arithmetic. A binary
  fixed-point estimate puts the root within half a unit, and the sign of
  8N - z^3 for the one boundary z near the estimate gives the result in
  each mode.
- For decimal32_t, the Newton step uses 64-bit products of 32-bit words.
  For decimal64_t, the first step uses them, and the last step uses them
  for its small term. Only decimal128_t uses 128-bit products
  throughout.
- For decimal128_t, when the boundary is more than 1/16 from the
  estimate, the estimate gives the result, and the 256-bit test does not
  run.
- The code has no jumps on random values: a bias keeps the Newton error
  positive, the result comes from sums of the flags, and 10^(e mod 3)
  comes from a table. The scale comes from the bit length of the
  significand, so it does not wait for the multiply.
- `u256` gets `operator-` with the same three paths as `operator+`, so
  that the 256-bit test of decimal128_t uses `u256`, `umul256` and the
  `u256` addition.
- `test_cbrt_edge` now expects cbrt(10^n) rounded once.
- The new test `test_cbrt_rounding.cpp` checks hard cases in all five
  modes for each type, and random decimal32_t values against exact
  bounds.
- `test_big_uints.cpp` checks the borrow and the wrap of `operator-`,
  also in constant evaluation.

Fixes #1500
+ it rounds the exact binary value to the decimal type, IEEE 754
convertFormat (5.4.2), like Intel's binary64_to_bid64
+ the constructors keep the shortest digits instead, so 0.07 gives 0.07
there and 0.07000000000000001 here
+ about as fast as Intel's conversion, and bit for bit the same for
every float and in every rounding mode
+ one name for float and double, IEEE 754's binary32 and binary64,
matching from_bid and from_dpd

```c++
#include <boost/decimal.hpp>
#include <iomanip>
#include <iostream>

int main()
{
    using namespace boost::decimal;

    std::cout << std::setprecision(16)
              << 99.99 << '\n'                              // 99.98999999999999
              << decimal64_t{99.99} << '\n'                 // 99.99
              << from_binary<decimal64_t>(99.99) << '\n';   // 99.98999999999999
}
```

This code shows how it can be used. The double `99.99` really holds
`99.98999999999999488...` `from_binary<decimal64_t>()` rounds that exact
value to decimal64's 16 digits, as IEEE 754 convertFormat specifies, and
gives the same result as Intel's `binary64_to_bid64`. The constructor
gives the shortest decimal that reads back as the same double.
`from_binary<decimal64_t>` is roughly 3x faster than the ctor, and about
as fast as `binary64_to_bid64` and produces identical results.
lround rounded the value with round(), and compared the result with the
limits of long converted to the decimal type. In decimal64_t,
LLONG_MAX and LLONG_MIN round away from zero, to 9223372036854776e3 and
-9223372036854776e3. These values passed the compare, and the
conversion to long overflowed to a value with the other sign. The
compares and the conversion also made lround slow.

lrint and llrint already round the integer significand and check the
range on the integer. This code moves from rint.hpp to
impl/round_integral.hpp as round_integral_int_impl, which takes the
rounding mode like round_integral_impl. lrint and llrint give the
current mode, and lround and llround give fe_dec_to_nearest_from_zero.

Fixes #1504
…t is zero (#1508)

`from_chars` and `strtod` parse `".05e0"` as `0.5` (instead of `0.05`).
With an exponent of zero (e0, e+0, e-00), the parser returns early. That
early return leaves out the zeros it skipped right after the decimal
point, which every nonzero exponent adds back (exponent +=
leading_zero_powers). The early return now adds them too. For the same
reason, "0.0e0" came out as 0e0 instead of 0e-1.
```c++
#include <boost/decimal.hpp>
#include <cstdio>
#include <cstring>

int main()
{
    using namespace boost::decimal;

    const char* inputs[] = {".05e0", "0.05e0", "0.05e+0", "0.05e1", "0.05"};
    for (const char* input : inputs)
    {
        decimal64_t value;
        from_chars(input, input + std::strlen(input), value);

        char buffer[64];
        *to_chars(buffer, buffer + sizeof(buffer) - 1, value, chars_format::scientific).ptr = '\0';
        std::printf("%-7s -> %s\n", input, buffer);
    }
}
```
Before:
> .05e0   -> 5e-01
> 0.05e0  -> 5e-01
> 0.05e+0 -> 5e-01
> 0.05e1  -> 5e-01
> 0.05    -> 5e-02

After:
> .05e0   -> 5e-02
> 0.05e0  -> 5e-02
> 0.05e+0 -> 5e-02
> 0.05e1  -> 5e-01
> 0.05    -> 5e-02
…rounding (#1509)

`boost::decimal::from_chars` and `strtod32`/`strtod64`/`strtod128` (and
so the `wcstod` versions) keep only the first 19 significant digits, or
38 for decimal128; later digits only shift the exponent. Usually that's
harmless, because 19 digits are more than the 7 or 16 a type keeps. But
when the kept digits form an exact tie between two neighbors, the
dropped digits are what decide the rounding. Without them, a value just
above the tie rounds to even, that is down, instead of up. At the bottom
of the range, this sends a value above half the smallest subnormal to
zero.

The parser now notes whether any dropped digit is nonzero, and if so
makes the last kept digit at least 1. That digit sits below the rounding
position of every type (digit 19 of 19 against 7 or 16 kept, digit 38 of
38 against 34), so it changes nothing except telling the rounding that
the value is above the tie. This works in every rounding mode.

The block in the exponent branch that was meant to round here never
runs: offset > significand_buffer_size can't be true, because the copy
loops stop at the buffer size. Even if it ran, it would round half up
from a single digit. It's removed.
```c++
#include <boost/decimal.hpp>
#include <cstdio>
#include <cstring>

template <typename T>
void show(const char* input)
{
    T value;
    boost::decimal::from_chars(input, input + std::strlen(input), value);

    char buffer[64];
    *boost::decimal::to_chars(buffer, buffer + sizeof(buffer) - 1, value).ptr = '\0';
    std::printf("%-34s -> %s\n", input, buffer);
}

int main()
{
    using namespace boost::decimal;

    // Each input is just above a tie between two neighbors, so it must round up
    show<decimal32_t>("1.00000050000000000000000001");
    show<decimal32_t>(".50000000000000000000000001e-101");
    show<decimal64_t>("1.0000000000000005000000001");
}
```
Before:
> 1.00000050000000000000000001       -> 1
> .50000000000000000000000001e-101   -> 0
> 1.0000000000000005000000001        -> 1

After:
> 1.00000050000000000000000001       -> 1.000001
> .50000000000000000000000001e-101   -> 1e-101
> 1.0000000000000005000000001        -> 1.000000000000001
… infinity (#1510)

Converting a decimal to float, double or long double gives 0 for -0 and
inf for -inf. to_float returns unsigned constants for zero and infinity
before it looks at the sign. They now keep it.

The roundtrip tests asserted the old result for -inf; they now expect
-inf. A new check in test_float_conversion.cpp covers both zeros and
both infinities, for all six decimal types and all three binary types.
```c++
#include <boost/decimal.hpp>
#include <cstdio>
#include <limits>

int main()
{
    using namespace boost::decimal;

    const decimal64_t negative_zero {-decimal64_t{0}};
    const decimal64_t negative_inf {-std::numeric_limits<decimal64_t>::infinity()};

    std::printf("double(-0)   = %g\n", static_cast<double>(negative_zero));
    std::printf("double(-inf) = %g\n", static_cast<double>(negative_inf));
    std::printf("float(-inf)  = %g\n", static_cast<double>(static_cast<float>(negative_inf)));
}
```
Before:
> double(-0)   = 0
> double(-inf) = inf
> float(-inf)  = inf

After:
> double(-0)   = -0
> double(-inf) = -inf
> float(-inf)  = -inf
operator> returned true and operator<= returned false for two
positive infinities. Compare the right operand when the left
operand is infinite. Add the same infinity tests for all types.

Each type decodes an infinity as a value above its largest finite
value, so the less functions order an infinity and a finite value
without help. Two infinities need a test, because the payload of an
infinity has no meaning, and the types decode an infinity as
different values. Test for NaN and for two infinities only when an
operand is not finite, and compare two infinities by sign. Write
the decimal32_t operators as the decimal64_t and decimal128_t
operators.

Fixed #1512
This PR has 11 commits. Each commit builds, and the tests pass after
each commit.

1. Expand local copies of the product operands: `mul_impl_dispatch`
takes the components by reference and expands local copies. Otherwise
MSVC 14.2 in C++14 mode multiplied the `lhs` significand from before
`expand_significand`.
2. Count the digits of an integer without a branch: `num_digits` gets
the number of digits from the bit length and one compare with the table
of powers of ten. This applies to the 32-bit, 64-bit, `uint128_t` and
`u256` versions.
3. Multiply two `uint128_t` values in fewer steps: `umul256` always
calculates the four 64-bit products. `divmod_pow10_uint128` gets the
high 128 bits of the product with the reciprocal from `umul256_hi`, in
place of the full `umul256` product.
4. Pack fast type results directly with scalar padding: scalar members
replace the `char` padding arrays of the fast types. The size and the
alignment of each type do not change. New `pack_in_range` overloads for
the fast types call `direct_init` for values in range.
5. Add fast type operands with exponents 0 to 3 apart in the aligned
kernel, as the IEEE types do.
6. Round quotients and products in the current mode: the division
finalizers and the multiplication finalizers round in the current mode.
In the directed modes, all division and multiplication paths call the
finalizers through out-of-line wrappers, and the constructor no longer
rounds the result again. The new test `test_mul_div_rounding` has
products and quotients of the six types in the five rounding modes, and
products with an integer.
7. Add in the directed modes with the aligned kernel:
`aligned_add_kernel` takes the rounding mode, and the additions with
exponents 0 to 3 apart use it in each mode. This includes
`decimal_fast32_t`. The new test `test_add_rounding` has sums in each
rounding mode.
8. Add operands with exponents 4 or more apart with a band kernel:
`band_add_kernel` divides the operand with the smaller exponent by
`10^d` one time and rounds the sum from the rest. If the sum gains or
loses a digit, the kernel keeps three guard digits and a sticky bit.
9. Divide to exactly p quotient digits: each division driver scales the
dividend by `10^(p-1)` when `lhs.sig >= rhs.sig`, and by `10^p` in the
other cases. Thus the quotient always has `p` digits, and the finalizers
no longer divide a quotient of `p + 1` digits by 10.
10. Divide a decimal32 product by a constant power of ten:
`mul_finalize_u64` divides the product by the constant `10^7` or `10^6`,
in place of the variable `pow10(extra)`. The compiler changes a division
by a constant into a multiplication.
11. Round without a branch: the round-half-to-even test `r > half || (r
== half && q odd)` becomes `r + (q & 1) > half`, which is the same test
for integers. The finalizers and the two addition kernels add the
round-up bit to the quotient, in place of an increment under a branch.

Fixes #1506

<details>
<summary>Correctness</summary>

- I compared the bits of `+`, `-`, `*` and `/` for 400000 pairs of each
type in the five rounding modes with `develop`. No value is different.
- The only differences are cohorts of exact sums of `decimal32_t`,
`decimal64_t` and `decimal128_t` in the directed modes. `develop`
expands short operands to full precision in these modes, so an exact sum
gets the smallest exponent. With the fix, an exact sum keeps the smaller
exponent of the two operands, as IEEE 754 prefers and as the default
mode already does.
- I compared 6000000 sums with exponents 4 to `p + 6` apart with Python
`decimal`, in the five rounding modes and the six types. All sums are
correct.
- The new tests `test_add_rounding` and `test_mul_div_rounding` use
results from Python `decimal`. They fail on mutants of the new rounding
code that the existing tests do not find.
- I built and ran each file in `test/` alone with GCC 16.2 and Clang
23.1, in C++14 and C++20, with `-Wall -Wextra -Werror`. 175 of 205 files
pass. The other 30 fail in the same way on `develop`, because they are
compile-fail tests, link tests with more than one file, benchmarks, or
tests that need extra flags. The arithmetic tests (`random_*_math`,
`test_add_rounding`, `test_mul_div_rounding`, `test_upward_rounding`,
`test_fenv`) also pass with `-m32`.

</details>

<details>
<summary>Speed</summary>

Each set has 16384 pairs. `stock` has random values. `full` has
significands with `p` digits and exponents 0 to 3 apart. `sameexp` has
equal exponents. `band` has exponents 4 to `p + 3` apart. `short` has
significands with few digits. The signs are random. The table gives ns
for each operation. I used Google Benchmark with `-O3` on an AMD Ryzen 9
3900X, with GCC 16.2 and Clang 23.1. Each value is the median of 5
rounds with 3 repetitions each.

| case | GCC develop | GCC fix | Clang develop | Clang fix |
|---|---|---|---|---|
| `add/decimal32_t/band/near` | 26.1 | 18.4 | 27.0 | 22.0 |
| `add/decimal32_t/short/up` | 41.4 | 9.5 | 45.4 | 10.9 |
| `add/decimal_fast32_t/full/near` | 25.1 | 17.2 | 24.9 | 15.6 |
| `add/decimal64_t/band/near` | 43.5 | 17.1 | 45.0 | 22.1 |
| `add/decimal64_t/short/up` | 46.2 | 9.9 | 47.9 | 11.9 |
| `add/decimal_fast64_t/full/near` | 27.0 | 18.2 | 28.6 | 15.2 |
| `mul/decimal_fast64_t/full/near` | 23.4 | 11.8 | 25.4 | 13.2 |
| `div/decimal64_t/full/up` | 58.9 | 16.1 | 53.9 | 18.8 |
| `add/decimal128_t/band/near` | 132.2 | 36.4 | 124.2 | 32.9 |
| `add/decimal128_t/full/up` | 81.4 | 30.8 | 67.2 | 31.9 |
| `add/decimal128_t/short/up` | 88.6 | 11.7 | 78.5 | 14.9 |
| `add/decimal_fast128_t/short/near` | 48.6 | 31.8 | 45.8 | 32.0 |
| `mul/decimal128_t/full/near` | 34.5 | 29.5 | 36.8 | 22.2 |
| `mul/decimal128_t/full/up` | 109.4 | 30.2 | 89.2 | 26.0 |
| `div/decimal128_t/full/near` | 64.2 | 56.7 | 67.1 | 54.8 |
| `div/decimal128_t/full/up` | 94.9 | 59.4 | 95.9 | 54.9 |

Over the 216 cases, the geometric mean of the time changes by -46% with
GCC and by -44% with Clang. These cases become slower by more than 3%:

- GCC, `decimal128_t` `add` of `short` operands in the default mode: +7%
(0.8 ns).
- GCC, `decimal128_t` `mul` of `short` operands: +8% to +10% (0.5 to 0.6
ns).
- Clang, `decimal_fast128_t` `add` of `stock` values: +13% (0.5 ns).

The full table follows.

</details>

<details>
<summary>All cases</summary>

| case | GCC develop | GCC fix | change | Clang develop | Clang fix |
change |
|---|---|---|---|---|---|---|
| `add/decimal32_t/stock/near` | 7.1 | 6.4 | -10% | 9.1 | 8.8 | -3% |
| `add/decimal_fast32_t/stock/near` | 8.9 | 7.3 | -18% | 5.5 | 5.0 | -9%
|
| `sub/decimal32_t/stock/near` | 9.4 | 8.4 | -11% | 9.2 | 9.1 | -1% |
| `sub/decimal_fast32_t/stock/near` | 9.0 | 7.9 | -12% | 7.9 | 7.9 | +0%
|
| `mul/decimal32_t/stock/near` | 19.0 | 14.6 | -23% | 24.2 | 17.2 | -29%
|
| `mul/decimal_fast32_t/stock/near` | 14.8 | 8.0 | -46% | 17.1 | 11.5 |
-33% |
| `div/decimal32_t/stock/near` | 25.7 | 17.7 | -31% | 30.4 | 19.9 | -35%
|
| `div/decimal_fast32_t/stock/near` | 17.0 | 6.6 | -61% | 19.0 | 10.9 |
-43% |
| `add/decimal32_t/full/near` | 23.5 | 17.9 | -24% | 22.8 | 18.7 | -18%
|
| `add/decimal_fast32_t/full/near` | 25.1 | 17.2 | -31% | 24.9 | 15.6 |
-37% |
| `sub/decimal32_t/full/near` | 23.4 | 18.4 | -21% | 23.1 | 19.5 | -16%
|
| `sub/decimal_fast32_t/full/near` | 25.2 | 17.9 | -29% | 25.7 | 15.8 |
-39% |
| `mul/decimal32_t/full/near` | 12.0 | 8.6 | -28% | 16.0 | 8.7 | -46% |
| `mul/decimal_fast32_t/full/near` | 12.5 | 4.0 | -68% | 16.0 | 6.9 |
-57% |
| `div/decimal32_t/full/near` | 18.6 | 11.7 | -37% | 25.8 | 13.4 | -48%
|
| `div/decimal_fast32_t/full/near` | 16.6 | 5.9 | -64% | 18.2 | 8.6 |
-53% |
| `add/decimal32_t/full/up` | 29.9 | 20.0 | -33% | 33.1 | 20.3 | -39% |
| `add/decimal_fast32_t/full/up` | 22.8 | 17.7 | -22% | 24.7 | 17.4 |
-30% |
| `sub/decimal32_t/full/up` | 30.3 | 20.0 | -34% | 32.9 | 20.5 | -38% |
| `sub/decimal_fast32_t/full/up` | 23.3 | 18.7 | -20% | 25.5 | 17.7 |
-31% |
| `mul/decimal32_t/full/up` | 20.6 | 9.4 | -54% | 22.7 | 10.1 | -56% |
| `mul/decimal_fast32_t/full/up` | 14.7 | 4.7 | -68% | 18.6 | 8.8 | -53%
|
| `div/decimal32_t/full/up` | 30.2 | 12.1 | -60% | 38.7 | 15.0 | -61% |
| `div/decimal_fast32_t/full/up` | 23.7 | 6.0 | -75% | 26.5 | 9.9 | -63%
|
| `add/decimal32_t/sameexp/near` | 14.0 | 11.7 | -16% | 13.1 | 11.5 |
-12% |
| `add/decimal_fast32_t/sameexp/near` | 17.4 | 12.1 | -30% | 17.2 | 11.6
| -33% |
| `sub/decimal32_t/sameexp/near` | 14.2 | 11.5 | -19% | 13.4 | 12.0 |
-10% |
| `sub/decimal_fast32_t/sameexp/near` | 17.6 | 13.2 | -25% | 17.8 | 11.7
| -34% |
| `mul/decimal32_t/sameexp/near` | 11.8 | 8.6 | -27% | 15.7 | 8.6 | -45%
|
| `mul/decimal_fast32_t/sameexp/near` | 12.4 | 4.0 | -68% | 16.0 | 6.9 |
-57% |
| `div/decimal32_t/sameexp/near` | 18.6 | 11.9 | -36% | 25.7 | 13.5 |
-47% |
| `div/decimal_fast32_t/sameexp/near` | 16.6 | 5.9 | -64% | 18.2 | 8.6 |
-53% |
| `add/decimal32_t/sameexp/up` | 22.8 | 13.2 | -42% | 22.4 | 12.8 | -43%
|
| `add/decimal_fast32_t/sameexp/up` | 18.4 | 11.7 | -36% | 18.9 | 12.7 |
-33% |
| `sub/decimal32_t/sameexp/up` | 22.7 | 13.1 | -42% | 22.2 | 13.4 | -40%
|
| `sub/decimal_fast32_t/sameexp/up` | 18.4 | 12.9 | -30% | 19.2 | 12.9 |
-33% |
| `mul/decimal32_t/sameexp/up` | 20.5 | 9.3 | -55% | 22.7 | 10.1 | -56%
|
| `mul/decimal_fast32_t/sameexp/up` | 14.6 | 4.6 | -68% | 18.6 | 8.8 |
-53% |
| `div/decimal32_t/sameexp/up` | 30.3 | 12.2 | -60% | 38.8 | 15.1 | -61%
|
| `div/decimal_fast32_t/sameexp/up` | 23.8 | 6.0 | -75% | 26.5 | 9.9 |
-63% |
| `add/decimal32_t/band/near` | 26.1 | 18.4 | -30% | 27.0 | 22.0 | -19%
|
| `add/decimal_fast32_t/band/near` | 24.8 | 15.6 | -37% | 23.5 | 15.7 |
-33% |
| `sub/decimal32_t/band/near` | 26.1 | 18.2 | -30% | 26.8 | 22.3 | -17%
|
| `sub/decimal_fast32_t/band/near` | 24.7 | 16.5 | -33% | 24.0 | 16.9 |
-30% |
| `mul/decimal32_t/band/near` | 12.0 | 8.7 | -28% | 15.7 | 8.7 | -45% |
| `mul/decimal_fast32_t/band/near` | 12.4 | 4.0 | -68% | 16.0 | 6.9 |
-57% |
| `div/decimal32_t/band/near` | 18.7 | 11.8 | -37% | 25.8 | 13.5 | -48%
|
| `div/decimal_fast32_t/band/near` | 16.6 | 5.9 | -64% | 18.3 | 8.6 |
-53% |
| `add/decimal32_t/band/up` | 30.3 | 17.9 | -41% | 32.6 | 22.1 | -32% |
| `add/decimal_fast32_t/band/up` | 23.9 | 16.1 | -33% | 23.0 | 15.8 |
-31% |
| `sub/decimal32_t/band/up` | 30.4 | 17.9 | -41% | 32.2 | 22.1 | -31% |
| `sub/decimal_fast32_t/band/up` | 24.1 | 16.6 | -31% | 23.8 | 17.1 |
-28% |
| `mul/decimal32_t/band/up` | 20.6 | 9.5 | -54% | 22.7 | 10.1 | -56% |
| `mul/decimal_fast32_t/band/up` | 14.7 | 4.6 | -69% | 18.6 | 8.8 | -53%
|
| `div/decimal32_t/band/up` | 30.2 | 12.2 | -60% | 39.0 | 15.1 | -61% |
| `div/decimal_fast32_t/band/up` | 23.7 | 6.0 | -75% | 26.6 | 9.8 | -63%
|
| `add/decimal32_t/short/near` | 9.2 | 8.3 | -10% | 9.6 | 9.4 | -2% |
| `add/decimal_fast32_t/short/near` | 21.3 | 17.6 | -17% | 21.3 | 16.0 |
-25% |
| `sub/decimal32_t/short/near` | 9.4 | 8.3 | -12% | 9.6 | 9.7 | +1% |
| `sub/decimal_fast32_t/short/near` | 21.1 | 18.1 | -14% | 22.0 | 16.2 |
-26% |
| `mul/decimal32_t/short/near` | 14.1 | 9.2 | -35% | 18.1 | 11.6 | -36%
|
| `mul/decimal_fast32_t/short/near` | 10.6 | 4.1 | -61% | 12.0 | 7.0 |
-42% |
| `div/decimal32_t/short/near` | 30.9 | 16.0 | -48% | 35.0 | 18.2 | -48%
|
| `div/decimal_fast32_t/short/near` | 15.9 | 5.9 | -63% | 17.7 | 8.6 |
-51% |
| `add/decimal32_t/short/up` | 41.4 | 9.5 | -77% | 45.4 | 10.9 | -76% |
| `add/decimal_fast32_t/short/up` | 21.7 | 18.2 | -16% | 24.8 | 17.7 |
-29% |
| `sub/decimal32_t/short/up` | 41.5 | 9.6 | -77% | 45.5 | 11.5 | -75% |
| `sub/decimal_fast32_t/short/up` | 22.3 | 18.8 | -16% | 25.9 | 17.9 |
-31% |
| `mul/decimal32_t/short/up` | 17.7 | 9.2 | -48% | 20.9 | 12.4 | -41% |
| `mul/decimal_fast32_t/short/up` | 15.9 | 4.7 | -70% | 20.4 | 8.9 |
-56% |
| `div/decimal32_t/short/up` | 43.7 | 16.5 | -62% | 50.3 | 19.7 | -61% |
| `div/decimal_fast32_t/short/up` | 24.6 | 6.0 | -76% | 27.3 | 9.8 |
-64% |
| `add/decimal64_t/stock/near` | 6.6 | 5.4 | -18% | 7.5 | 6.6 | -12% |
| `add/decimal_fast64_t/stock/near` | 8.0 | 6.8 | -15% | 5.0 | 3.8 |
-24% |
| `sub/decimal64_t/stock/near` | 10.0 | 7.9 | -21% | 7.7 | 6.7 | -13% |
| `sub/decimal_fast64_t/stock/near` | 11.9 | 5.9 | -50% | 7.7 | 6.2 |
-19% |
| `mul/decimal64_t/stock/near` | 27.0 | 20.7 | -23% | 27.7 | 19.3 | -30%
|
| `mul/decimal_fast64_t/stock/near` | 24.6 | 15.8 | -36% | 25.8 | 15.4 |
-40% |
| `div/decimal64_t/stock/near` | 34.8 | 20.9 | -40% | 33.6 | 21.2 | -37%
|
| `div/decimal_fast64_t/stock/near` | 27.5 | 13.0 | -53% | 27.0 | 14.1 |
-48% |
| `add/decimal64_t/full/near` | 26.9 | 18.7 | -30% | 26.2 | 19.2 | -27%
|
| `add/decimal_fast64_t/full/near` | 27.0 | 18.2 | -33% | 28.6 | 15.2 |
-47% |
| `sub/decimal64_t/full/near` | 26.9 | 18.2 | -32% | 26.4 | 19.2 | -27%
|
| `sub/decimal_fast64_t/full/near` | 26.0 | 17.1 | -34% | 29.1 | 14.8 |
-49% |
| `mul/decimal64_t/full/near` | 25.1 | 17.0 | -32% | 24.6 | 15.7 | -36%
|
| `mul/decimal_fast64_t/full/near` | 23.4 | 11.8 | -50% | 25.4 | 13.2 |
-48% |
| `div/decimal64_t/full/near` | 29.7 | 16.0 | -46% | 28.8 | 18.0 | -38%
|
| `div/decimal_fast64_t/full/near` | 26.6 | 12.9 | -52% | 27.0 | 13.0 |
-52% |
| `add/decimal64_t/full/up` | 38.7 | 19.6 | -49% | 38.1 | 20.7 | -46% |
| `add/decimal_fast64_t/full/up` | 30.5 | 21.0 | -31% | 30.8 | 20.0 |
-35% |
| `sub/decimal64_t/full/up` | 38.6 | 19.7 | -49% | 38.1 | 20.7 | -46% |
| `sub/decimal_fast64_t/full/up` | 29.6 | 20.4 | -31% | 31.2 | 20.3 |
-35% |
| `mul/decimal64_t/full/up` | 34.4 | 18.8 | -45% | 32.6 | 17.7 | -46% |
| `mul/decimal_fast64_t/full/up` | 33.1 | 14.3 | -57% | 30.6 | 15.7 |
-49% |
| `div/decimal64_t/full/up` | 58.9 | 16.1 | -73% | 53.9 | 18.8 | -65% |
| `div/decimal_fast64_t/full/up` | 48.8 | 13.2 | -73% | 49.4 | 13.0 |
-74% |
| `add/decimal64_t/sameexp/near` | 15.1 | 12.1 | -20% | 14.4 | 12.2 |
-15% |
| `add/decimal_fast64_t/sameexp/near` | 17.4 | 12.9 | -26% | 19.3 | 11.3
| -41% |
| `sub/decimal64_t/sameexp/near` | 15.1 | 11.7 | -23% | 14.5 | 12.4 |
-14% |
| `sub/decimal_fast64_t/sameexp/near` | 17.1 | 12.9 | -25% | 20.0 | 11.2
| -44% |
| `mul/decimal64_t/sameexp/near` | 25.1 | 16.6 | -34% | 24.5 | 15.7 |
-36% |
| `mul/decimal_fast64_t/sameexp/near` | 23.4 | 11.7 | -50% | 25.4 | 13.2
| -48% |
| `div/decimal64_t/sameexp/near` | 29.7 | 16.1 | -46% | 28.8 | 17.9 |
-38% |
| `div/decimal_fast64_t/sameexp/near` | 26.6 | 12.9 | -52% | 26.7 | 13.0
| -51% |
| `add/decimal64_t/sameexp/up` | 25.7 | 12.6 | -51% | 24.5 | 13.8 | -44%
|
| `add/decimal_fast64_t/sameexp/up` | 21.9 | 14.2 | -35% | 21.3 | 15.5 |
-27% |
| `sub/decimal64_t/sameexp/up` | 25.4 | 12.8 | -50% | 24.9 | 14.0 | -44%
|
| `sub/decimal_fast64_t/sameexp/up` | 21.3 | 14.4 | -32% | 21.6 | 16.0 |
-26% |
| `mul/decimal64_t/sameexp/up` | 34.3 | 18.7 | -45% | 32.5 | 17.7 | -46%
|
| `mul/decimal_fast64_t/sameexp/up` | 33.1 | 14.3 | -57% | 30.6 | 15.7 |
-49% |
| `div/decimal64_t/sameexp/up` | 58.9 | 16.1 | -73% | 54.0 | 18.8 | -65%
|
| `div/decimal_fast64_t/sameexp/up` | 48.9 | 13.2 | -73% | 49.4 | 13.0 |
-74% |
| `add/decimal64_t/band/near` | 43.5 | 17.1 | -61% | 45.0 | 22.1 | -51%
|
| `add/decimal_fast64_t/band/near` | 47.4 | 15.7 | -67% | 41.3 | 15.1 |
-63% |
| `sub/decimal64_t/band/near` | 44.1 | 17.3 | -61% | 44.9 | 22.4 | -50%
|
| `sub/decimal_fast64_t/band/near` | 46.3 | 15.7 | -66% | 41.4 | 15.5 |
-63% |
| `mul/decimal64_t/band/near` | 25.0 | 16.7 | -33% | 24.6 | 15.7 | -36%
|
| `mul/decimal_fast64_t/band/near` | 23.4 | 11.8 | -50% | 25.4 | 13.2 |
-48% |
| `div/decimal64_t/band/near` | 29.7 | 16.1 | -46% | 28.8 | 18.0 | -38%
|
| `div/decimal_fast64_t/band/near` | 26.5 | 12.9 | -51% | 26.8 | 13.0 |
-51% |
| `add/decimal64_t/band/up` | 46.7 | 17.0 | -64% | 45.6 | 22.0 | -52% |
| `add/decimal_fast64_t/band/up` | 41.5 | 15.7 | -62% | 38.9 | 15.2 |
-61% |
| `sub/decimal64_t/band/up` | 46.2 | 17.2 | -63% | 45.6 | 22.1 | -52% |
| `sub/decimal_fast64_t/band/up` | 40.7 | 15.6 | -62% | 39.4 | 15.5 |
-61% |
| `mul/decimal64_t/band/up` | 34.3 | 18.8 | -45% | 32.6 | 17.7 | -46% |
| `mul/decimal_fast64_t/band/up` | 33.0 | 14.3 | -57% | 30.7 | 15.7 |
-49% |
| `div/decimal64_t/band/up` | 58.9 | 16.1 | -73% | 53.9 | 18.7 | -65% |
| `div/decimal_fast64_t/band/up` | 48.8 | 13.1 | -73% | 49.5 | 13.0 |
-74% |
| `add/decimal64_t/short/near` | 10.1 | 8.9 | -12% | 10.3 | 10.5 | +2% |
| `add/decimal_fast64_t/short/near` | 22.9 | 18.6 | -19% | 24.9 | 15.9 |
-36% |
| `sub/decimal64_t/short/near` | 10.6 | 8.9 | -16% | 10.4 | 10.7 | +3% |
| `sub/decimal_fast64_t/short/near` | 21.8 | 17.6 | -19% | 25.6 | 15.5 |
-39% |
| `mul/decimal64_t/short/near` | 4.5 | 4.6 | +2% | 6.8 | 6.7 | -1% |
| `mul/decimal_fast64_t/short/near` | 22.2 | 12.0 | -46% | 20.7 | 13.2 |
-36% |
| `div/decimal64_t/short/near` | 38.6 | 23.8 | -38% | 37.7 | 25.4 | -33%
|
| `div/decimal_fast64_t/short/near` | 26.1 | 12.9 | -51% | 26.2 | 13.0 |
-50% |
| `add/decimal64_t/short/up` | 46.2 | 9.9 | -79% | 47.9 | 11.9 | -75% |
| `add/decimal_fast64_t/short/up` | 30.8 | 21.2 | -31% | 31.7 | 20.2 |
-36% |
| `sub/decimal64_t/short/up` | 45.4 | 10.1 | -78% | 47.8 | 12.0 | -75% |
| `sub/decimal_fast64_t/short/up` | 29.7 | 20.5 | -31% | 32.1 | 20.4 |
-36% |
| `mul/decimal64_t/short/up` | 4.5 | 4.6 | +2% | 6.8 | 6.7 | -1% |
| `mul/decimal_fast64_t/short/up` | 32.7 | 14.5 | -56% | 30.4 | 15.7 |
-48% |
| `div/decimal64_t/short/up` | 69.2 | 24.8 | -64% | 64.4 | 26.5 | -59% |
| `div/decimal_fast64_t/short/up` | 50.1 | 13.2 | -74% | 50.1 | 13.0 |
-74% |
| `add/decimal128_t/stock/near` | 4.2 | 3.9 | -7% | 6.8 | 6.4 | -6% |
| `add/decimal_fast128_t/stock/near` | 8.3 | 3.8 | -54% | 3.9 | 4.4 |
+13% |
| `sub/decimal128_t/stock/near` | 6.6 | 6.1 | -8% | 6.8 | 6.5 | -4% |
| `sub/decimal_fast128_t/stock/near` | 13.1 | 6.8 | -48% | 8.5 | 8.7 |
+2% |
| `mul/decimal128_t/stock/near` | 38.6 | 34.2 | -11% | 40.4 | 26.7 |
-34% |
| `mul/decimal_fast128_t/stock/near` | 42.8 | 38.4 | -10% | 41.1 | 32.8
| -20% |
| `div/decimal128_t/stock/near` | 70.0 | 62.1 | -11% | 74.1 | 60.8 |
-18% |
| `div/decimal_fast128_t/stock/near` | 60.9 | 54.3 | -11% | 67.5 | 51.5
| -24% |
| `add/decimal128_t/full/near` | 39.5 | 29.1 | -26% | 41.3 | 31.5 | -24%
|
| `add/decimal_fast128_t/full/near` | 48.0 | 31.1 | -35% | 45.8 | 30.9 |
-33% |
| `sub/decimal128_t/full/near` | 39.9 | 31.6 | -21% | 42.0 | 30.9 | -26%
|
| `sub/decimal_fast128_t/full/near` | 50.7 | 31.1 | -39% | 47.3 | 31.8 |
-33% |
| `mul/decimal128_t/full/near` | 34.5 | 29.5 | -14% | 36.8 | 22.2 | -40%
|
| `mul/decimal_fast128_t/full/near` | 31.0 | 26.9 | -13% | 31.9 | 21.2 |
-34% |
| `div/decimal128_t/full/near` | 64.2 | 56.7 | -12% | 67.1 | 54.8 | -18%
|
| `div/decimal_fast128_t/full/near` | 60.0 | 51.0 | -15% | 67.3 | 48.4 |
-28% |
| `add/decimal128_t/full/up` | 81.4 | 30.8 | -62% | 67.2 | 31.9 | -53% |
| `add/decimal_fast128_t/full/up` | 71.6 | 37.4 | -48% | 67.7 | 34.9 |
-48% |
| `sub/decimal128_t/full/up` | 82.1 | 32.8 | -60% | 67.2 | 31.6 | -53% |
| `sub/decimal_fast128_t/full/up` | 75.7 | 38.3 | -49% | 69.9 | 36.4 |
-48% |
| `mul/decimal128_t/full/up` | 109.4 | 30.2 | -72% | 89.2 | 26.0 | -71%
|
| `mul/decimal_fast128_t/full/up` | 92.7 | 29.9 | -68% | 82.7 | 24.8 |
-70% |
| `div/decimal128_t/full/up` | 94.9 | 59.4 | -37% | 95.9 | 54.9 | -43% |
| `div/decimal_fast128_t/full/up` | 85.9 | 53.6 | -38% | 88.4 | 48.9 |
-45% |
| `add/decimal128_t/sameexp/near` | 18.9 | 16.1 | -15% | 21.3 | 18.8 |
-12% |
| `add/decimal_fast128_t/sameexp/near` | 31.7 | 18.5 | -42% | 27.3 |
19.0 | -30% |
| `sub/decimal128_t/sameexp/near` | 19.1 | 16.1 | -16% | 21.8 | 18.5 |
-15% |
| `sub/decimal_fast128_t/sameexp/near` | 35.5 | 18.6 | -48% | 28.9 |
19.4 | -33% |
| `mul/decimal128_t/sameexp/near` | 34.5 | 29.7 | -14% | 36.8 | 22.3 |
-39% |
| `mul/decimal_fast128_t/sameexp/near` | 31.3 | 27.0 | -14% | 32.0 |
21.3 | -33% |
| `div/decimal128_t/sameexp/near` | 64.1 | 56.7 | -12% | 67.0 | 54.8 |
-18% |
| `div/decimal_fast128_t/sameexp/near` | 60.0 | 50.9 | -15% | 67.3 |
48.5 | -28% |
| `add/decimal128_t/sameexp/up` | 42.3 | 16.8 | -60% | 37.1 | 19.4 |
-48% |
| `add/decimal_fast128_t/sameexp/up` | 35.5 | 24.5 | -31% | 40.9 | 22.6
| -45% |
| `sub/decimal128_t/sameexp/up` | 42.6 | 17.0 | -60% | 37.8 | 19.4 |
-49% |
| `sub/decimal_fast128_t/sameexp/up` | 40.0 | 25.4 | -36% | 43.0 | 24.3
| -43% |
| `mul/decimal128_t/sameexp/up` | 108.8 | 30.3 | -72% | 89.0 | 26.1 |
-71% |
| `mul/decimal_fast128_t/sameexp/up` | 93.5 | 30.1 | -68% | 82.6 | 24.9
| -70% |
| `div/decimal128_t/sameexp/up` | 94.9 | 59.5 | -37% | 95.7 | 54.9 |
-43% |
| `div/decimal_fast128_t/sameexp/up` | 85.8 | 53.6 | -38% | 88.3 | 49.0
| -45% |
| `add/decimal128_t/band/near` | 132.2 | 36.4 | -72% | 124.2 | 32.9 |
-74% |
| `add/decimal_fast128_t/band/near` | 128.4 | 35.0 | -73% | 118.6 | 34.6
| -71% |
| `sub/decimal128_t/band/near` | 134.6 | 37.1 | -72% | 125.5 | 33.1 |
-74% |
| `sub/decimal_fast128_t/band/near` | 131.7 | 35.4 | -73% | 120.4 | 36.2
| -70% |
| `mul/decimal128_t/band/near` | 34.4 | 29.5 | -14% | 36.8 | 22.2 | -40%
|
| `mul/decimal_fast128_t/band/near` | 31.2 | 27.0 | -13% | 31.9 | 21.2 |
-34% |
| `div/decimal128_t/band/near` | 64.1 | 56.7 | -12% | 67.0 | 55.0 | -18%
|
| `div/decimal_fast128_t/band/near` | 59.8 | 50.9 | -15% | 67.4 | 48.4 |
-28% |
| `add/decimal128_t/band/up` | 131.4 | 36.7 | -72% | 118.8 | 32.8 | -72%
|
| `add/decimal_fast128_t/band/up` | 127.3 | 35.3 | -72% | 113.5 | 34.4 |
-70% |
| `sub/decimal128_t/band/up` | 132.6 | 37.5 | -72% | 120.0 | 33.2 | -72%
|
| `sub/decimal_fast128_t/band/up` | 131.4 | 35.8 | -73% | 115.2 | 36.1 |
-69% |
| `mul/decimal128_t/band/up` | 108.8 | 30.2 | -72% | 89.1 | 26.1 | -71%
|
| `mul/decimal_fast128_t/band/up` | 93.0 | 29.9 | -68% | 82.7 | 24.8 |
-70% |
| `div/decimal128_t/band/up` | 95.0 | 59.3 | -38% | 96.1 | 54.9 | -43% |
| `div/decimal_fast128_t/band/up` | 85.8 | 53.4 | -38% | 88.5 | 49.0 |
-45% |
| `add/decimal128_t/short/near` | 11.7 | 12.5 | +7% | 14.5 | 14.6 | +1%
|
| `add/decimal_fast128_t/short/near` | 48.6 | 31.8 | -35% | 45.8 | 32.0
| -30% |
| `sub/decimal128_t/short/near` | 12.0 | 12.3 | +3% | 14.7 | 14.1 | -4%
|
| `sub/decimal_fast128_t/short/near` | 51.1 | 31.7 | -38% | 47.3 | 32.9
| -30% |
| `mul/decimal128_t/short/near` | 6.2 | 6.8 | +10% | 9.3 | 9.2 | -1% |
| `mul/decimal_fast128_t/short/near` | 27.8 | 27.0 | -3% | 28.1 | 20.9 |
-26% |
| `div/decimal128_t/short/near` | 78.9 | 70.8 | -10% | 82.2 | 63.9 |
-22% |
| `div/decimal_fast128_t/short/near` | 60.4 | 51.4 | -15% | 66.9 | 48.8
| -27% |
| `add/decimal128_t/short/up` | 88.6 | 11.7 | -87% | 78.5 | 14.9 | -81%
|
| `add/decimal_fast128_t/short/up` | 72.9 | 34.5 | -53% | 68.9 | 35.7 |
-48% |
| `sub/decimal128_t/short/up` | 91.5 | 11.7 | -87% | 78.4 | 14.6 | -81%
|
| `sub/decimal_fast128_t/short/up` | 77.1 | 35.0 | -55% | 71.2 | 37.0 |
-48% |
| `mul/decimal128_t/short/up` | 6.2 | 6.7 | +8% | 9.3 | 9.2 | -1% |
| `mul/decimal_fast128_t/short/up` | 93.1 | 26.8 | -71% | 82.4 | 24.7 |
-70% |
| `div/decimal128_t/short/up` | 111.5 | 73.1 | -34% | 110.4 | 64.4 |
-42% |
| `div/decimal_fast128_t/short/up` | 87.3 | 54.3 | -38% | 89.5 | 49.4 |
-45% |

</details>

<details>
<summary>Code size</summary>

Bytes of `.text` in an object file with one function that returns `a +
b`, `a * b` or `a / b`, `-O2`:

| function | GCC develop | GCC fix | Clang develop | Clang fix |
|---|---|---|---|---|
| `decimal32_t` `+` | 8671 | 9625 | 5528 | 7153 |
| `decimal32_t` `*` | 6883 | 5460 | 4468 | 4537 |
| `decimal32_t` `/` | 7513 | 3580 | 5090 | 3831 |
| `decimal64_t` `+` | 13368 | 12372 | 9466 | 9538 |
| `decimal64_t` `*` | 8564 | 7432 | 6398 | 6069 |
| `decimal64_t` `/` | 10237 | 6202 | 7041 | 4285 |
| `decimal128_t` `+` | 20728 | 21239 | 13862 | 15963 |
| `decimal128_t` `*` | 17308 | 16678 | 12497 | 13408 |
| `decimal128_t` `/` | 16155 | 11665 | 10896 | 9440 |
| `decimal_fast32_t` `+` | 4418 | 5753 | 3777 | 5729 |
| `decimal_fast32_t` `*` | 3128 | 2848 | 3472 | 3770 |
| `decimal_fast32_t` `/` | 3647 | 1716 | 3330 | 2480 |
| `decimal_fast64_t` `+` | 9230 | 8515 | 6722 | 7698 |
| `decimal_fast64_t` `*` | 4798 | 4137 | 4306 | 4275 |
| `decimal_fast64_t` `/` | 5910 | 2192 | 4607 | 2377 |
| `decimal_fast128_t` `+` | 15495 | 18184 | 11815 | 15005 |
| `decimal_fast128_t` `*` | 12448 | 14180 | 9704 | 10565 |
| `decimal_fast128_t` `/` | 10150 | 7212 | 7993 | 6804 |

</details>
- The old `sqrt` found the root with Newton steps that divide 128-bit
and 256-bit integers. It also converted the input and the result with
decimal multiplications.
- The new `sqrt` writes x as N * 10^j with an even j, so that sqrt(N)
has p digits.
- A table of 1 / sqrt with a parabola term gives a first estimate, as in
`cbrt`. Goldschmidt steps that only multiply then give binary estimates
of sqrt(N) and 1 / sqrt(N).
- For `decimal128_t`, one Newton step on the root uses the reciprocal in
place of a division. Its fraction gives the result unless the root is
near an integer or a half.
- In the other cases, the remainder N - z^2 gives the floor and the
rounding in the current mode. The estimate is within one unit of the
floor, so the remainder is small and the low word of N holds it.
- The function builds the result one time, with `pack_in_range`, as
`cbrt` does. A root that rounds up to 10^p loses one digit first.
- The old table, the reciprocal approximations and the three
implementation files go away, and also `mul128_by_64` in `u256.hpp`,
because only the old `sqrt` used it.
- `sqrt` and `cbrt` share `umul128` in `u256.hpp`, the 64-bit product
that each had as its own copy.
- With `BOOST_DECIMAL_FAST_MATH`, `sqrt` of a zero returns zero before
the root search. Before, a zero went into the search, and a shift by 64
and the table index were out of range. `test_fast_math` checks it.
- `test_sqrt_rounding` gets a `decimal128_t` case that starts from an
estimate above the floor, and cases whose root rounds up to 10^p.
- `doc/decimal_float_and_sqrt.md` sections 5 to 7 describe the new
method, its files and its speed.

<details>
<summary>Correctness</summary>

- The results are the same bits as on `develop` (f3e436f). I compared a
hash of the results in the five rounding modes:
- All `decimal32_t` significands from 1000000 to 9999999, with the
exponents 0, 1, -101 and 90.
- 1000000 random values of each of the six types in each mode, with
random exponents.
- For each random value, a square q^2 and its neighbors q^2 - 1 and q^2
+ 1, also with random exponents.
- Zeros, infinities, NaN, negative values, the smallest and largest
values, and powers of ten.
- For `decimal128_t`, y and t are within 2^-59.5. Then the Newton step
is within 0.022 of the root, and the fast path needs a margin of 2^-5. A
check of 10^9 random inputs gives 2^-59.7.
- `test_sqrt`, `test_sqrt_rounding`, `test_sqrt_rounding_upward` and
`test_fast_math` pass with GCC 16.2 and Clang 23.1, in C++14, C++17 and
C++20, at `-O2` and `-O3`, with `-Wall -Wextra -Wconversion
-Wsign-conversion -Wold-style-cast -Wshadow -Werror`.

- `test_fast_math` also passes with `-fsanitize=undefined,address`.
Without the zero test, UBSan stops it with "shift exponent 64 is too
large".

</details>

<details>
<summary>Speed</summary>

The table gives ns for each call. The input has 1000000 random values
with `p` digits and exponents from -40 to 40. The mode is the default
mode. I used `-O3` on an AMD Ryzen 9 3900X, with GCC 16.2 and Clang
23.1, on one core. Each value is the best of 3 runs.

| type | GCC develop | GCC fix | Clang develop | Clang fix |
|---|---|---|---|---|
| `decimal32_t` | 55.8 | 15.6 | 75.4 | 19.7 |
| `decimal64_t` | 123.2 | 18.8 | 141.1 | 23.0 |
| `decimal128_t` | 423.8 | 38.8 | 395.9 | 43.3 |
| `decimal_fast32_t` | 49.3 | 11.3 | 55.1 | 13.9 |
| `decimal_fast64_t` | 115.3 | 14.7 | 109.3 | 13.8 |
| `decimal_fast128_t` | 412.6 | 33.3 | 389.1 | 38.8 |

</details>

<details>
<summary>Size</summary>

One function calls `sqrt` for `decimal32_t`, `decimal64_t` and
`decimal128_t`. The table gives the `.text` size of its object file at
`-O2`, in bytes.

| compiler | develop | fix |
|---|---|---|
| GCC 16.2 | 79623 | 25226 |
| Clang 23.1 | 65058 | 21521 |

</details>
This PR has 2 commits. Each commit builds, and the tests pass after each
commit.

1. Change only the exponent field in scalbn and scalbln: for a finite,
canonical value with a new exponent in range, the result comes from a
change to the exponent field only. A helper that is not inlined does the
other cases. It clamps the exponent to a limit that gives the same
overflow or underflow, it gives a canonical zero for a non-canonical
significand, and it gives a quiet NaN for a signaling NaN. For zero, the
exponent now changes too, as IEEE 754 gives for scaleB and as Intel
libbid does.
2. Keep the largest exponent when a new exponent is too large: on
exponent overflow with room in the coefficient, the constructors of
`decimal32_t`, `decimal64_t` and `decimal128_t` add only the excess
digits. This also corrects the quantum exponent of products and of
parsed values.

Fixes #1518

<details>
<summary>Correctness</summary>

- The reproducer of #1518 gives the same bits as Intel libbid 2.0 Update
4 for all rows, and UBSan reports no error.
- I compared the bits of `scalbn` with `develop` for 18000000 calls in
the five rounding modes, with exponents in the range that `develop`
handles correctly. Only the exponent of zero is different.
- I compared `scalbn` with Intel libbid on 3400000 special and
non-canonical encodings and on 6000000 values near the largest exponent.
No value is different.
- I compared `+`, `-`, `*`, `/` and `fma` near the exponent limits with
Intel libbid. With the second commit, the quantum exponent of
`decimal32_t` and `decimal64_t` products on overflow is the same as
Intel libbid (410 and 621 differences in 400000 on `develop`).
- `test_cmath`, `test_construction_exponent_range` and
`github_issue_805` pass with GCC 16.2 and Clang 23.1, in C++14, C++17
and C++20, with ASan and UBSan and `-Werror`. They also pass with
`-m32`, with `BOOST_DECIMAL_FAST_MATH`, and with MSVC 19.4 x64 and x86
in C++14, C++17 and C++20 with `/W4 /WX`.
- An NVCC kernel for the six types gives the same bits as the host.

</details>

<details>
<summary>Speed</summary>

ns for each `scalbn` call, 65536 inputs, `-O3`, AMD Ryzen 9 3900X.

| type | GCC develop | GCC fix | Clang develop | Clang fix | Intel
libbid |
|---|---|---|---|---|---|
| `decimal32_t` | 9.15 | 2.08 | 11.3 | 2.00 | 3.13 |
| `decimal64_t` | 9.55 | 1.80 | 11.5 | 1.66 | 3.34 |
| `decimal128_t` | 14.6 | 1.91 | 22.5 | 1.58 | 5.01 |
| `decimal_fast32_t` | 3.19 | 1.82 | 2.39 | 1.11 | |
| `decimal_fast64_t` | 11.8 | 1.23 | 1.90 | 1.09 | |
| `decimal_fast128_t` | 15.1 | 1.49 | 6.11 | 1.30 | |

MSVC 19.4 x64, ns for each call, develop to fix: `decimal32_t` 12.5 to
2.1, `decimal64_t` 13.5 to 2.1, `decimal128_t` 19.3 to 1.9,
`decimal_fast32_t` 2.6 to 2.2, `decimal_fast64_t` 4.0 to 2.4,
`decimal_fast128_t` 9.6 to 2.7.

</details>
This PR has 1 commit.

- `acosh` gives `-NaN` for zero, a subnormal and `-inf`, as it does for
the other arguments below 1.
- `atanh` gives NaN for an infinity, as it does for the other arguments
outside `[-1, 1]`.
- `test_atanh` expects NaN for an infinity, and a new test covers the
edges of both domains for the six types.

C17 F.10.2.1 and F.10.2.3 and IEEE 754-2019 9.2 give a NaN for an
argument outside the domain. Intel libbid and glibc give NaN for the
same arguments.

Fixes #1520

<details>
<summary>Correctness</summary>

- The reproducer of #1520 gives NaN for all rows with the fix:

```
decimal32_t: acosh(denorm_min) = -nan(ind), acosh(-denorm_min) = -nan(ind), acosh(-inf) = -nan(ind), atanh(inf) = nan, atanh(-inf) = nan
decimal64_t: acosh(denorm_min) = -nan(ind), acosh(-denorm_min) = -nan(ind), acosh(-inf) = -nan(ind), atanh(inf) = nan, atanh(-inf) = nan
decimal128_t: acosh(denorm_min) = -nan(ind), acosh(-denorm_min) = -nan(ind), acosh(-inf) = -nan(ind), atanh(inf) = nan, atanh(-inf) = nan
decimal_fast32_t: acosh(denorm_min) = -nan(ind), acosh(-denorm_min) = -nan(ind), acosh(-inf) = -nan(ind), atanh(inf) = nan, atanh(-inf) = nan
decimal_fast64_t: acosh(denorm_min) = -nan(ind), acosh(-denorm_min) = -nan(ind), acosh(-inf) = -nan(ind), atanh(inf) = nan, atanh(-inf) = nan
decimal_fast128_t: acosh(denorm_min) = -nan(ind), acosh(-denorm_min) = -nan(ind), acosh(-inf) = -nan(ind), atanh(inf) = nan, atanh(-inf) = nan
```

- `github_issue_1520` fails on `develop` with 30 errors and passes with
the fix.
- `github_issue_1520`, `test_acosh` and `test_atanh` pass with GCC 16.2
and Clang 23.1, in C++14 and C++20, with `-Wall -Wextra -Werror`. The
new test also passes with `BOOST_DECIMAL_FAST_MATH`.
- With `BOOST_DECIMAL_FAST_MATH`, the code paths do not change: `isinf`
is always false, and `acosh` does not use the changed branch.

</details>

<details>
<summary>Speed</summary>

The change applies only to values that are not normal. Cycles for each
call, 512 normal inputs, `-O2`, AMD Ryzen 9 3900X, core 3:

| function | type | GCC develop | GCC fix | Clang develop | Clang fix |
|---|---|---|---|---|---|
| `acosh` | `decimal32_t` | 2716 | 2706 | 2922 | 2924 |
| `acosh` | `decimal64_t` | 8798 | 8804 | 7725 | 7716 |
| `acosh` | `decimal128_t` | 13594 | 13575 | 12695 | 12777 |
| `atanh` | `decimal32_t` | 3189 | 3180 | 3465 | 3466 |
| `atanh` | `decimal64_t` | 11626 | 11598 | 10120 | 10117 |
| `atanh` | `decimal128_t` | 19139 | 19223 | 17609 | 17507 |

The differences are below 1%, which is the noise of the measurement.

</details>
Bumps [handlebars](https://github.com/handlebars-lang/handlebars.js)
from 4.7.9 to 4.7.10.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/handlebars-lang/handlebars.js/releases">handlebars's
releases</a>.</em></p>
<blockquote>
<h2>v4.7.10</h2>
<ul>
<li>security: Sanitize the source map URL when minifying - f37c599</li>
<li>security: Iterate lazily in #each and strip whitespace in linear
time - 812c226</li>
<li>security: Escape <code>&lt;!--</code> and <code>&lt;script</code> in
precompiled output - 609d1b1 GHSA-xw65-4hp5-5hc7</li>
<li>security: Don't trust special properties on context data - ceec388
GHSA-p8wg-vrv2-v86f</li>
<li>security: Only compile partials that are template strings -
c28ee7a</li>
<li>security: Only dispatch known node types in the Compiler and Visitor
- 7d501a5</li>
<li>security: Validate AST values in the compiler instead of the parser
- 703fdcc GHSA-8r5x-fm3f-whwj</li>
<li>Clarify that --root does not restrict filesystem access -
0fcf25c</li>
<li>Bump minimist to ^1.2.8 - ea8ed82</li>
<li>Fix Ruby component publishing documentation - d069c1c</li>
<li>Fix Composer component definition - 6714e07</li>
</ul>
<p>Compatibility notes:</p>
<ul>
<li><code>{{#each}}</code> iterates iterables such as <code>Map</code>,
<code>Set</code> and generators lazily, like <code>for...of</code>,
instead of
copying them into an array first. Values added to the iterable while the
block renders are now visited too.</li>
</ul>
<p><a
href="https://github.com/handlebars-lang/handlebars.js/compare/v4.7.9...v4.7.10">Commits</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/handlebars-lang/handlebars.js/blob/v4.7.10/release-notes.md">handlebars's
changelog</a>.</em></p>
<blockquote>
<h2>v4.7.10 - October 5th, 2026</h2>
<ul>
<li>security: Sanitize the source map URL when minifying - f37c599</li>
<li>security: Iterate lazily in #each and strip whitespace in linear
time - 812c226</li>
<li>security: Escape <code>&lt;!--</code> and <code>&lt;script</code> in
precompiled output - 609d1b1 GHSA-xw65-4hp5-5hc7</li>
<li>security: Don't trust special properties on context data - ceec388
GHSA-p8wg-vrv2-v86f</li>
<li>security: Only compile partials that are template strings -
c28ee7a</li>
<li>security: Only dispatch known node types in the Compiler and Visitor
- 7d501a5</li>
<li>security: Validate AST values in the compiler instead of the parser
- 703fdcc GHSA-8r5x-fm3f-whwj</li>
<li>Clarify that --root does not restrict filesystem access -
0fcf25c</li>
<li>Bump minimist to ^1.2.8 - ea8ed82</li>
<li>Fix Ruby component publishing documentation - d069c1c</li>
<li>Fix Composer component definition - 6714e07</li>
</ul>
<p>Compatibility notes:</p>
<ul>
<li><code>{{#each}}</code> iterates iterables such as <code>Map</code>,
<code>Set</code> and generators lazily, like <code>for...of</code>,
instead of
copying them into an array first. Values added to the iterable while the
block renders are now visited too.</li>
</ul>
<p><a
href="https://github.com/handlebars-lang/handlebars.js/compare/v4.7.9...v4.7.10">Commits</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/handlebars-lang/handlebars.js/commit/45ce15255e0f430ac6d19742b18e316b7539264e"><code>45ce152</code></a>
v4.7.10</li>
<li><a
href="https://github.com/handlebars-lang/handlebars.js/commit/e47b2365d5656ab5689ae226aef90a485a90c5d8"><code>e47b236</code></a>
Update release notes</li>
<li><a
href="https://github.com/handlebars-lang/handlebars.js/commit/f37c599de9187715eb1e9afedcb2d80c175487a0"><code>f37c599</code></a>
Sanitize the source map URL when minifying</li>
<li><a
href="https://github.com/handlebars-lang/handlebars.js/commit/812c2260cb8794ef4ab63a7f1d946431112560ca"><code>812c226</code></a>
Iterate lazily in #each and strip whitespace in linear time</li>
<li><a
href="https://github.com/handlebars-lang/handlebars.js/commit/609d1b11c833c9a3e00f56f2f34d22f425446725"><code>609d1b1</code></a>
Escape <code>&lt;!--</code> and <code>&lt;script</code> in precompiled
output</li>
<li><a
href="https://github.com/handlebars-lang/handlebars.js/commit/ceec388abe1d1aac8f6369860d5f390fa71ef4fa"><code>ceec388</code></a>
Don't trust special properties on context data</li>
<li><a
href="https://github.com/handlebars-lang/handlebars.js/commit/c28ee7adb312308e0fe26f4d435a481be5e1ff98"><code>c28ee7a</code></a>
Only compile partials that are template strings</li>
<li><a
href="https://github.com/handlebars-lang/handlebars.js/commit/7d501a5af2ac3aa9ac788a8889dbe419849e46f1"><code>7d501a5</code></a>
Only dispatch known node types in the Compiler and Visitor</li>
<li><a
href="https://github.com/handlebars-lang/handlebars.js/commit/703fdcc5fd6cc8d1cc0c33cc19de40c467c4b8d2"><code>703fdcc</code></a>
Validate AST values in the compiler instead of the parser</li>
<li><a
href="https://github.com/handlebars-lang/handlebars.js/commit/0fcf25c63038b43e78e17adbf029cf073e80554d"><code>0fcf25c</code></a>
Clarify that --root does not restrict filesystem access</li>
<li>Additional commits viewable in <a
href="https://github.com/handlebars-lang/handlebars.js/compare/v4.7.9...v4.7.10">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=handlebars&package-manager=npm_and_yarn&previous-version=4.7.9&new-version=4.7.10)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/boostorg/decimal/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- `fma` for `decimal32_t`, `decimal64_t` and the fast types added the
exact product and `z` with `add_impl`. If `z` was far below the product,
the nearest modes dropped `z`, and a tie in the product went to the even
neighbor.
- `fma` now keeps the exact product of the significands in 64, 128 or
256 bits, and it does not move the product up. `z` moves to the product.
A far `z` moves up p digits, and the product moves down to it.
- The operand that moves down records a dropped digit that is not zero.
After the sum, this record goes into the last digit. Because of this, a
carry or a borrow does not remove it.
- A sum of 2p - 1 or 2p digits rounds in one step with the finalizers of
multiplication. Other sums round in the constructor. A zero operand
leaves one operation, which rounds one time.
- For a finite `x` and `y` and an infinite `z`, `fma` returned `x * y +
z`. A large product rounded to an infinity, and with `z` of the other
sign the sum was NaN. `fma` now returns `z` in this case, for all six
types.
- `test_fma_tie` tests ties with a far `z`, a dropped digit with a carry
or a borrow, the bounds of the finalizers, and the five rounding modes.
`github_issue_1522` tests the infinite `z`.

Fixes #1522

<details>
<summary>Correctness</summary>

- 3000000 random triples for each of `decimal32_t`, `decimal64_t` and
`decimal128_t`, in the five rounding modes, against Intel libbid 2.0
Update 4. Each value that is not the same is an underflow error of
libbid. Python `decimal` gives our value in each of these cases.
- 4500000 triples with a far `z`, for all six types and the five modes,
against Python `decimal`. All results are correct.
- In the `tie_far` set of the benchmark, `develop` gives a different
value from libbid for 6720 of 16384 `decimal32_t` triples and 6193 of
16384 `decimal64_t` triples. The fix gives 0.
- `test_fma`, `test_fma_tie`, `github_issue_1522` and `test_cmath` pass
with GCC 16.2 and Clang 23.1 in C++14 and C++20 with `-Wall -Wextra
-Werror`. They also pass without the builtin 128-bit integer, with
`BOOST_DECIMAL_FAST_MATH`, and with
`BOOST_DECIMAL_NO_CONSTEVAL_DETECTION`.

</details>

<details>
<summary>Speed</summary>

The table gives ns for each call, the best of 3 runs. Each set has 16384
triples: random significands and exponents (random), a tie with a far
`z` (tie_far), a `z` near the product (near), a `z` near `-(x * y)`
(cancel) and a zero `z` (zero_z). I used `-O3 -march=native` on an AMD
Ryzen 9 3900X, on one core. libbid is built with GCC at `-O2`.

| type | set | GCC develop | GCC fix | Clang develop | Clang fix |
libbid |
|---|---|---|---|---|---|---|
| `decimal32_t` | random | 40.3 | 18.9 | 39.8 | 17.6 | 42.4 |
| `decimal32_t` | tie_far | 28.0 | 15.6 | 30.6 | 15.0 | 42.3 |
| `decimal32_t` | near | 45.6 | 22.7 | 42.8 | 22.0 | 37.9 |
| `decimal32_t` | cancel | 27.5 | 15.3 | 28.3 | 14.4 | 16.8 |
| `decimal32_t` | zero_z | 26.6 | 14.3 | 29.8 | 12.2 | 37.6 |
| `decimal64_t` | random | 112.7 | 30.0 | 92.6 | 28.8 | 37.8 |
| `decimal64_t` | tie_far | 55.4 | 26.1 | 49.4 | 23.1 | 29.4 |
| `decimal64_t` | near | 159.9 | 30.3 | 124.6 | 30.2 | 46.0 |
| `decimal64_t` | cancel | 62.3 | 17.1 | 60.0 | 16.1 | 27.0 |
| `decimal64_t` | zero_z | 40.0 | 21.5 | 38.3 | 18.4 | 31.9 |
| `decimal128_t` | random | 135.2 | 58.1 | 115.6 | 56.4 | 154.1 |
| `decimal128_t` | tie_far | 118.6 | 55.0 | 99.9 | 49.3 | 129.3 |
| `decimal128_t` | near | 131.6 | 59.2 | 111.9 | 59.1 | 177.1 |
| `decimal128_t` | cancel | 44.9 | 41.7 | 46.4 | 49.1 | 91.6 |
| `decimal128_t` | zero_z | 102.6 | 37.1 | 83.6 | 32.1 | 113.1 |
| `decimal_fast32_t` | random | 37.1 | 13.5 | 36.6 | 15.9 | |
| `decimal_fast32_t` | tie_far | 26.1 | 10.1 | 26.8 | 13.4 | |
| `decimal_fast32_t` | near | 41.5 | 16.9 | 40.3 | 19.3 | |
| `decimal_fast32_t` | cancel | 23.6 | 13.4 | 25.1 | 13.0 | |
| `decimal_fast32_t` | zero_z | 24.9 | 6.8 | 25.8 | 9.1 | |
| `decimal_fast64_t` | random | 110.6 | 25.8 | 89.5 | 25.1 | |
| `decimal_fast64_t` | tie_far | 57.5 | 22.5 | 47.3 | 20.2 | |
| `decimal_fast64_t` | near | 153.5 | 26.3 | 120.7 | 26.8 | |
| `decimal_fast64_t` | cancel | 58.1 | 18.5 | 60.0 | 15.7 | |
| `decimal_fast64_t` | zero_z | 43.0 | 16.7 | 36.7 | 13.8 | |
| `decimal_fast128_t` | random | 135.3 | 58.4 | 118.8 | 56.5 | |
| `decimal_fast128_t` | tie_far | 121.5 | 55.0 | 102.4 | 50.0 | |
| `decimal_fast128_t` | near | 128.6 | 60.4 | 115.2 | 58.6 | |
| `decimal_fast128_t` | cancel | 49.1 | 46.9 | 46.9 | 51.6 | |
| `decimal_fast128_t` | zero_z | 102.5 | 35.8 | 86.1 | 33.5 | |

With Clang, the 128-bit types are 3 to 5 ns slower in the cancel set.

</details>

<details>
<summary>Size</summary>

One function calls `fma` for each of the six types. The table gives the
size of all `.text` sections of its object file, in bytes.

| compiler | flags | develop | fix |
|---|---|---|---|
| GCC 16.2 | `-O2` | 120683 | 117880 |
| GCC 16.2 | `-Os` | 68440 | 68563 |
| Clang 23.1 | `-O2` | 87270 | 82073 |
| Clang 23.1 | `-Os` | 70472 | 68224 |

</details>
…#1517)

fmod scaled both significands to the smaller exponent and took the
integer remainder. When the exponents were too far apart for that, it
used x - trunc(x / y) * y. The quotient has more digits than the type,
so this value is not exact, and often it is not even below y. When x / y
overflowed, fmod returned NaN. For decimal128_t the limit used the
digits10 of u256, which is one too large, so an exponent gap of 44
overflowed the u256 product.

remainder was built on fmod and had the same defects. It also compared
with |y| * 0.5, which rounds for a subnormal y with an odd last digit.

remquo divided x by y, rounded the quotient n, and returned x - n * y.
This is not exact either, and x / y could overflow to an infinite
result. A halfway quotient went toward zero, not to the even integer. An
infinite y gave NaN, but C gives x.

The new detail::exact_mod takes the significands and exponents of x and
y. When the exponent of x is the larger one, it reduces mx * 10^(ex -
ey) modulo k * my in steps that keep the product in range, and stops at
a zero remainder. Otherwise x is below 2 * 10^p at its own exponent, and
one remainder is enough. The result is the exact remainder at the
exponent min(ex, ey), which IEEE 754 prefers, and the low bits of the
quotient. For remainder, k is 2. For remquo, k is one more than the mask
of the stored quotient bits. A last compare rounds the quotient half to
even.

The remainder stays in the type of the significand. Each step multiplies
it by at most 10^19 and reduces the product with one division: a 2-by-1
word division for decimal32_t and decimal64_t, and a 3-by-2 word
division for decimal128_t, so no u256 value is necessary. For
decimal32_t, x times 10^12 fits 64 bits, so the first step needs no
reduction before it. For a gap of 76 digits or more, the remainder is
multiplied by (10^19)^n modulo k * my, which repeated squaring gives in
about 2 * log2(n) steps.

The result has at most p digits and the exponent of x or y, so
pack_in_range writes it without the rounding checks of the constructor.
A zero y is found from its significand, not with a decimal comparison.
With BOOST_DECIMAL_FAST_MATH, a zero y gives zero, as in remainder and
remquo. Before, operator% divided by the zero y, which trapped.

operator% of each type, remainder and remquo now use this helper.
operator% no longer divides first, because the quotient was used only
for the old fallback and the overflow test. The remainder output of the
division helpers was read only by operator%, so it is removed, and so is
the u256 power of ten that only the old fmod used.

random_decimal64_math expected NaN from 1e+277 % 1e-311, which was the
overflow. The exact result is zero.

Fixes #1516
This PR has 2 commits.

- The cmath page no longer shows an `int* quo` parameter for `fdim`,
`fmax` and `fmin`, and calls the result of `fdim` the positive
difference.
- `fdim` gives `+0` for an infinite `x` that is not above `y`, and a NaN
for an infinite `x` with a NaN `y`.
- `fdim` gives a quiet NaN for a signaling NaN argument.
- `fdim` no longer has its own NaN and infinity branch. The compare `y
>= x` is false for a NaN, and `x - y` gives a quiet NaN and the correct
result for an infinite `x`.
- A new test covers infinite arguments and signaling NaN arguments for
the six types.

C17 7.12.12.1 gives `x - y` if `x > y`, and `+0` if `x <= y`. IEEE
754-2019 6.2 and 7.2 give a quiet NaN for an operation on a signaling
NaN. Intel libbid and glibc give the same results as the fix.

Fixes #1525

<details>
<summary>Correctness</summary>

- The reproducer of #1525 gives the correct results for all six types
with the fix:

```
decimal32_t: fdim(-inf, 1) = 0, fdim(inf, inf) = 0, fdim(inf, qNaN) = nan, fdim(snan, 1) = qNaN, fdim(1, snan) = qNaN
decimal64_t: fdim(-inf, 1) = 0, fdim(inf, inf) = 0, fdim(inf, qNaN) = nan, fdim(snan, 1) = qNaN, fdim(1, snan) = qNaN
decimal128_t: fdim(-inf, 1) = 0, fdim(inf, inf) = 0, fdim(inf, qNaN) = nan, fdim(snan, 1) = qNaN, fdim(1, snan) = qNaN
decimal_fast32_t: fdim(-inf, 1) = 0, fdim(inf, inf) = 0, fdim(inf, qNaN) = nan, fdim(snan, 1) = qNaN, fdim(1, snan) = qNaN
decimal_fast64_t: fdim(-inf, 1) = 0, fdim(inf, inf) = 0, fdim(inf, qNaN) = nan, fdim(snan, 1) = qNaN, fdim(1, snan) = qNaN
decimal_fast128_t: fdim(-inf, 1) = 0, fdim(inf, inf) = 0, fdim(inf, qNaN) = nan, fdim(snan, 1) = qNaN, fdim(1, snan) = qNaN
```

- Compared with Intel libbid `__bid32_fdim`, `__bid64_fdim` and
`__bid128_fdim` on 160000 pairs per type in the 5 rounding modes. The
pairs include zeros, infinities, quiet and signaling NaNs, subnormals,
the largest values, equal values and neighbors. `develop` gives 4060
(`decimal32_t`), 2025 (`decimal64_t`) and 2025 (`decimal128_t`)
incorrect values. The fix gives 0. Two differences remain, and both are
permitted:
- A result can have the same value as libbid with a different exponent.
This comes from `operator-`, not from `fdim`.
- If `x` is a quiet NaN and `y` is a signaling NaN of the other sign,
the fix gives the NaN of `y`, and libbid gives the NaN of `x`. IEEE
754-2019 6.2.3 permits either.
- `github_issue_1525` fails on `develop` with 162 errors and passes with
the fix.
- `github_issue_1525` and `test_cmath` pass with GCC 16.2 and Clang
23.1, in C++14 and C++20, with `-Wall -Wextra -Werror`. The new test
also passes with `BOOST_DECIMAL_FAST_MATH`.
- With `BOOST_DECIMAL_FAST_MATH`, the code does not change: the removed
branch was already disabled in that mode.

</details>

<details>
<summary>Speed</summary>

Nanoseconds for each call, best of 3 runs of 1024 pairs, `-O2`, AMD
Ryzen 9 3900X, one core. `mix` has random pairs, `gt` has `x > y` and
`le` has `x <= y`:

| type | inputs | GCC develop | GCC fix | Clang develop | Clang fix |
|---|---|---|---|---|---|
| `decimal32_t` | mix | 8.7 | 9.3 | 10.8 | 10.8 |
| `decimal32_t` | gt | 13.7 | 14.6 | 17.8 | 17.7 |
| `decimal32_t` | le | 2.9 | 3.1 | 4.1 | 4.2 |
| `decimal64_t` | mix | 11.4 | 11.3 | 13.6 | 13.5 |
| `decimal64_t` | gt | 18.1 | 17.9 | 23.1 | 22.7 |
| `decimal64_t` | le | 4.5 | 4.4 | 4.9 | 4.9 |
| `decimal128_t` | mix | 23.6 | 24.7 | 27.9 | 27.9 |
| `decimal128_t` | gt | 40.8 | 42.0 | 46.8 | 46.5 |
| `decimal128_t` | le | 8.9 | 9.0 | 12.1 | 12.1 |
| `decimal_fast32_t` | mix | 8.7 | 9.0 | 8.1 | 7.6 |
| `decimal_fast32_t` | gt | 13.9 | 14.8 | 13.8 | 12.3 |
| `decimal_fast32_t` | le | 3.6 | 3.5 | 4.2 | 3.4 |
| `decimal_fast64_t` | mix | 10.0 | 10.1 | 10.6 | 9.0 |
| `decimal_fast64_t` | gt | 17.8 | 18.0 | 17.4 | 16.7 |
| `decimal_fast64_t` | le | 3.6 | 3.2 | 5.0 | 3.6 |
| `decimal_fast128_t` | mix | 15.6 | 16.1 | 15.6 | 15.3 |
| `decimal_fast128_t` | gt | 26.9 | 26.6 | 31.4 | 30.5 |
| `decimal_fast128_t` | le | 7.1 | 7.3 | 3.3 | 3.1 |

GCC is up to 7% slower for `decimal32_t` and `decimal_fast32_t`, and
Clang is up to 28% faster for the fast types. Intel libbid
`__bid64_fdim` takes 18.8 ns for the `mix` pairs on the same machine.

</details>

<details>
<summary>Size</summary>

`.text` bytes of one object file with six functions that call `fdim`,
one for each type:

| compiler | flags | develop | fix |
|---|---|---|---|
| GCC 16.2 | `-O2` | 8297 | 9329 |
| GCC 16.2 | `-Os` | 5349 | 5716 |
| Clang 23.1 | `-O2` | 863 | 896 |
| Clang 23.1 | `-Os` | 261 | 307 |

A NaN now goes to `operator-`, so the code of `operator-` for values
that are not finite stays in `fdim`.

</details>
@cppalliance-bot

Copy link
Copy Markdown

An automated preview of the documentation is available at https://1527.decimal.prtest3.cppalliance.org/libs/decimal/doc/html/index.html

If more commits are pushed to the pull request, the docs will rebuild at the same URL.

2026-10-23 15:37:40 UTC

@codecov

codecov Bot commented Oct 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.54863% with 77 lines in your changes missing coverage. Please review.
✅ Project coverage is 98.5%. Comparing base (144e274) to head (79d0cb7).
⚠️ Report is 50 commits behind head on master.

Files with missing lines Patch % Lines
include/boost/decimal/detail/add_impl.hpp 74.1% 34 Missing ⚠️
...clude/boost/decimal/detail/dragonbox/dragonbox.hpp 92.2% 11 Missing ⚠️
include/boost/decimal/detail/cmath/next.hpp 53.4% 7 Missing ⚠️
include/boost/decimal/detail/div_impl.hpp 88.7% 6 Missing ⚠️
...nclude/boost/decimal/detail/cmath/comparetotal.hpp 0.0% 5 Missing ⚠️
include/boost/decimal/charconv.hpp 93.2% 3 Missing ⚠️
include/boost/decimal/decimal32_t.hpp 95.0% 3 Missing ⚠️
include/boost/decimal/decimal128_t.hpp 96.0% 2 Missing ⚠️
include/boost/decimal/decimal64_t.hpp 95.7% 2 Missing ⚠️
include/boost/decimal/detail/cmath/atan.hpp 97.5% 1 Missing ⚠️
... and 3 more
Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff            @@
##           master   #1527     +/-   ##
========================================
+ Coverage    98.3%   98.5%   +0.2%     
========================================
  Files         291     326     +35     
  Lines       23674   27689   +4015     
  Branches     2070    2300    +230     
========================================
+ Hits        23250   27248   +3998     
- Misses        424     441     +17     
Files with missing lines Coverage Δ
include/boost/decimal/bid_conversion.hpp 100.0% <100.0%> (ø)
include/boost/decimal/cfenv.hpp 100.0% <ø> (ø)
include/boost/decimal/cmath.hpp 100.0% <ø> (ø)
include/boost/decimal/decimal_fast128_t.hpp 98.3% <100.0%> (+2.7%) ⬆️
include/boost/decimal/decimal_fast32_t.hpp 98.4% <100.0%> (+1.0%) ⬆️
include/boost/decimal/decimal_fast64_t.hpp 98.9% <100.0%> (+1.4%) ⬆️
include/boost/decimal/detail/attributes.hpp 100.0% <100.0%> (ø)
include/boost/decimal/detail/cmath/acos.hpp 100.0% <100.0%> (ø)
include/boost/decimal/detail/cmath/acosh.hpp 100.0% <100.0%> (ø)
include/boost/decimal/detail/cmath/asin.hpp 100.0% <100.0%> (ø)
... and 145 more

... and 10 files with indirect coverage changes


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 144e274...79d0cb7. Read the comment docs.

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

@mborland
mborland merged commit a4958c2 into master Oct 23, 2026
148 checks passed
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.

6 participants