Andres Salas
Andres Salas

Reputation: 175

How to manually convert fprintf hexadecimal representation of floating point to decimal

The hexadecimal representation of a floating point number from fprintf and snprintf seems to take the form (using A as a format specifier): 0XH.HHHHP[+-]d , where the number of hexadecimal digits to the right of the decimal point is variable.

I've found at least one manual explaining this format:

https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/printf.3.html

  aA     The double argument is rounded and converted to hexadecimal notation in the 
         style [-]0xh.hhhp[+-]d, where the number of digits
         after the hexadecimal-point character is equal to the precision
         specification.  If the precision is missing, it is taken as
         enough to represent the floating-point number exactly, and no
         rounding occurs.  If the precision is zero, no hexadecimal-point
         character appears.  The p is a literal character `p', and the
         exponent consists of a positive or negative sign followed by a
         decimal number representing an exponent of 2.  The A conversion
         uses the prefix ``0X'' (rather than ``0x''), the letters
         ``ABCDEF'' (rather than ``abcdef'') to represent the hex digits,
         and the letter `P' (rather than `p') to separate the mantissa and
         exponent.

         Note that there may be multiple valid ways to represent floating-point 
         point numbers in this hexadecimal format.  For example,
         0x3.24p+0, 0x6.48p-1 and 0xc.9p-2 are all equivalent.  The format
         chosen depends on the internal representation of the number, but
         the implementation guarantees that the length of the mantissa
         will be minimized.  Zeroes are always represented with a mantissa
         of 0 (preceded by a `-' if appropriate) and an exponent of +0.

I don't understand this explanation. I believe I understand the general approach to converting floating point numbers to binary: include a sign bit, an exponent bit that refers to the location of the first significant digit in binary, and use the rest of the bits to represent the number to the right of the decimal in binary.

What I don't understand is how this is done with this format, for example:

What does the character to the left of the decimal point represent?

What does it mean when the decimal point is missing?

What do the characters to the right of the decimal point represent?

Why is the letter P always there?

Can anyone show me some examples of converting to/from this representation and a floating point number?

Upvotes: 0

Views: 101

Answers (2)

Andres Salas
Andres Salas

Reputation: 175

Eric's answer covers essentially all of my questions, except (unless I missed it of course) for some an example of how to convert from a [+-]0XH.HHHHP[+-]d -formatted hex number to a floating point number. The procedure to do this is as follows:

Take an example A-formatted number, 0X1.68P+6

  1. Separate the number into four components: () (0X) (1.68) (P+6)
  2. Infer a positive sign for the first, empty component (which could be negative), apply this to the final number that follows from the following steps:
  3. Ignore the second part (my understanding is that it is a "this is a hex representation of a float" identifier)
  4. Take the third H.HHH (1.68 in this case) part, and convert all hex characters to binary, using four bits per hex character: (1.68 -> 0001.01101000)
  5. Shift the radix point to the right by the decimal number following P in the fourth and final component of the hex number, in this case (P+6) works out to six shifts to the right: (0001.01101000 -> 0001011010.00). Note that a negative number indicates that the shift would be to the left.
  6. Re-convert the shifted binary number in step 5 to decimal: (0001011010.00 -> 90.0)

Sometimes the hex representation does not have a decimal point. As Eric's answer points out, this just means the hex representation starts its conversion as if its fractional component is zero. It may end up with a fractional component after the decimal point shifting step (5 above), but the conversion in step 4 above at least would have a fractional component of zero.

Upvotes: 0

Eric Postpischil
Eric Postpischil

Reputation: 223409

I've found at least one manual explaining this format…

You should look to the C standard for information like this.

What does the character to the left of the decimal point represent?

In decimal, 34.5•100, 3.45•101, and .345•102, are the same number. In hexadecimal, 9A.B16•160, 9.AB16•161, .9AB16•162 are the same number. In the description of the style as [-]0xh.hhhhp±d, the h.hhhh part is just telling you that the formatting routine will format it with one hexadecimal digit to the left of the point (and adjust the exponent to match).

What does it mean when the decimal point is missing?

This is merely an aesthetic choice. When there are no digits in fraction positions, to the right of a decimal point, there is no need to have a decimal point to indicate where the fraction positions start. However, the %a format is mostly used to display the entire floating-point value, so we usually let all the digits appear and do not set the precision to zero.

What do the characters to the right of the decimal point represent?

They are digits in fraction positions, the same as in decimal notation. For example, the hexadecimal numeral 123.45616 represents 1•162 + 2•161 + 3•160 + 4•16−1 + 5•16−2 + 6•16−3.

Why is the letter P always there?

It stands for “power” and marks the start of the exponent. For output, we could suppress it if the exponent is zero. However, this output format is chosen to match the form of hexadecimal literals in source code, and, for those, we need “p” at least in integers to distinguish the hexadecimal integer literal 0x3 from the hexadecimal floating-point literal 0x3p0.

Upvotes: 3

Related Questions