2016-10-04 3 views
6

является добавление x + x взаимозаменяемы размножением 2 * x в IEEE 754 (IEC 559) с плавающей точкой стандарта, или вообще говоря, нет никаких гарантий, что case_add и case_mulвсегда дают точно такой же результат?Взаимозаменяемость IEEE 754 с плавающей точкой сложения и умножения

#include <limits> 

template <typename T> 
T case_add(T x, size_t n) 
{ 
    static_assert(std::numeric_limits<T>::is_iec559, "invalid type"); 

    T result(x); 

    for (size_t i = 1; i < n; ++i) 
    { 
     result += x; 
    } 

    return result; 
} 

template <typename T> 
T case_mul(T x, size_t n) 
{ 
    static_assert(std::numeric_limits<T>::is_iec559, "invalid type"); 

    return x * static_cast<T>(n); 
} 
+0

Обратите внимание, что там, кажется, много способов обобщающих п * х, но удивительно так много эквивалентны! Это как-то связано с http://stackoverflow.com/questions/21690585/is-3xx-always-exact и http://stackoverflow.com/questions/21676955/floating-point-product-expansion-equivalence –

ответ

9

является добавление x + x взаимозаменяемыми с помощью умножения 2 * x в IEEE 754 (IEC 559) с плавающей запятой стандарт

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

или, вообще говоря, есть ли какие-либо гарантии, что case_add и case_mul всегда дают точно такой же результат?

В целом, нет. Из того, что я могу сказать, что, кажется, держит за n <= 5:

  • n=3: а x+x точна (т.е. не включает в себя не округления), поэтому (x+x)+x только предполагает одно округление на заключительном этапе.
  • n=4 (и вы используете режим округления по умолчанию), то

    • если последний бит x равен 0, то x+x+x точен, и поэтому результаты равны по той же причине, как n=3.
    • Если последние 2 бита 01, то точное значение x+x+x будет иметь последние 2 бита 1|1 (где | обозначает конечный бит в формате), который будет округлен до 0|0. Следующее добавление даст точный результат |01, поэтому результат будет округлен, отменив предыдущую ошибку.
    • Если последние 2 бита 11, то точное значение x+x+x будет иметь последние 2 бита 0|1, которое будет округлено до 0|0. Следующее дополнение даст точный результат |11, поэтому результат будет округлен, снова отменив предыдущую ошибку.
  • n=5 (опять же, при условии, по умолчанию округления): так x+x+x+x точно, она имеет по той же причине, как n=3.

Для n=6 он не работает, например. принять x быть 1.0000000000000002 (следующий double после 1.0), в этом случае 6x является 6.000000000000002 и x+x+x+x+x+x является 6.000000000000001

+0

Ничего себе ваше доказательство намного короче, чем мое доказательство в случае анализа того, является ли 3 * x в том же бинаде, что и 2 * x или 4 * x. –

+1

Хороший подробный ответ с доказательством правильности. – plasmacel

+1

Из того, что я могу сказать на основе исчерпывающих тестов с IEEE-754 'binary32' (=' float'), умножение на * n * идентично * (n-1) * повторным добавлениям, когда * n * ≤ 5 для 'FE_TONEAREST ', * n * ≤ 3 для' FE_TOWARDZERO', * n * ≤ 3 для 'FE_DOWNWARD' и * n * ≤ 3 для' FE_UPWARD'. Является ли доказательство возможным для режимов направленного округления? – njuffa

3

Если n, например, pow(2, 54) то умножение будет работать нормально, но на пути сложения как только значение результата достаточно больше, чем на входе x, result += x даст result.

1

Да, но это не относится в целом. Умножение на число выше 2 может не дать одинаковых результатов, так как вы изменили показатель экспоненты и можете немного упасть, если вы замените его добавлением. Однако умножение на два не может отбросить бит, если их заменить операциями добавления.

1

Если аккумулятор result в case_add становится слишком большим, добавление x приведет к ошибкам округления. В определенный момент добавление x не будет иметь никакого эффекта. Таким образом, функции не будут давать тот же результат.

Например, если double x = 0x1.0000000000001p0 (шестнадцатеричное поплавка обозначения):

n case_add    case_mul 

1 0x1.0000000000001p+0 0x1.0000000000001p+0 
2 0x1.0000000000001p+1 0x1.0000000000001p+1 
3 0x1.8000000000002p+1 0x1.8000000000002p+1 
4 0x1.0000000000001p+2 0x1.0000000000001p+2 
5 0x1.4000000000001p+2 0x1.4000000000001p+2 
6 0x1.8000000000001p+2 0x1.8000000000002p+2 
Смежные вопросы