Пример с плавающей точкой — небезопасный аддитивный идентификатор c ++ (a + 0.0! = a)

В Статья MSDN, упоминается, когда включен режим fp: fast, такие операции, как аддитивная идентификация (a±0.0 = a, 0.0-a = -a) небезопасны. Есть ли пример, который a+0 != a под такой режим?

РЕДАКТИРОВАТЬ: Как кто-то упоминал ниже, этот тип проблемы обычно возникает при сравнении. Моя проблема из сравнения, psedocode выглядит следующим образом:

for(i=0;i<v.len;i++)
{
sum+=v[i];
if( sum >= threshold) break;
}

Это ломается после добавления значения 0 (v[i]). v[i] не из расчета, он назначен. Я понимаю, если мой v[i] это из расчета, то округление может вступить в игру, но почему, хотя я даю v[i] нулевое значение, у меня все еще есть это sum < threshold но sum + v[i] >= threshold?

7

Решение

упоминается, когда включен режим fp: fast, такие операции, как аддитивная идентификация (a ± 0.0 = a, 0.0-a = -a), небезопасны.

О чем говорит эта статья

Любое из следующих (небезопасных) алгебраических правил может использоваться оптимизатором, когда включен режим fp: fast:

И тогда он перечисляет ± 0,0 = а и 0,0-а = -а

Это не говорит о том, что эти идентификационные данные небезопасны, если включен fp: fast. Это говорит о том, что эти идентификаторы не верны для арифметики IEEE 754, но что / fp: fast оптимизирует, как если бы они были истинными.

Я не уверен в примере, который показывает, что a + 0.0 == a быть ложным (за исключением NaN, очевидно), но IEEE 754 имеет много тонкостей, например, когда промежуточные значения должны быть усечены. Одна возможность состоит в том, что если у вас есть какое-то выражение, которое включает в себя + 0.0, это может привести к требованию в соответствии с IEEE 754 выполнять усечение для промежуточного значения, но при этом / fp: fast будет генерировать код, который не выполняет усечение, и, следовательно, более поздние результаты могут отличаться от того, что строго требуется в IEEE 754.


Используя информацию Паскаля Куока, вот программа, которая производит различный вывод на основе / fp: fast

#include <cmath>
#include <iostream>

int main() {
volatile double a = -0.0;
if (_copysign(1.0, a + 0.0) ==  _copysign(1.0, 0.0)) {
std::cout << "correct IEEE 754 results\n";
} else {
std::cout << "result not IEEE 754 conformant\n";
}
}

При сборке с / fp: fast программа выдает «результат не соответствует IEEE 754», а при сборке с / fp: strict программа выводит «правильные результаты IEEE 754».

1

Другие решения

Причина, по которой он «небезопасен», заключается в том, что то, что компилятор предполагает равным нулю, в действительности может не оказаться нулевым из-за ошибок округления.

Возьмем этот пример, который добавляет два числа с плавающей точкой на грани точности, которую допускают 32-разрядные числа с плавающей точкой:

    float a = 33554430, b = 16777215;
float x = a + b;
float y = x - a - b;
float z = 1;
z = z + y;

При использовании fp: fast компилятор говорит: «поскольку x = a + b, y = x — a — b = 0, поэтому« z + y »- это просто z». Тем не менее, из-за ошибок округления, y на самом деле заканчивается -1, а не 0. Таким образом, вы получите другой результат без fp: fast.

3

Он не говорит что-то «фиксированное», например: «если вы установите / fp: fast, а переменная a окажется 3.12345, то + 0 может не быть». Он говорит, что когда вы устанавливаете / fp: fast, компилятор будет использовать ярлыки, которые означают, что если вы вычислите + 0, а затем сравните это с тем, что вы сохранили для a, то нет гарантии, что они будут одинаковыми.

Здесь есть отличная статья об этом классе проблем (которые свойственны вычислениям с плавающей запятой на компьютерах) здесь: http://www.parashift.com/c++-faq-lite/floating-point-arith2.html

2

Если a является -0.0, затем a + 0.0 +0,0.

2
По вопросам рекламы [email protected]