Comments 19
https://habrahabr.ru/post/266023/
У вас неплохой научный стиль изложения материала, но, будьте милосердны, не пишите на нём научно-популярные и прикладные статьи! Вы же на хабре, а не в Duke Mathematical Journal, будьте проще!
Кроме того, вам остро не хватает сорцов, Описываемая задача очень хорошо и приятно оборачивается в любом ООП-языке, позволяя спрятать в недра код для оценки погрешностей. Если бы вы написали обёртки над float/double/long double, позволяющие оценить погрешности вычислений, вы бы принесли сообществу реальную пользу и смогли бы измерить накопление погрешностей в популярных продуктах, например, в пакетах работы с графикой, особенно, форматах сохранения с потерей информации. А так полезность ваших статей, не смотря на их занимательность, остаётся на достаточно низком уровне.
P.S. Для получения точного значения арифметических и не только операций желательно использовать хотя бы MathCad (или, как минимум, Wolfram Alpha), который умеет выводить результаты арифметических операций с точностью до нужной цифры. Тот же Excel спокойно накапливает погрешность, хотя в нём и есть двоично-десятичные поля и numeric-типы. Ну и удобнее MathCad для таких задач.
Как мы видим, абсолютная погрешность представления оказывается ничтожно малой по сравнению с погрешностью округления при итерационном суммировании.
И все? Целый пост ради того, чтобы сообщить очевидную вещь?
Проверьте математику.
Мои результаты меньше в шесть раз:
- js: Δ=53.105953216552734; δ=9.891748879763943e-8
- c#:Δ=52,7059531211853; δ=9,81724309923268E-08
js:
"use strict";
var s = 0.0, N = 5368712233, M = N/4;
for (var i=0; i<M; i++) {
s += 0.1;
s += 0.1;
s += 0.1;
s += 0.1;
}
for (var i=0; i<N%4; i++) {
s += 0.1;
}
console.log(Math.abs(0.1*N - s), Math.abs(0.1*N - s) / (0.1*N));
C#:
double s = 0.0;
long N = 5368712233;
for (long i = 0; i < N; i++)
{
s += 0.1;
}
Console.WriteLine($"Δ={Math.Abs(0.1 * N - s)} δ={Math.Abs(0.1 * N - s) / (0.1 * N)}");
Пересчитал на С++.
Использовал /fp:strict и _controlfp_s(nullptr, _RC_CHOP, _MCW_RC)
.
Код сложения:
fld qword ptr [s]
fadd qword ptr [a]
fstp qword ptr [s]
mfence // или lock add [s], 0
Вроде бы в таком режиме процессор ну никак не должен переиспользовать значения из 80ти разрядных регистров (хотя я не уверен).
Получилось Δ=110.431599 δ=2.05695e-07, то есть в два раза больше чем раньше (и в три раза меньше чем у вас). Причем из всех факторов повлиял лишь _RC_CHOP.
UPD: что-то я не в ту сторону полез. Строгий режим же подразумевает воспроизводимость результатов на других устройствах — а значит, хитрых оптимизаций быть не может, можно было не ставить барьеров. Добавил еще _controlfp_s(nullptr, _PC_53, _MCW_PC)
для надежности. Ничего не изменилось.
Результаты ваших вычислений у вас неправильные. В три раза больше чем в действительности.
Рекуррентные формулы для расчета ошибок итерационного суммирования двоичных чисел ограниченной длины