for the last few days, I was trying to write a function that convert IEEE 754 double precision floating points to string and it turns out to be more difficult than I expected.

When the exponent is in the range (the decimal point is within the mantissa) it is not that difficult to format it to string but when the exponent it outside the range (there is many zeros to left or to the right) you need multi-precision representation of the number so you can do multiplication and division.

I looked at glibc implementation of printf and it actually uses the MP library to handle this case.

Does anyone know a**complete** implementation of float formatting that I can look at?

I wonder what Casey will do when replacing snprintf?

Here is the function that I have written:

When the exponent is in the range (the decimal point is within the mantissa) it is not that difficult to format it to string but when the exponent it outside the range (there is many zeros to left or to the right) you need multi-precision representation of the number so you can do multiplication and division.

I looked at glibc implementation of printf and it actually uses the MP library to handle this case.

Does anyone know a

I wonder what Casey will do when replacing snprintf?

Here is the function that I have written:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | int Log10(u64 Num) { int Result = 0; while(Num) { Num /= 10; Result++; } return Result; } void PrintFloat(double Double) { u64 I = *(u64 *)&Double; bool32 Sign = (I&(1ULL<<63))?true:false; s32 Exp = ((I>>52) & ((1<<11)-1)) - 1023; u64 Man = I & ((1ULL<<52)-1); u64 NormalMan = Man | (1ULL<<52); u64 Number = 0; u64 Fraction = 0; s32 NormalizedExp = Exp - 52; if(NormalizedExp < 0 && NormalizedExp >= -(64-4)) { Number = NormalMan >> -NormalizedExp; Fraction = NormalMan & ((1ULL<<-NormalizedExp)-1); } else if(NormalizedExp >= 0 && NormalizedExp <= (64-53)) { Number = NormalMan << NormalizedExp; } else { // TODO case that cannot handle } printf("%s%llu.", Sign?"-":"", Number); u32 Log = Log10((1ULL<<52)-1); u32 Count = 0; if(Fraction == 0) { printf("0"); } else { while(Fraction && Count++<Log) { Fraction *= 10; printf("%u", Fraction >> (-NormalizedExp)); Fraction &= (1ULL<<(-NormalizedExp)) - 1; } } printf("\n%f\n\n", I); } int main(u32 ArgCount, char **Args) { PrintFloat(1000000000000000000000000000000000000000000000000000000000000.0); PrintFloat(0.3); PrintFloat(100150.0); PrintFloat(10011000000000000000.0); PrintFloat(1.0/(256)); PrintFloat(0.0009); return 0; } |

I would look into Dragon4 for starters, after that I would read this:

http://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf

Here is a link with the Dragon4 implementation:

http://www.ryanjuckett.com/progra.../printing-floating-point-numbers/

http://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf

Here is a link with the Dragon4 implementation:

http://www.ryanjuckett.com/progra.../printing-floating-point-numbers/

Edited by Caleb
on

Yes, to accurately print all floating point numbers you will need to use multiple-precision integer implementation. If you can put some limitations (like only 2 fractional digits needed or similar) then you can probably get away with simpler method.

To test your function - loop over all valid floats. They are not so many, only ~4 billion:

To test your function - loop over all valid floats. They are not so many, only ~4 billion:

1 2 3 4 5 6 7 8 9 10 11 12 13 | uint32_t x = 0; do { float f = *((float*)&x); if (f is not infinity and f is not NaN) { convert f to string with C standard library function convert f to string using your function and compare strings! } x++ } while (x != 0); |

Edited by Mārtiņš Možeiko
on

@cubercaleb Thanks man the second link is just super amazing.

The explanation is soooo good.

The explanation is soooo good.

Edited by Ameen Sayegh
on