Вкратце: как я могу выполнить a+b
так, чтобы любая потеря точности из-за усечения была от нуля , а не к нулю?
Длинная история
Я вычисляю сумму длинного ряда значений с плавающей запятой с целью вычисления среднего значения выборки и дисперсии множества. Поскольку Var (X) = E (X 2 ) - E (X) 2 , достаточно поддерживать счет числа всех чисел, сумму всех числа на данный момент и сумма квадратов всех чисел на данный момент.
Пока все хорошо.
Однако абсолютно необходимо, чтобы E (X 2 )> E (X) 2 , что из-за точности с плавающей запятой не всегда дело. В псевдокоде проблема заключается в следующем:
int count;
double sum, sumOfSquares;
...
double value = <current-value>;
double sqrVal = value*value;
count++;
sum += value; //slightly rounded down since value is truncated to fit into sum
sumOfSquares += sqrVal; //rounded down MORE since the order-of-magnitude
//difference between sqrVal and sumOfSquares is twice that between value and sum;
Для переменных последовательностей это не большая проблема - в итоге вы немного недооцениваете дисперсию, но часто это не большая проблема. Однако для постоянных или почти постоянных наборов с ненулевым средним значением это может означать, что E (X 2 ) 2 , в результате получается отрицательная вычисленная дисперсия, которая нарушает ожидания потребления кода.
Теперь я знаю о суммировании Кахана, которое не является привлекательным решением. Во-первых, это делает код восприимчивым к капризам оптимизации (в зависимости от флагов оптимизации, код может проявлять или не проявлять эту проблему), а во-вторых, проблема не в действительно из-за точности - что достаточно хорошо - это потому, что сложение вносит систематическую ошибку в сторону нуля. Если бы я мог выполнить строку
sumOfSquares += sqrVal;
таким образом, чтобы гарантировать, что sqrVal округлен, а не уменьшен до точности sumOfSquares, у меня было бы численно разумное решение. Но как мне этого достичь?
Изменить: Законченный вопрос - почему нажатие клавиши ввода в раскрывающемся списке в поле тега так или иначе отправляет вопрос?