C#-приём: точное сложение `double` через алгоритм Кэхэна
При сложении миллионов значений типа double небольшие ошибки округления постепенно накапливаются. Особенно плохо, когда рядом оказываются очень большие и очень маленькие числа.
Обычный вариант:
double sum = 0;
foreach (double value in values)
{
sum += value;
}
Более точный вариант:
public static double KahanSum(ReadOnlySpan values)
{
double sum = 0;
double compensation = 0;
foreach (double value in values)
{
double adjusted = value - compensation;
double next = sum + adjusted;
compensation = (next - sum) - adjusted;
sum = next;
}
return sum;
}
Переменная compensation сохраняет часть числа, потерянную при округлении, и возвращает её в следующее сложение.
Использование:
double[] values = { 0.1, 0.2, 0.3, 0.4 };
double result = KahanSum(values);
Метод полезен для:
- научных расчётов;
- статистики и аналитики;
- обработки сигналов;
- симуляций;
- накопления миллионов небольших значений.
ReadOnlySpan позволяет передавать массивы и участки памяти без дополнительных копирований.
Алгоритм требует нескольких дополнительных операций на каждом шаге, зато заметно уменьшает накопленную погрешность.
Для денежных расчётов по-прежнему лучше использовать decimal. А когда нужна скорость double и повышенная численная устойчивость – пригодится Kahan Summation.
При сложении миллионов значений типа double небольшие ошибки округления постепенно накапливаются. Особенно плохо, когда рядом оказываются очень большие и очень маленькие числа.
Обычный вариант:
double sum = 0;
foreach (double value in values)
{
sum += value;
}
Более точный вариант:
public static double KahanSum(ReadOnlySpan values)
{
double sum = 0;
double compensation = 0;
foreach (double value in values)
{
double adjusted = value - compensation;
double next = sum + adjusted;
compensation = (next - sum) - adjusted;
sum = next;
}
return sum;
}
Переменная compensation сохраняет часть числа, потерянную при округлении, и возвращает её в следующее сложение.
Использование:
double[] values = { 0.1, 0.2, 0.3, 0.4 };
double result = KahanSum(values);
Метод полезен для:
- научных расчётов;
- статистики и аналитики;
- обработки сигналов;
- симуляций;
- накопления миллионов небольших значений.
ReadOnlySpan позволяет передавать массивы и участки памяти без дополнительных копирований.
Алгоритм требует нескольких дополнительных операций на каждом шаге, зато заметно уменьшает накопленную погрешность.
Для денежных расчётов по-прежнему лучше использовать decimal. А когда нужна скорость double и повышенная численная устойчивость – пригодится Kahan Summation.