Как избежать ошибок с плавающей запятой, когда вам нужно использовать float?

Я пытаюсь повлиять на перевод 3D-модели с помощью некоторых кнопок пользовательского интерфейса, чтобы сдвинуть позицию на 0,1 или -0,1.

Моя модельная позиция представляет собой трехмерный поплавок, поэтому просто добавление 0,1f к одному из значений вызывает очевидные ошибки округления. Хотя я могу использовать что-то вроде BigDecimal для сохранения точности, мне все равно придется преобразовать его из float и обратно в float в конце, и это всегда приводит к глупым числам, которые делают мой пользовательский интерфейс похожим на беспорядок.

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

Итак, как я могу избежать этих ошибок, когда мне нужно использовать float?

4 ответа

Я бы использовал класс Rational. Их много - этот выглядит так, как будто он должен работать.

Одна значительная стоимость будет заключаться в том, что Rational отображается в float и один, когда знаменатель сводится к gcd. Тот, который я опубликовал, всегда сохраняет числитель и знаменатель в полностью восстановленном состоянии, что должно быть достаточно эффективным, если вы всегда добавляете или вычитаете 1/10.

Эта реализация сохраняет нормированные значения (т.е. с согласованным знаком), но не восстанавливается.

Вы должны выбрать свою реализацию, чтобы наилучшим образом соответствовать вашему использованию.


Простым решением является либо использование фиксированной точности. то есть целое число 10x или 100x, что вы хотите.

float f = 10;
f += 0.1f;

становится

int i = 100;
i += 1; // use an many times as you like
// use i / 10.0 as required.

Я бы не использовал float в любом случае, так как вы получите больше ошибок округления, чем ******, если у вас нет миллионов значений float. ****** дает вам еще 8 цифр точности и с разумное округление не увидит этих ошибок.


Если вы придерживаетесь поплавков: Самый простой способ избежать ошибки - использовать поплавки, которые являются точными, но около желаемого значения, которое

round (2 ^ n * value) * 1/2 ^ n.

n - количество бит, значение используемого числа (в вашем случае 0.1)

В вашем случае с повышенной точностью:

n = 4 = > 0,125 n = 8 (байт) = > 0,9765625 n = 16 (короткий) = > 0,100006103516....

Длинные цепочки чисел являются артефактами двоичного преобразования, реальное число имеет гораздо меньше бит.

Поскольку поплавки точны, сложение и вычитание будут не вводить ошибки смещения, но всегда будет предсказуемым, если количество бит не дольше, чем удерживается значение float.

Если вы опасаетесь, что ваш дисплей будет скомпрометирован используя это решение (потому что это нечетные поплавки), используйте и хранить только целые числа (шаг увеличения -1/1). Конечное значение, которое установлено внутри, равно

x = значение * шаг.

По мере того как шаг увеличивается или уменьшается на величину 1, точность будет сохранена.

licensed under cc by-sa 3.0 with attribution.