Является ли дополнение 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);
}
Является ли дополнение
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
,01
, тогда точное значение x+x+x
будет иметь последние 2 бита 1|1
(где | указывает последний бит в формате), который будет округлен до 0|0
, Следующее дополнение даст точный результат |01
, поэтому результат будет округлен в меньшую сторону, отменяя предыдущую ошибку.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
Если n
например pow(2, 54)
тогда умножение будет работать нормально, но в пути сложения, когда значение результата будет достаточно большим, чем вход x
, result += x
даст result
,
Да, но в целом это не так. Умножение на число, большее 2, может не дать таких же результатов, так как вы изменили экспоненту, и может немного упасть, если заменить на add. Однако умножение на два не может немного уменьшиться, если его заменить операциями добавления.
Если аккумулятор 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