For the purposes of this question, I do not have the ability to use printf
facilities (I can't tell you why, unfortunately, but let's just assume for now that I know what I'm doing).
For an IEEE754 single precision number, you have the following bits:
SEEE EEEE EFFF FFFF FFFF FFFF FFFF FFFF
where S
is the sign, E
is the exponent and F
is the fraction.
Printing the sign is relatively easy for all cases, as is catching all the special cases like NaN
(E == 0xff, F != 0
), Inf
(E == 0xff, F == 0
) and 0
(E == 0, F == 0
, considered special just because the exponent bias isn't used in that case).
I have two questions.
The first is how best to turn denormalised numbers (where E == 0, F != 0
) into normalised numbers (where 1 <= E <= 0xfe
)? I suspect this will be necessary to simplify the answer to the next question (but I could be wrong so feel free to educate me).
The second question is how to print out the normalised numbers. I want to be able to print them out in two ways, exponential like -3.74195E3
and non-exponential like 3741.95
. Although, just looking at those two side-by-side, it should be fairly easy to turn the former into the latter by just moving the decimal point around. So let's just concentrate on the exponential form.
I have a vague recollection of an algorithm I used long ago for printing out PI where you used one of the ever-reducing formulae and kept an upper and lower limit on the possibilities, outputting a digit when both limits agreed, and shifting the calculation by a factor of 10 (so when the upper and lower limits were
3.2364
and3.1234
, you could output the3
and adjust for that in the calculation).But it's been a long time since I did that so I don't even know if that's a suitable approach to take here. It seems so since the value of each bit is half that of the previous bit when moving through the fractional part (
1/2
,1/4
,1/8
and so on).
I would really prefer not to have to go trudging through printf
source code unless absolutely necessary so, if anyone can help out with this, I'll be eternally grateful.
If you want to get exact results for every conversion, you'll have to use arbitrary-precision arithmetic, as done in printf() implementations. If you want to get results that are "close," perhaps differing only in their least significant digit(s), then a very simple double-precision based algorithm will suffice: for the integer part, repeatedly divide by ten and append the remainders to form the decimal string (in reverse); for the fractional part, repeatedly multiply by ten and subtract off the integer parts to form the decimal string.
I recently wrote an article about this method: http://www.exploringbinary.com/quick-and-dirty-floating-point-to-decimal-conversion/ . It does not print scientific notation, but that should be trivial to add. The algorithm prints subnormal numbers (the ones I printed came out accurately, but you'd have to do more thorough testing).
Denormalized numbers cannot be turned into normalized numbers of the same floating point type. The equivalent normalized number's exponent will be too small to be represented by the exponent.
To print normalized numbers, one silly way I can think of is to repeatedly multiply by 10 (well, for the fractional part).
The first thing you need to do is convert the exponent to decimal (since presumably that's what you want the output in) using logarithms. You take the fraction of that result and multiply the mantissa by the exp10 of that fraction, and then convert that to decimal characters. From there you just need to insert the decimal point in the appropriate location, shifted by the now-decimal exponent.
There is a paper by G. Steele describing in more details an algorithm which seems based on the same principle as the one you outline. If memory serve, there are time when you are forced to use unbounded precision arithmetic. (I think it is How to print floating-point numbers accurately but citeseer is currently down from here, I can't confirm and google results are polluted by a retrospective paper by the same from 20 years later).
来源:https://stackoverflow.com/questions/4274926/how-do-you-print-out-an-ieee754-number-without-printf