Нюансы точности с плавающей точкой - PullRequest
3 голосов
/ 15 июня 2010

Я нашел этот код в примерах NVIDIA CUDA SDK.

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

Может кто-нибудь сказать мне случай, когда предупреждение будет напечатано, и самое главное, почему.

Ответы [ 3 ]

5 голосов
/ 15 июня 2010

Обычно вы не можете сложить много чисел с плавающей запятой.

Со временем сумма становится на порядок меньше, чем каждое новое добавленное число, поэтому точность теряется. Например, в случае с плавающей запятой добавление миллиона чисел того же порядка дает тот же результат, что и десять миллионов, потому что к тому времени, когда это сделано, каждое добавленное новое число ничего не меняет.

Есть алгоритмы вокруг этого, которые включают пару умножений для каждого добавленного числа (в действительности, просто для правильного суммирования чисел). Да, с плавающей запятой сложно.

См. http://floating -point-gui.de /

2 голосов
/ 16 июня 2010

Функция написана с учетом определенного диапазона входных данных.Если ожидание этих входных данных не выполнено, выдается предупреждение:

#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;
}
1 голос
/ 15 июня 2010

A float обычно имеет диапазон примерно +/- 1e38, но с точностью до 5 или 6 цифр. Это означает, например, что что-то вроде 12345678 может быть сохранено, но оно будет храниться только с точностью ~ 6 цифр, поэтому вы получите предупреждение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...