NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

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



>Number:         56937
>Category:       lib
>Synopsis:       printf(3) long double %a formatting is broken
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    lib-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Fri Jul 22 13:15:00 +0000 2022
>Originator:     kre%munnari.OZ.AU@localhost
>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@localhost:/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).



Home | Main Index | Thread Index | Old Index