Итак, у меня есть двойное значение, равное 1234, я хочу переместить десятичный знак, чтобы сделать его 12,34
Чтобы сделать это, я умножаю 0,1 на 1234 два раза, примерно так
double x = 1234;
for(int i=1;i<=2;i++)
{
x = x*.1;
}
System.out.println(x);
Результат будет напечатан: «12.340000000000002».
Есть ли способ, без простого форматирования до двух десятичных знаков, правильно иметь двойное хранилище 12,34?
x /= 100;
?Ответы:
Если вы используете
double
илиfloat
, вам следует использовать округление или ожидать появления ошибок округления. Если вы не можете этого сделать, используйтеBigDecimal
.Проблема в том, что 0,1 не является точным представлением, и, выполняя расчет дважды, вы усугубляете эту ошибку.
Однако 100 можно представить точно, поэтому попробуйте:
который печатает:
Это работает, потому что
Double.toString(d)
выполняет небольшое округление от вашего имени, но это не так много. Если вам интересно, как это может выглядеть без округления:печатает:
Короче говоря, округление неизбежно для разумных ответов с плавающей запятой, независимо от того, делаете вы это явно или нет.
Примечание:
x / 100
иx * 0.01
не совсем то же самое, когда дело доходит до ошибки округления. Это связано с тем, что ошибка округления для первого выражения зависит от значений x, тогда как0.01
во втором выражении фиксированная ошибка округления.отпечатки
источник
1234/100
, как вы это сделали, на самом деле ничего не делает с основной проблемой - оно должно быть в точности равно письму1234 * 0.01
./100
и*0.01
эквивалентны друг другу, но не OP*0.1*0.1
.Нет - если вы хотите точно хранить десятичные значения, используйте
BigDecimal
.double
просто не может точно представить такое число, как 0,1, точно так же, как вы не можете точно записать значение трети с конечным числом десятичных цифр.источник
если это просто форматирование, попробуйте printf
вывод
источник
System.out.printf()
это правильный путь.В финансовых программах принято использовать целые числа для монет. В школе нас учили использовать фиксированную точку вместо плавающей, но обычно это степени двойки. Хранение пенни в целых числах тоже можно назвать «фиксированной точкой».
В классе нас вообще спросили, какие числа в точности можно представить в базе.
Для
base=p1^n1*p2^n2
... вы можете представить любое N, где N = n * p1 ^ m1 * p2 ^ m2.Пусть
base=14=2^1*7^1
... вы можете представить 1/7 1/14 1/28 1/49, но не 1/3Я знаю о финансовом программном обеспечении - я преобразовал финансовые отчеты Ticketmaster из VAX asm в PASCAL. У них был свой formatln () с кодами для грошей. Причиной преобразования было то, что 32-битных целых чисел уже было недостаточно. +/- 2 миллиарда пенни - это 20 миллионов долларов, и я забыл, что это переполнено на чемпионат мира или Олимпийские игры.
Я поклялся хранить тайну. Ну что ж. В академии, если это хорошо, вы публикуете; в промышленности вы держите это в секрете.
источник
вы можете попробовать целочисленное представление
источник
r
меньше 10, нулевое заполнение не происходит, и 1204 даст результат 12,4. Правильная строка форматирования больше похожа на "% d.% 02d"Это вызвано тем, как компьютеры хранят числа с плавающей запятой. Они этого не делают. Как программист, вы должны прочитать это руководство с плавающей запятой, чтобы познакомиться с испытаниями и невзгодами обработки чисел с плавающей запятой.
источник
Забавно, что в многочисленных сообщениях упоминается использование BigDecimal, но никто не беспокоится о том, чтобы дать правильный ответ на основе BigDecimal? Потому что даже с BigDecimal вы все равно можете пойти не так, как показано в этом коде.
Дает этот вывод
Конструктор BigDecimal особо отмечает, что лучше использовать конструктор String, чем числовой конструктор. На максимальную точность также влияет необязательный MathContext.
Согласно BigDecimal Javadoc, можно создать BigDecimal, который точно равен 0,1, при условии, что вы используете конструктор String.
источник
Да, есть. С каждой двойной операцией вы можете потерять точность, но степень точности отличается для каждой операции и может быть минимизирована путем выбора правильной последовательности операций. Например, при умножении набора чисел перед умножением лучше всего отсортировать набор по экспоненте.
Любая приличная книга по обработке чисел описывает это. Например: http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html.
И чтобы ответить на ваш вопрос:
Используйте деление вместо умножения, так вы получите правильный результат.
источник
Нет, поскольку типы с плавающей запятой Java (на самом деле все типы с плавающей запятой) - это компромисс между размером и точностью. Хотя они очень полезны для множества задач, если вам нужна произвольная точность, вы должны использовать
BigDecimal
.источник