Безопасно инвертировать вычисление с плавающей точкой

Интересно, как правильно инвертировать этот расчет:

float x = a * 25.0f + b; // where a and b are integers and b is in [0-25[

Как я могу избежать возможных ошибок округления с плавающей точкой. Ответы очевидны, даже если x имеет некоторую ошибку, поэтому это должно быть возможно реализовать.

-1

Решение

Попробуйте использовать арифметика по модулю, то есть целочисленное деление / и остаток %:

int a = ((int) (x + 0.5f)) / 25;
int b = ((int) (x + 0.5f)) % 25;

если x могу иметь ошибки округления, например x = 53.999997 вместо 54 затем круглый это до ближайшего целого числа: (int) (x + 0.5f), Пожалуйста, обратите внимание, что x должно быть достаточно мал быть брошенным в int: x = 1e30f определенно потерпит неудачу.

0

Другие решения

Для диапазона а вы дали, вы не можете безопасно вернуть б.

Для = 10 ^ 6 вам нужно 20 бит. Если вы умножите на 25, вам нужно еще 5 бит. Таким образом, для экстремальных значений a вам понадобится 25 битов значений и для представления x. IEEE 754 с плавающей запятой одинарной точности предлагает только вам 24. Это означает, что x может потерять младший значащий бит. Вместо истинного значения x у вас есть x +/- 1.

Но у вас есть доступ к дополнительной информации:

  • если х<2 ^ 24, тогда вы знаете, что вы можете получить b и a наивным алгоритмом
  • если x> = 2 ^ 24 и значение нечетное ((int)(x))%4 == 2тогда вы знаете, что не было никакого округления. Действительно, отмена последнего бита является случаем точного связывания и приводит к округлению до ближайшего, даже в режиме округления по умолчанию IEEE 754.
  • только в случае x> = 2 ^ 24 и значенииand четно, вы не можете сделать вывод, и у вас есть 3 возможных значения для пары {a, b}.

Вывод: здесь вы должны использовать двойную точность

1

По вопросам рекламы [email protected]