3

I want to understand how numbers (doubles) are represented in bash and what happens when I printf numbers in hexadecimal format in bash.

According to the IEEE 754 standard double should be represented by 64 bits: 52 bits (13 hex numbers) for a significand, 11 bits for the exponent and 1 bit for sign.

To check it, I wrote a simple C program that transforms hex into dec (using printf).

include <stdio.h>
int main(int argc, char **argv)
{
printf("hex read = %40.24a\n", 0x1.000010C6F7A0B5E1Fp+0);
}

compiling with gcc 4.2.1, I get

hex read =          0x1.000010c6f7a0b00000000000p+0

From this result I conclude, that as I expect significand is defined by 13 hex digits 000010c6f7a0b.

Now I turn to bash and use the following script:

#!/bin/bash
echo "hex read = 0x"$1
printf "hex       =%80.70a\n" "0x"$1
printf "hex -> dec=%80.70f\n" `echo "0x"$1`

GNU bash 3.2.48

$ bash hex2dec 1.000010C6F7A0B5E1F
hex read = 0x1.000010C6F7A0B5E1F
hex       =   0x1.000010c6f7a0b000000000000000000000000000000000000000000000000000000000p+0
hex -> dec=        1.0000009999999999177333620536956004798412322998046875000000000000000000

So everything worked as I expected: 13 hex digits define the significand of the number.

GNU bash 4.1.5

$ bash hex2dec 1.000010C6F7A0B5E1F
hex read = 0x1.000010C6F7A0B5E1F
hex       =   0x8.00008637bd05af10000000000000000000000000000000000000000000000000000000p-3
hex -> dec=        1.0000009999999999993737856418540843606024282053112983703613281250000000

This is not what I expected!

Question 1 Why in GNU bash 4.1.5 double's significand is represented by 16 hex digits (instead of 13 according to IEEE 754)?

Question 2 Why printf "%a" represents the hex number in a different format in different bash versions (bash 3.2.48 0x1.hh...hp+d and bash 4.1.5 0xh.hh...hp+d?). Shouldn't printf follow the same standard in both bash versions and be reglamented by http://pubs.opengroup.org/onlinepubs/009695399/functions/fprintf.html?

Zombo
  • 1
  • 62
  • 391
  • 407
user1541776
  • 497
  • 4
  • 14
  • 2
    If there was a change it was most likely a bugfix (or regression). Those are both old versions. Check the changelogs. Bash has been around longer than C99 and therefore I would assume also `%a` and hex floating point literals. Note POSIX doesn't require `printf(1)` to support any of the floating point format specifications, so all of these things go beyond the minimum requirement for `printf(1)`. `%a` is probably one of the least portable of those. – ormaaj Feb 01 '13 at 08:36
  • @ormaaj, thank you for the comment. I didn't find info on how to check changelogs on http://www.gnu.org/software/bash/manual/bash.html Could you, please, provide a link? I also didn't find the official website that contains reglamentation for printf(1). I thought it should be available at opengroup.org, but I found only printf (not printf(1)) specification. Printf specification includes %a though. So, could you, please, also provide me with a link to the official page that describes printf(1) format? Finally, how to understand which standard follows this particular version of bash? Thank you! – user1541776 Feb 01 '13 at 23:27
  • 1
    [printf utility](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html#tag_20_94) [changelog](http://git.savannah.gnu.org/cgit/bash.git/tree/CHANGES?h=devel) FWIW, I got results that agree with the older bash behaviour in ksh93. – ormaaj Feb 04 '13 at 21:36

1 Answers1

1

Answer 1 The current bash's printf on x86 uses long double for input/output conversion in accordance with IEEE 754 (cf. Extended and extendable precision formats, x86 Extended Precision Format and bash's printf's definition of floatmax_t), similar to the program

#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
    printf("%La\n", strtold("0x1.000010C6F7A0B5E1F", NULL));
}
  • its output is 0x8.00008637bd05af1p-3.

Answer 2 Bash ultimately uses the C library's printf; the output of the C program above does follow the standard you refer to:

there is one hexadecimal digit (which shall be non-zero if the argument is a normalized floating-point number and is otherwise unspecified) before the decimal-point character

Armali
  • 18,255
  • 14
  • 57
  • 171