1

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?

9
  • 1
    The debugger is just showing a representation. Depending on the debugger you can change this. Perhaps if you are so concerned you can just look at the bit patterns Commented Jul 11, 2015 at 11:44
  • Why *= 0.1 and /= 10.0 gives different results while they should be identical? Commented Jul 11, 2015 at 11:50
  • @igntec Because they are not identical in finite precision binary arithmetic. Read this. Commented Jul 11, 2015 at 11:53
  • docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html et al. Commented Jul 11, 2015 at 11:55
  • Also good reading: floating-point-gui.de Commented Jul 11, 2015 at 11:55

1 Answer 1

2

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

Demo.

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).

Sign up to request clarification or add additional context in comments.

2 Comments

Couldn't you also divide by increasing powers of 10 i.e. 10, 100, 1000 etc. to get accurate results?
@MarkRansom Right, that would also work, because multiplications to compute a positive power of ten would be exact, and then a single division would make the corresponding negative power without accumulating errors. I edited the answer and linked an additional demo. Thank you very much!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.