Взаимозаменяемость сложения и умножения IEEE 754

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

#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);
}

6

Решение

Является ли дополнение 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

9

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

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

3

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

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
1
По вопросам рекламы [email protected]