Вычисление среднего из двух значений, минимизация ошибок - PullRequest
8 голосов
/ 24 июня 2011

Я делаю некоторые вычисления с плавающей запятой, и результаты не настолько точны, как мне бы хотелось.

Это алгоритм:

...
center = (max_x + min_x) / 2
distance = old_x - center
new_x = center + (distance * factor)

return new_x

min_x, max_x и old_x являются числами с плавающей точкой. Я считаю, что самая большая ошибка возникает, когда я беру среднее из максимума и минимума, а затем ошибка умножается на коэффициент (который может быть плавающим).

Как я могу минимизировать ошибку из-за вычисления FP, чтобы new_x был настолько точным, насколько это возможно?

Ответы [ 5 ]

4 голосов
/ 24 июня 2011

Если old_x и center близки, то вы теряете точность.

Это называется Потеря значимости

Вы можете изменить вычисление так, чтобы в конце вычитание произошло:

center = (max_x + min_x) / 2
new_x = (center + (old_x * factor)) - (center * factor)
2 голосов
/ 24 июня 2011

В зависимости от вашего языка, вы можете использовать числовой тип с фиксированной / произвольной точностью, например, десятичный в питоне или BigDecimal в Java .

1 голос
/ 26 ноября 2013

Все предыдущие реализации не используют округление и, следовательно, имеют большую ошибку: Вот как это сделать в математике с фиксированной точкой: Я использую предвидение X.1u (1 младший бит используется для дробной части).

//center = (max_x + min_x) / 2
center = max_x + min_x // zero error here

// distance = old_x - center
distance = (old_x << 1) - center // zero error here

//new_x = center + (distance * factor)
new_x = (**1** + center + (distance * factor)) >> 1

return new_x

Если коэффициент также является фиксированной точкой (целым числом) с N битами, описывающими дробь, то new_x можно рассчитать как:

new_x = ( (1 << N) + (center << N) + (distance * factor) ) >> (N + 1)
  • (центр << N) </strong> имеет N + 1 дробных бит
  • расстояние * коэффициент имеет N + 1 дробных бит
  • (1 << N) </strong> - «половина», так как 1 << (N + 1) </strong> - «единица» в приведенной выше точности с фиксированной точкой.

После понимания каждой части вышеприведенную строку можно сжать:

new_x = ( ((1 + center) << N) + (distance * factor) ) >> (N + 1)

Используемый целочисленный тип должен быть достаточно большим, естественно. Если допустимый диапазон неизвестен, следует проверить ввод этой функции и что-то еще. В большинстве случаев это не нужно.

Это так же хорошо, как и в математике с фиксированной запятой. Так HW-схемы выполняют целочисленные математические операции.

1 голос
/ 25 июня 2011

Как говорит Йохай, ваша проблема, вероятно, вызвана вычитанием old_x - center. Если old_x и center близки друг к другу, то вы теряете точность.

Простым решением было бы сделать вычисление, используя double вместо float, но я думаю, что это невозможно. В этом случае вам нужно избавиться от вычитания. Одна возможность -

distance_max = max_x - center
distance_min = min_x - center
distance = (distance_max + distance_min) / 2
new_x = center + factor * distance

Это помогает, если max_x, min_x и center довольно далеко друг от друга, в то время как среднее значение max_x и min_x близко к center. Если это не помогает, возможно, вы можете адаптировать вычисление max_x так, чтобы вы фактически вычислили max_x - center, но это требует изменений в той части, которую вы нам не показали.

1 голос
/ 25 июня 2011

Это устраняет, по крайней мере, один источник ошибки из вашего исходного алгоритма:

# Adding min and max can produce a value of larger magnitude, losing some low-order bits
center = min_x + (max_x - min_x)/2
distance = old_x - center
new_x = center + (distance * factor)

return new_x

Если вы больше знаете о взаимосвязи между old_x, min_x и max_x, вы, вероятно, можете сделатьлучше чем это.

...