Ява - удалить остатки поплавка элегантно - PullRequest
3 голосов
/ 31 января 2012

Вкратце - мне тяжело с оставшимися кадрами с плавающей точкой
(т. е. 10,00000123 вместо 10)

Вот и я:

Мне нужно сгенерировать список с плавающей запятой следующим образом (на самом деле это карта, мне нужно вернуть объектный орех, не обращайте на это внимания)

Список А: 0,25, 0,5, 0,75, 1, ...
или
Список B: 0,01, 0,02, 0,03, 0,04, ...

каждый раз, когда я получаю число и округляю его до ближайшей ячейки в списке.
Допустим, я получил 0,051, чтобы получить ячейку в списке A - я возвращаю 0,05.
Допустим, я получил 0,21, чтобы получить ячейку в списке B - я возвращаю 0,25.

Итак, я начал делать это

float a = Math.round(Value / step) * step;

но чем больше времени я получаю 0.2500001 (остатки поплавка) Мне нужен умный способ, чтобы обойти это.

Может быть, взять число цифр после точки и повторить

Math.round(Value / 100) * 100;?

Есть ли умнее? Я попытался сделать это

        final float factor = Math.round(1 / step);
        final float value = (float) Math.round(value * factor) / factor;

но у меня иногда есть такой список

Список А: 10, 15, 20, 25, 30, ...

и когда я получаю 22, я получаю ячейку 20. проблема в том, что когда я получаю разрыв 10

Math.round(1 / baseAssetStep) 

возвращает 0 - и я получаю NaN

Ответы [ 4 ]

6 голосов
/ 31 января 2012

Используйте BigDecimal вместо float.


Из Java Tutorials из Примитивные типы данных :

float: [...] Этот тип данных никогда не должен использоваться для точных значений, таких как валюта. Для этого вам нужно будет использовать класс java.math.BigDecimal . Числа и Строки покрывают BigDecimal и другие полезные классы, предоставляемые платформой Java.

1 голос
/ 31 января 2012

Во-первых, я бы использовал double или long вместо этого, поскольку они имеют гораздо больше цифр точности.Если вам действительно нужно, используйте BigDecimal, но довольно редко можно найти реальную ситуацию, в которой double или long не справятся с работой.

double d = 10.00000123;
double r = Math.round(d * 10000) / 10000.0;

или используйте long с точностью с фиксированной точкой.

long l = 100000; // the actual value * 10000

Распространенным вариантом использования для определения точности с фиксированной запятой являются деньги.Вместо использования долларов с double используйте центы с long или даже int.

1 голос
/ 31 января 2012

Короче говоря, «это не те цифры, которые вы ищете».

Плавающие точки представлены как двоичные дробные числа, и некоторые числа, которые могут быть легко представлены в base-10 (например, 0,01), не могут быть представлены конечным числом двоичных цифр. Это похоже на то, как 1/3 легко в базе 3 (это всего лишь 0,1), но требует бесконечного числа цифр в базе 10 (0,333 ...).

Если бы вы попытались представить 1/3 с конечным числом цифр в базе 10, вы получили бы приближение. Точно так же, если вы попытаетесь представить 1/10 с конечным числом цифр в базе 2 (что и делают float и double), вы получите приблизительное значение, аналогично 1/100. То, что вы считаете 0,01 в коде, на самом деле очень близко, но не совсем равно 1/100.

Существует множество ресурсов, касающихся плавающих точек и сложности работы с ними. http://floating -point-gui.de / - хорошее место для старта.

0 голосов
/ 31 января 2012

вам нужен постоянный пробел, поэтому, возможно, попробуйте другое решение.Возьмите пробел, назовем его G. Нарисуйте случайное int - назовем его R, (если вы знаете максимальное число в вашем списке, вы можете нарисовать его правильно).Теперь единственное, что нужно сделать, это R * G, который даст вам номер из вашего списка - конечно, с некоторой точностью, потому что это неизбежно при использовании float - чтобы напечатать его, просто используйте формат.Вы также можете комбинировать это с BigDecimal;)

...