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:
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;
}
|