Здравствуйте, Kazmerchuk Pavel, Вы писали:
KP>Можно ли без округления вычислить c, чтобы гарантировать (b+c) == a?
Разница двух double чисел не всегда представима в double без потери точности.
То есть для double ответ — нельзя.
Если необходимо считать точно подобные суммы, то тут поможет только длинная арифметика (с хранением всех значащих бит). Но понятно, что не всегда подходит и всё это довольно далеко от стандартного типа double.
А так, операции в IEEE754 c floating-point не ассоциативные. То есть в них принципиально не выполняется равенство
(a + b) + c == a + (b + c).
Нужно с этим уметь жить и писать алгоритмы соответствующим образом.
Например, этот эффект можно учитывать и минимизировать как в
алгоритме суммирования Кэхэна. Заметь, что там тоже повторяется похожий паттерн из действий
c = a — b; t = b + c; почти как у тебя в примере.
Либо этот эффект можно даже эксплуатировать во благо, например, для быстрого округления чисел без преобразования типов:
double round_pos(double x) {
const double magic = 0x1p+52;
return x + magic - magic;
}
Демо:
https://godbolt.org/z/d9qexTdWW
Ну и ещё можно упомянуть, что компилятору обычно можно сказать, что он должен считать, что floating-point ассоциативный (например, через ключ
-ffast-math в gcc/clang). Тогда он будет генерировать машинный код исходя из предположений, что
(a+b)-b можно заменить сначала на
a+(b-b), и потом на просто
a (да, случай с NaN тоже считается несуществующим). Но не советую идти этой дорогой: тут наоборот сломаются хорошие алгоритмы, которые написаны в рассчёте на гарантии и поведение ieee754. То есть они будут выдавать иногда чушь, но зато работать будет быстро