NetBSD Problem Report #56937

From kre@munnari.OZ.AU  Fri Jul 22 13:13:48 2022
Return-Path: <kre@munnari.OZ.AU>
Received: from mail.netbsd.org (mail.netbsd.org [199.233.217.200])
	(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits))
	(Client CN "mail.NetBSD.org", Issuer "mail.NetBSD.org CA" (not verified))
	by mollari.NetBSD.org (Postfix) with ESMTPS id CA5911A9239
	for <gnats-bugs@gnats.NetBSD.org>; Fri, 22 Jul 2022 13:13:48 +0000 (UTC)
Message-Id: <202207221045.26MAjDmB002514@jacaranda.noi.kre.to>
Date: Fri, 22 Jul 2022 17:45:13 +0700 (+07)
From: kre@munnari.OZ.AU
Reply-To:
To: gnats-bugs@NetBSD.org
Subject: printf(3) long double %a formatting is broken
X-Send-Pr-Version: 3.95

>Number:         56937
>Category:       lib
>Synopsis:       printf(3) long double %a formatting is broken
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    lib-bug-people
>State:          closed
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Fri Jul 22 13:15:00 +0000 2022
>Closed-Date:    Fri Aug 23 20:23:06 +0000 2024
>Last-Modified:  Fri Aug 23 20:23:06 +0000 2024
>Originator:     kre@munnari.OZ.AU
>Release:        NetBSD 9.99.99
>Organization:
>Environment:
System: NetBSD jacaranda.noi.kre.to 9.99.99 NetBSD 9.99.99 (JACARANDA:1.1-20220718) #0: Tue Jul 19 03:35:07 +07 2022 kre@jacaranda.noi.kre.to:/usr/obj/testing/kernels/amd64/JACARANDA amd64
Architecture: x86_64
Machine: amd64
>Description:
	When printf(3) is asked to print a long double (flost) in %a format
	(eg: printf("%La", (long double)1.0); ) It includes a bit set in the
	result that should not be there.

>How-To-Repeat:
	You can write code easily to test this (I did), but a simple way
	is to install (of not already done) any version (well, recentish)
	of bash ans use its builtin printf(1) - bash uses long double for
	floats in its arithmetic

	jacaranda$ printf %a\\n 1
	0x8.8p-3
	jacaranda$ /usr/bin/printf %a\\n 1
	0x1p+0

	The first of those is from bash, and is clearly nonsense, no
	matter how you express it, the floating point representation
	of 1 has (at most) 1 bit set (in its mantissa), not two.

	The second is NetBSD's printf, which uses regular double for
	its floats, and is correct.

	Note that the difference between 0x8p-3 and 0x1p+0 is
	irrelevant - either is acceptable (those values represent the
	same thing).

	Similarly, from bash:

	jacaranda$ printf '%a ' 1.5 1.25 1.125 1.0625 ; printf \\n
	0x8.cp-3 0x8.ap-3 0x8.9p-3 0x8.88p-3 

	Those values should all have 2 bits set, not the 3 shown.
	And they do when it is just a double being printed:

	jacaranda$ /usr/bin/printf '%a ' 1.5 1.25 1.125 1.0625 ; printf \\n
	0x1.8p+0 0x1.4p+0 0x1.2p+0 0x1.1p+0 

	You can repeat all of this using a simple C program, to demonstrate
	that the problem is not internal to bash somewhere (which is
	what I first assumed).

	The internal format difference between double and long double
	should not be altering the representation (in this form) of any
	of these values, the extra precision or range is not relevant.

	All the other conversions (%e %f %g) seem fine, as does parsing
	floats in any of the formats they're allowed - including converting
	0xX.XXXp[+-]N into float/double/long double.

>Fix:
	I spent some time hunting in src/lib/libc/gdtoa/hdtoa.c
	which is where I think the problem occurs, but ...
	(It doesn't help that I don't know either IEEE or x87
	floating point representations almost at all).

>Release-Note:

>Audit-Trail:
From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/56937 CVS commit: src/tests/lib/libc/stdio
Date: Wed, 8 May 2024 18:19:57 +0000

 Module Name:	src
 Committed By:	riastradh
 Date:		Wed May  8 18:19:57 UTC 2024

 Modified Files:
 	src/tests/lib/libc/stdio: t_printf.c

 Log Message:
 tests/lib/libc/stdio/t_printf: Add a couple simple %La tests.

 PR lib/56937: printf(3) long double %a formatting is broken


 To generate a diff of this commit:
 cvs rdiff -u -r1.10 -r1.11 src/tests/lib/libc/stdio/t_printf.c

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/56937 CVS commit: src/tests/lib/libc/stdio
Date: Wed, 8 May 2024 20:23:15 +0000

 Module Name:	src
 Committed By:	riastradh
 Date:		Wed May  8 20:23:15 UTC 2024

 Modified Files:
 	src/tests/lib/libc/stdio: t_printf.c

 Log Message:
 tests/lib/libc/stdio/t_printf: Add another %La test.

 This one was adapted from the screw case shown in

 https://mail-index.netbsd.org/tech-userlevel/2020/04/11/msg012329.html

 which wasn't broken in our libc, but which nevertheless prompted us
 to commit a wrong and apparently untested patch that has rendered
 printf %La broken for the last four years, which is a little
 embarrassing.  (The part of that patch that led to a buffer overrun
 has been worked around, so now the output is just incorrect.)

 PR lib/56937: printf(3) long double %a formatting is broken


 To generate a diff of this commit:
 cvs rdiff -u -r1.13 -r1.14 src/tests/lib/libc/stdio/t_printf.c

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/56937 CVS commit: src
Date: Thu, 9 May 2024 12:24:24 +0000

 Module Name:	src
 Committed By:	riastradh
 Date:		Thu May  9 12:24:24 UTC 2024

 Modified Files:
 	src/lib/libc/gdtoa: hdtoa.c
 	src/tests/lib/libc/stdio: t_printf.c

 Log Message:
 Revert various broken changes to printf %La (hldtoa).

 This reverts:

 hdtoa.c 1.12 (PR/56247: Greg A. Woods: printf("%La", LDBL_MIN) dumps core)
 hdtoa.c 1.11 (fix tyop)
 hdtoa.c 1.10 (Via enh at google dot com in tech-userlevel. Fix handling of
     EXT_FRAC{H,L}BITS (although we don't need to since we don't have them).)

 The underlying motivation for this change was that when ld128 is
 decomposed into 4x32 words, this hldtoa logic is broken.

 But we don't decompose ld128 into 4x32 words; we decompose it into
 6x64 words.

 And the change, which was supposed to be a noop in our case of 2x64
 words (or similar for x87 80-bit floating-point), broke it to the
 point of causing buffer overruns (PR 56247) which when worked around
 led to just incorrect output output (PR 56937).

 If we want to make the #ifdefs for 4x32 words work, that's fine, but
 we absolutely must have automatic test cases to detect this kind of
 regression because %La formatting is extremely important for
 diagnosing details of floating-point data since it doesn't involve
 rounding in binary formats.  For now I've added some trivial tests;
 there is a more extensive test suite inside gdtoa that we need to
 wire up before anyone tries any other shenanigans in this code.

 PR lib/56937: printf(3) long double %a formatting is broken


 To generate a diff of this commit:
 cvs rdiff -u -r1.12 -r1.13 src/lib/libc/gdtoa/hdtoa.c
 cvs rdiff -u -r1.14 -r1.15 src/tests/lib/libc/stdio/t_printf.c

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

State-Changed-From-To: open->needs-pullups
State-Changed-By: riastradh@NetBSD.org
State-Changed-When: Thu, 09 May 2024 12:26:58 +0000
State-Changed-Why:
we shipped broken %La in netbsd-10 (but not in netbsd-9)


State-Changed-From-To: needs-pullups->pending-pullups
State-Changed-By: riastradh@NetBSD.org
State-Changed-When: Sat, 17 Aug 2024 15:16:12 +0000
State-Changed-Why:
pullup-9 #1866 https://releng.netbsd.org/cgi-bin/req-9.cgi?show=1866
pullup-10 #787 https://releng.netbsd.org/cgi-bin/req-10.cgi?show=787


From: "Martin Husemann" <martin@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/56937 CVS commit: [netbsd-9] src/tests/lib/libc/stdio
Date: Thu, 22 Aug 2024 19:53:56 +0000

 Module Name:	src
 Committed By:	martin
 Date:		Thu Aug 22 19:53:56 UTC 2024

 Modified Files:
 	src/tests/lib/libc/stdio [netbsd-9]: t_printf.c

 Log Message:
 Pull up following revision(s) (requested by riastradh in ticket #1866):

 	tests/lib/libc/stdio/t_printf.c: revision 1.17
 	tests/lib/libc/stdio/t_printf.c: revision 1.18
 	tests/lib/libc/stdio/t_printf.c: revision 1.11
 	tests/lib/libc/stdio/t_printf.c: revision 1.12
 	tests/lib/libc/stdio/t_printf.c: revision 1.13
 	tests/lib/libc/stdio/t_printf.c: revision 1.14
 	tests/lib/libc/stdio/t_printf.c: revision 1.15
 	tests/lib/libc/stdio/t_printf.c: revision 1.16
 	(all via patch)

 tests/lib/libc/stdio/t_printf: Add a couple simple %La tests.

 PR lib/56937: printf(3) long double %a formatting is broken
 tests/lib/libc/stdio/t_printf: Fix %La test.
 0xa.99ap+0 is closer to (long double)10.6 in x86 ld80 and in
 binary128 (and possibly more formats, haven't verified).
 tests/lib/libc/stdio/t_printf: Fix %a test the same way.
 tests/lib/libc/stdio/t_printf: Add another %La test.

 This one was adapted from the screw case shown in
 https://mail-index.netbsd.org/tech-userlevel/2020/04/11/msg012329.html
 which wasn't broken in our libc, but which nevertheless prompted us
 to commit a wrong and apparently untested patch that has rendered
 printf %La broken for the last four years, which is a little
 embarrassing.  (The part of that patch that led to a buffer overrun
 has been worked around, so now the output is just incorrect.)

 PR lib/56937: printf(3) long double %a formatting is broken
 tests/lib/libc/stdio/t_printf: Fix another rounding error.
 Noted by kre.

 This doesn't break a passing test or fix a failed test, at least on
 x86 -- our printf produces `0x1.533p+3' for the double case and
 `0xa.99ap+0' for the long double case.  But of the hexadecimal number
 literals that that start with 0x5 having three hexadigits to the
 right of the fractional point, 0x5.4cdp+1 closest to the IEEE 754
 binary64, VAX D, x86 extended precision, and IEEE 754 binary128
 floating-point numbers closest to 10.6.

 The reason is that the number 10.6 (or the nearest floating-point
 number in any format with enough precision) is:
 101.0100 1100 1100|1100... * 2^1 = 0x5.4cc|c...p+1

 If we round at the vertical bar to the _nearest_ output with three
 hexadigits of precision, the result is:
 101.0100 1100 1101 * 2^1 = 0x5.4cdp+1

 tests/lib/libc/stdio/t_printf: Fix typo in ld128 case.
 printf %La does not write the `L' suffix.

 tests/lib/libc/stdio/t_printf: Fix sign error in ld128 case.
 Also link back to where the test case came from.


 To generate a diff of this commit:
 cvs rdiff -u -r1.8.34.1 -r1.8.34.2 src/tests/lib/libc/stdio/t_printf.c

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

From: "Martin Husemann" <martin@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/56937 CVS commit: [netbsd-10] src
Date: Thu, 22 Aug 2024 19:59:47 +0000

 Module Name:	src
 Committed By:	martin
 Date:		Thu Aug 22 19:59:46 UTC 2024

 Modified Files:
 	src/lib/libc/gdtoa [netbsd-10]: hdtoa.c
 	src/tests/lib/libc/stdio [netbsd-10]: t_printf.c

 Log Message:
 Pull up following revision(s) (requested by riastradh in ticket #787):

 	tests/lib/libc/stdio/t_printf.c: revision 1.17
 	tests/lib/libc/stdio/t_printf.c: revision 1.18
 	lib/libc/gdtoa/hdtoa.c: revision 1.13
 	tests/lib/libc/stdio/t_printf.c: revision 1.11
 	tests/lib/libc/stdio/t_printf.c: revision 1.12
 	tests/lib/libc/stdio/t_printf.c: revision 1.13
 	tests/lib/libc/stdio/t_printf.c: revision 1.14
 	tests/lib/libc/stdio/t_printf.c: revision 1.15
 	tests/lib/libc/stdio/t_printf.c: revision 1.16

 tests/lib/libc/stdio/t_printf: Add a couple simple %La tests.

 PR lib/56937: printf(3) long double %a formatting is broken
 tests/lib/libc/stdio/t_printf: Fix %La test.
 0xa.99ap+0 is closer to (long double)10.6 in x86 ld80 and in
 binary128 (and possibly more formats, haven't verified).
 tests/lib/libc/stdio/t_printf: Fix %a test the same way.
 tests/lib/libc/stdio/t_printf: Add another %La test.

 This one was adapted from the screw case shown in
 https://mail-index.netbsd.org/tech-userlevel/2020/04/11/msg012329.html
 which wasn't broken in our libc, but which nevertheless prompted us
 to commit a wrong and apparently untested patch that has rendered
 printf %La broken for the last four years, which is a little
 embarrassing.  (The part of that patch that led to a buffer overrun
 has been worked around, so now the output is just incorrect.)

 PR lib/56937: printf(3) long double %a formatting is broken

 Revert various broken changes to printf %La (hldtoa).
 This reverts:
 hdtoa.c 1.12 (PR/56247: Greg A. Woods: printf("%La", LDBL_MIN) dumps core)
 hdtoa.c 1.11 (fix tyop)
 hdtoa.c 1.10 (Via enh at google dot com in tech-userlevel. Fix handling of
     EXT_FRAC{H,L}BITS (although we don't need to since we don't have them).)

 The underlying motivation for this change was that when ld128 is
 decomposed into 4x32 words, this hldtoa logic is broken.

 But we don't decompose ld128 into 4x32 words; we decompose it into
 6x64 words.

 And the change, which was supposed to be a noop in our case of 2x64
 words (or similar for x87 80-bit floating-point), broke it to the
 point of causing buffer overruns (PR 56247) which when worked around
 led to just incorrect output output (PR 56937).
 If we want to make the #ifdefs for 4x32 words work, that's fine, but
 we absolutely must have automatic test cases to detect this kind of
 regression because %La formatting is extremely important for
 diagnosing details of floating-point data since it doesn't involve
 rounding in binary formats.  For now I've added some trivial tests;
 there is a more extensive test suite inside gdtoa that we need to
 wire up before anyone tries any other shenanigans in this code.

 PR lib/56937: printf(3) long double %a formatting is broken
 tests/lib/libc/stdio/t_printf: Fix another rounding error.
 Noted by kre.

 This doesn't break a passing test or fix a failed test, at least on
 x86 -- our printf produces `0x1.533p+3' for the double case and
 `0xa.99ap+0' for the long double case.  But of the hexadecimal number
 literals that that start with 0x5 having three hexadigits to the
 right of the fractional point, 0x5.4cdp+1 closest to the IEEE 754
 binary64, VAX D, x86 extended precision, and IEEE 754 binary128
 floating-point numbers closest to 10.6.

 The reason is that the number 10.6 (or the nearest floating-point
 number in any format with enough precision) is:
 101.0100 1100 1100|1100... * 2^1 = 0x5.4cc|c...p+1
 If we round at the vertical bar to the _nearest_ output with three
 hexadigits of precision, the result is:
 101.0100 1100 1101 * 2^1 = 0x5.4cdp+1
 tests/lib/libc/stdio/t_printf: Fix typo in ld128 case.
 printf %La does not write the `L' suffix.
 tests/lib/libc/stdio/t_printf: Fix sign error in ld128 case.

 Also link back to where the test case came from.


 To generate a diff of this commit:
 cvs rdiff -u -r1.12 -r1.12.2.1 src/lib/libc/gdtoa/hdtoa.c
 cvs rdiff -u -r1.8.42.1 -r1.8.42.2 src/tests/lib/libc/stdio/t_printf.c

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

State-Changed-From-To: pending-pullups->closed
State-Changed-By: riastradh@NetBSD.org
State-Changed-When: Fri, 23 Aug 2024 20:23:06 +0000
State-Changed-Why:
fixed in HEAD, pulled up to 10, tests pulled up to 9
(bug was added between 9 and 10)


>Unformatted:

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.47 2022/09/11 19:34:41 kim Exp $
$NetBSD: gnats_config.sh,v 1.9 2014/08/02 14:16:04 spz Exp $
Copyright © 1994-2024 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.