Reputation: 1056
VS minimum double value = 2.2250738585072014e-308. atof function converts string to double value such as when you look at this value in the debugger you get original string representation.
double d = atof("2.2250738585072014e-308"); // debugger will show 2.2250738585072014e-308
As we can see, double value is not denormalized (there is no DEN)
I try to achieve the same precision when converting string to double. Here is the code:
double my_atof(char* digits, int digits_length, int ep)
{
int idot = digits_length;
for (int i = 0; i < digits_length; i++)
{
if (digits[i] == '.')
{
idot = i;
break;
}
}
double accum = 0.0;
int power = ep + idot - 1;
for (int i = 0; i < digits_length; i++)
{
if (digits[i] != '.')
{
if (digits[i] != '0')
{
double base_in_power = 1.0;
if (power >= 0)
{
for (int k = 0; k < power; k++) base_in_power *= 10.0;
}
else if (power < 0)
{
for (int k = 0; k < -power; k++) base_in_power *= 0.1;
}
accum += (digits[i] - '0') * base_in_power;
}
power--;
}
else power = ep - 1;
}
return accum;
}
Now, let's try:
char* float_str = "2.2250738585072014";
int float_length = strlen(float_str);
double d = my_atof(float_str, float_length, -308);
Debugger shows that d = 2.2250738585072379e-308. I tried to substitute
for (int k = 0; k < -power; k++) base_in_power *= 0.1;
with
for (int k = 0; k < -power; k++) base_in_power /= 10.0;
but it results in denormalized value. How to achieve the same precision as VS does, such that debugger will show the same number?
Upvotes: 1
Views: 765
Reputation: 726569
The problem is with double representation of the 0.1
constant, or the division by 10.0
, which produces exactly the same result: negative powers of ten have no exact representation in floating-point numbers, because they have no exact representation as a sum of negative powers of 2.
When you compute negative powers of ten by repeated multiplication, you accumulate the error. First few negative powers come out right, but after about 0.000001
the difference becomes visible. Run this program to see what is happening:
double p10[] = {
0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001, 0.0000001, 0.00000001, 0.000000001, 0.0000000001
};
int main(void) {
double a = 1;
for (int i = 0 ; i != 10 ; i++) {
double aa = a * 0.1;
double d = aa - p10[i];
printf("%d %.30lf\n", aa == p10[i], d);
a = aa;
}
return 0;
}
The output looks like this:
1 0.000000000000000000000000000000
1 0.000000000000000000000000000000
1 0.000000000000000000000000000000
1 0.000000000000000000000000000000
1 0.000000000000000000000000000000
0 0.000000000000000000000211758237
0 0.000000000000000000000026469780
0 0.000000000000000000000001654361
0 0.000000000000000000000000206795
0 0.000000000000000000000000025849
The first few powers match exactly, but then some differences start appearing. When you use powers that you compute to compose the number during your string-to-float conversion, the accumulated errors make it into the final result. If the library function uses a look-up table (see this implementation for an example), the result that you get would be different from result that they get.
You can fix your implementation by hard-coding a table of negative powers of ten, and referencing this table instead of computing the powers manually. Alternatively you could construct a positive power of ten by consecutive multiplications, and then do a single division 1 / pow10
to construct the corresponding negative power (demo).
Upvotes: 2