views:

89

answers:

3

I found this code in NVIDIA's CUDA SDK samples.

void computeGold( float* reference, float* idata, const unsigned int len)
{
    reference[0] = 0;
    double total_sum = 0;
    unsigned int i;
    for( i = 1; i < len; ++i)
    {
        total_sum += idata[i-1];
        reference[i] = idata[i-1] + reference[i-1];
    }
    // Here it should be okay to use != because we have integer values
    // in a range where float can be exactly represented
    if (total_sum != reference[i-1])
        printf("Warning: exceeding single-precision accuracy.  Scan will be inaccurate.\n");
}
//(C) Nvidia Corp

Can somebody please tell me a case where the warning would be printed, and most importantly, why.

+4  A: 

Normally, you cannot sum many floating point numbers.

Eventually the sum becomes different order of magnitude than every new added number, so precision is lost. For example, in case of float, adding a million numbers of the same order of magnitude gives the same result as ten million, because by the time it's done, every new number added doesn't change anything.

There're algorithms around this which involve a couple of multiplications for every added number (indeed, just to sum numbers properly). Yeah, floating point is tricky.

See http://floating-point-gui.de/

Pavel Radzivilovsky
+1 for the link. I was going to link to *What Every CS Should Know...*, but that is linked there, and a much more difficult read.
RBerteig
in fact, I don't like this gui.de, but I just can't seen to find a better one online.
Pavel Radzivilovsky
A: 

A float normally has a range of something like +/- 1e38, but a precision of only about 5 or 6 digits. This means, for example, that something like 12345678 can be stored, but it'll only be stored with ~6 digits of precision, so you'll get the warning.

Jerry Coffin
+2  A: 

The function is written with a certain range of input data in mind. If that input data expectation isn't met, the warning will print:

#include <stdio.h>
#define COUNT_OF(x) (sizeof(x)/sizeof(0[(x)]))

void computeGold( float* reference, float* idata, const unsigned int len)
{
    double total_sum = 0;
    unsigned int i;

    reference[0] = 0;

    for( i = 1; i < len; ++i)
    {
        total_sum += idata[i-1];
        reference[i] = idata[i-1] + reference[i-1];
    }
    // Here it should be okay to use != because we have integer values
    // in a range where float can be exactly represented
    if (total_sum != reference[i-1])
        printf("Warning: exceeding single-precision accuracy.  Scan will be inaccurate.\n");
}
//(C) Nvidia Corp


float data[] = {
    1.0,
    2.0,
    3.0,
    4.0,
    5.0
};

float data2[] = {
    123456.0,
    999999.0,
    999999.0,
    999999.0,
    999999.0,
    999999.0,
    999999.0,
    999999.0,
    999999.0,
    999999.0,
    999999.0,
    999999.0,
    999999.0,
    999999.0,
    999999.0,
    999999.0,
    999999.0,
    999999.0,
    123456.0
};

float ref[COUNT_OF(data2)] = {0.0};

int main()
{
    computeGold( ref, data, COUNT_OF(data));
    computeGold( ref, data2, COUNT_OF(data2));
    return 0;
}
Michael Burr
But my problem is that both the calculations inside compute gold add the exact same numbers in the exact same order, where are things going wrong?.
@user247077: Are you saying that you're seeing the warning, but not all the time - even for the same set of numbers?
Michael Burr
No, what I'm saying is that if the set of numbers is x1, x2,..., xn, then in both the cases, actual addition and reference addition, I'm adding them in the same order. Then why is it that I'm getting an error. I'll be really grateful if you could answer this. Thank you. PS I'm sorry for the late reply. I thought that stackoverflow would notify me of the comments.
@user247077: one set of additions is accumulated in `total_sum` which is of type `double`. The other addition is accumulated in the `reference` array members, which are of type `float`. Type `double` generally has more precision than type `float`, so it's able to accumulate results with more significant digits before losing data. As Jerry Coffin mentioned, `float` will often have only about 6 digits of precision, while double will have about 15 (note those numbers aren't hard and fast, but they're often in the ballpark for 32-bit platforms).
Michael Burr
Oh Damn! I completely missed that they had different types. :(
Btw, a very stupid and unrelated question, are you keeping track of this question manually for comments? Because I don't get any notification of new comments.
@user247077: I do not get email notification of comments getting added to the question. I get a notification in my user profile's "Recent Activity" page, which I look at periodically (sorry for how long it's taken me to get back to this...).
Michael Burr