Точность double
От: Kazmerchuk Pavel  
Дата: 25.05.21 18:47
Оценка:
#include <cassert>

int main()
{
   double a = 0.014390783999999978;
   double b = 0.052996730719235739;
   double c = a - b; //-0.038605946719235763
   
   assert((b+c) == a); //b+c = 0.014390783999999976
}


Можно ли без округления вычислить c, чтобы гарантировать (b+c) == a? Спасибо.
http://coliru.stacked-crooked.com/a/eb63581b259878aa
Re: Точность double
От: reversecode google
Дата: 25.05.21 18:55
Оценка: -3
long double
Re: Точность double
От: Homunculus Россия  
Дата: 25.05.21 18:57
Оценка: +4
Здравствуйте, Kazmerchuk Pavel, Вы писали:

Забудь про «==« для плавающей точки. Только разницу с эпсилон сравнивай
Re: Точность double
От: LaptevVV Россия  
Дата: 25.05.21 19:02
Оценка:
KP>Можно ли без округления вычислить c, чтобы гарантировать (b+c) == a? Спасибо.
Нет. Даже (со)процессор делает округление.
Тип округления устанавливается в одном из регистров сопроцессора.
без округления выполняются операции со степенями двойки: 1/2 = 2^(-1), 1/4 = 2^(-2) и т.д.
Но и в этом случае выполняется нормализация, при которой младшие разряды результата могут исчезнуть.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Отредактировано 25.05.2021 19:05 LaptevVV . Предыдущая версия .
Re[2]: Точность double
От: watchmaker  
Дата: 25.05.21 19:32
Оценка: +5 :))) :))) :))) :))) :)
Здравствуйте, LaptevVV, Вы писали:

KP>>Можно ли без округления вычислить c, чтобы гарантировать (b+c) == a? Спасибо.

LVV>Даже (со)процессор делает округление.

Да тут даже до процессора дело не всегда доходит.
Компилятор выкидывает ненужные вычисления и заменяет тело функции на прямой вызов assert_fail. Не нужны в машинном коде эти ваши сравнения, если и так всё понятно
Отредактировано 25.05.2021 20:31 watchmaker . Предыдущая версия .
Re[2]: Точность double
От: Kazmerchuk Pavel  
Дата: 25.05.21 19:36
Оценка:
Здравствуйте, LaptevVV, Вы писали:

LVV>Нет. Даже (со)процессор делает округление.

А вычислить дополнительную поправку к с, чтобы, например (b + c + correction) == a?
Re: Точность double
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 25.05.21 19:40
Оценка:
Здравствуйте, Kazmerchuk Pavel, Вы писали:

KP>
KP>   double a = 0.014390783999999978;
KP>   double b = 0.052996730719235739;
KP>   double c = a - b; //-0.038605946719235763
KP>


KP>Можно ли без округления вычислить c, чтобы гарантировать (b+c) == a? Спасибо.


Нет. double умеет не больше 15ти значащих десятичных знаков хранить, а у тебя — 17. А тут a и b сразу уже хранят не те значения, которые ты им задаёшь. По идее, компилятор должен предупредить об этом, но не факт
Маньяк Робокряк колесит по городу
Re[2]: Точность double
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 25.05.21 19:40
Оценка:
Здравствуйте, reversecode, Вы писали:

R>long double


Который в MSVC, внезапно, тот же double
Маньяк Робокряк колесит по городу
Re[2]: Точность double
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 25.05.21 19:42
Оценка:
Здравствуйте, Homunculus, Вы писали:

H>Забудь про «==« для плавающей точки. Только разницу с эпсилон сравнивай


У меня — можно — https://github.com/al-martyn1/marty_decimal

Маньяк Робокряк колесит по городу
Re[3]: Точность double
От: reversecode google
Дата: 25.05.21 19:47
Оценка:
это или UB или не UB
а какое там представление он имеет в каком компилере до одного места
что то годболд msvс перестал у меня показывать
остальные gcc clang ok
Re: Точность double
От: watchmaker  
Дата: 25.05.21 20:05
Оценка: 4 (1)
Здравствуйте, 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. То есть они будут выдавать иногда чушь, но зато работать будет быстро
Отредактировано 25.05.2021 20:44 watchmaker . Предыдущая версия . Еще …
Отредактировано 25.05.2021 20:10 watchmaker . Предыдущая версия .
Отредактировано 25.05.2021 20:06 watchmaker . Предыдущая версия .
Re[4]: Точность double
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 25.05.21 20:17
Оценка: +1
Здравствуйте, reversecode, Вы писали:

R>это или UB или не UB


С чего бы это UB?


R>а какое там представление он имеет в каком компилере до одного места


А это пока ты по граблям не походил
Маньяк Робокряк колесит по городу
Re: Точность double
От: Zhendos  
Дата: 25.05.21 21:28
Оценка: 21 (2)
Здравствуйте, Kazmerchuk Pavel, Вы писали:

KP>
KP>{
KP>   double a = 0.014390783999999978;
KP>   double b = 0.052996730719235739;
KP>   double c = a - b; //-0.038605946719235763
   
KP>   assert((b+c) == a); //b+c = 0.014390783999999976
KP>}
KP>


можно вычислить ошибку (смотри D. E. Knuth, TAOCP, Vol 2, 4.2.2, Theorem B.):

#include <cassert>

double sum(double u, double v, double& t) {
    double s = u + v;
    double up = s - v;
    double vpp = s - up;
    up -= u;
    vpp -= v;
    t = -(up + vpp);

    return s;
}

int main()
{
    double a = 0.014390783999999978;
    double b = 0.052996730719235739;

    double err;
    const auto c = sum(a, -b, err);

    assert((b+c) + err == a); //b+c = 0.014390783999999976                                                                                                                    
}
Отредактировано 25.05.2021 21:30 Zhendos . Предыдущая версия .
Re[2]: Точность double
От: Kazmerchuk Pavel  
Дата: 26.05.21 09:55
Оценка:
Здравствуйте, Zhendos, Вы писали:

Z>Здравствуйте, Kazmerchuk Pavel, Вы писали:


KP>>
KP>>{
KP>>   double a = 0.014390783999999978;
KP>>   double b = 0.052996730719235739;
KP>>   double c = a - b; //-0.038605946719235763
   
KP>>   assert((b+c) == a); //b+c = 0.014390783999999976
KP>>}
KP>>


Z>можно вычислить ошибку (смотри D. E. Knuth, TAOCP, Vol 2, 4.2.2, Theorem B.):


Z>
Z>#include <cassert>

Z>double sum(double u, double v, double& t) {
Z>    double s = u + v;
Z>    double up = s - v;
Z>    double vpp = s - up;
Z>    up -= u;
Z>    vpp -= v;
Z>    t = -(up + vpp);

Z>    return s;
Z>}

Z>int main()
Z>{
Z>    double a = 0.014390783999999978;
Z>    double b = 0.052996730719235739;

Z>    double err;
Z>    const auto c = sum(a, -b, err);

Z>    assert((b+c) + err == a); //b+c = 0.014390783999999976                                                                                                                    
Z>}
Z>



Спасибо! То что нужно!
Re: Точность double
От: PM  
Дата: 26.05.21 20:17
Оценка: 1 (1)
Здравствуйте, Kazmerchuk Pavel, Вы писали:

KP>Можно ли без округления вычислить c, чтобы гарантировать (b+c) == a? Спасибо.


Еще +0.5 копеек: в статье https://habr.com/ru/post/525090/ сравниваются алгоритмы суммирования Kahan и Rump–Ogita–Oishi
Re[2]: Точность double
От: T4r4sB Россия  
Дата: 26.05.21 20:21
Оценка: +2
Здравствуйте, Zhendos, Вы писали:

Z>можно вычислить ошибку (смотри D. E. Knuth, TAOCP, Vol 2, 4.2.2, Theorem B.):


И даже это ничего не гарантирует
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re: Точность double
От: Vzhyk2  
Дата: 27.05.21 05:29
Оценка: +1
Здравствуйте, Kazmerchuk Pavel, Вы писали:

KP>Можно ли без округления вычислить c, чтобы гарантировать (b+c) == a? Спасибо.

Можно, но для начала тебе нужно открыть любой учебник по вычислениям на компьютерах и внимательно прочитать и всё поймешь, что можно, что нельзя. Раньше в школе основы оного преподавали. "Значашие цифры", "системы счисления", "периодические и не периодические дроби", "целые, рациональные и иррациональные числа" — это, например, из тех основ.
Re[2]: Точность double
От: Vzhyk2  
Дата: 27.05.21 05:30
Оценка:
Здравствуйте, Homunculus, Вы писали:

H>Забудь про «==« для плавающей точки. Только разницу с эпсилон сравнивай

Чушь. Всё зависит от того, что именно ты хочешь сделать и где.
Re[2]: Точность double
От: Vzhyk2  
Дата: 27.05.21 05:31
Оценка:
Здравствуйте, Marty, Вы писали:

M>Нет. double умеет не больше 15ти значащих десятичных знаков хранить, а у тебя — 17. А тут a и b сразу уже хранят не те значения, которые ты им задаёшь. По идее, компилятор должен предупредить об этом, но не факт

А еще в советской школе рассказывали про погрешности округления и что с ними происходит при вычислениях.
Re[2]: Точность double
От: Vzhyk2  
Дата: 27.05.21 05:34
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>Ну и ещё можно упомянуть, что компилятору обычно можно сказать, что он должен считать, что floating-point ассоциативный (например, через ключ -ffast-math в gcc/clang). Тогда он будет генерировать машинный код исходя из предположений, что (a+b)-b можно заменить сначала на a+(b-b), и потом на просто a (да, случай с NaN тоже считается несуществующим). Но не советую идти этой дорогой: тут наоборот сломаются хорошие алгоритмы, которые написаны в рассчёте на гарантии и поведение ieee754. То есть они будут выдавать иногда чушь, но зато работать будет быстро

Это нехорошие алгоритмы, хорошие обычно делают устойчивыми и им пофиг на твои ключики компилятору.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.