mailing list of musl libc
 help / color / mirror / code / Atom feed
From: Rich Felker <dalias@libc.org>
To: Peter Ammon <corydoras@ridiculousfish.com>
Cc: musl@lists.openwall.com
Subject: Re: [musl] [PATCH] Fix printf hex float formatting with precision
Date: Thu, 11 Apr 2024 22:34:41 -0400	[thread overview]
Message-ID: <20240412023440.GD4163@brightrain.aerifal.cx> (raw)
In-Reply-To: <F821CF10-24E5-4700-B514-62604EC9B891@ridiculousfish.com>

On Thu, Apr 11, 2024 at 06:17:25PM -0700, Peter Ammon wrote:
> printf hex formatting with precision may emit excess digits on
> targets where long double is an alias of double. For example, on
> ARMv7, the expression `printf("%.12a", M_PI)` outputs 13 digits past
> the decimal, instead of 12.
> 
> I believe the cause is a bogus rounding calculation in fmt_fp:
> 
> if (p<0 || p>=LDBL_MANT_DIG/4-1) re=0;
> else re=LDBL_MANT_DIG/4-1-p;
> if (re) {
>     round *= 1<<(LDBL_MANT_DIG%4);
>     while (re--) round*=16;
>     ...
> 
> I wasn't able to justify the calculation of `re`; I think it suffers
> from trying to round in terms of number of hex digits, which is
> tricky because the number of fractional mantissa bits may not be a
> multiple of 4.
> 
> I propose to fix it by working in bits instead of hex digits, as follows:
> 
> if (p>=0 && p<(LDBL_MANT_DIG-1+3)/4) {
>     int re = LDBL_MANT_DIG-1-(p*4);
>     long double round = 1ULL<<re;
> ....
> 
> This is justified as follows:
> 
> - The value to format has been scaled to the range [1, 2), so there
> is exactly one bit before the radix. Thus, the fractional portion of
> the mantissa may be printed at full precision with LDBL_MANT_DIG-1,
> divided by 4, rounding up; thus `(LDBL_MANT_DIG-1+3)/4`
> - The caller has requested a precision of p hex digits, so p*4 bits.
> - `re` is then the number of bits to round off, as LDBL_MANT_DIG-1-(p*4)
> - Thus we compute `round` as 2**re. Adding `round` will lose `re`
> bits of precision, rounding per the fp mode; subtracting this again
> will round the original value.
> 
> I also removed an initial factor of 8 from the `round` as it seemed
> incorrect to me; for example we may want to round only the last bit,
> in which case the min round of 8 would be too big.
> 
> I am by no means a numerics expert, so I very much welcome another
> pair of eyes. I will be happy to contribute a test.

Aside from one minor fix, this code dates back to the initial check-in
of musl, and based on the behavior and the initial value 8 for round,
I'm pretty sure it was written assuming we want the digit before the
radix point to be in the range [8,F] rather than 1, to pack the max
amount of precision into the output. For some reason, I decided this
was not the best behavior, but must have failed to compensate for that
in the rounding logic. There may be pre-0.5.0 trees somewhere I could
excavate to figure out exactly what happened, but that's my best
guess.

FWIW the bug seems to be fixed by making it do y*=8; e2-=3;

Offhand without delving deep into it, your fix sounds correct. It
could probably be factored as two parts, first just fixing the bug
(I think the only actual bug is the 8), and a second switching to your
better algorithm for computing the rounding term.

Rich

  reply	other threads:[~2024-04-12  2:34 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-04-12  1:17 Peter Ammon
2024-04-12  2:34 ` Rich Felker [this message]
2024-04-12 19:57 ` Rich Felker
2024-04-12 23:33   ` Rich Felker
2024-04-13 16:00     ` Peter Ammon

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240412023440.GD4163@brightrain.aerifal.cx \
    --to=dalias@libc.org \
    --cc=corydoras@ridiculousfish.com \
    --cc=musl@lists.openwall.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/musl/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).