Перемещение десятичных знаков в двойном - PullRequest
92 голосов
/ 08 февраля 2011

Итак, у меня есть двойной набор, равный 1234, я хочу переместить десятичную запятую, чтобы она стала 12,34

Итак, чтобы сделать это, я умножаю .1 на 1234 два раза, вроде как

double x = 1234;
for(int i=1;i<=2;i++)
{
  x = x*.1;
}
System.out.println(x);

Будет напечатан результат "12.340000000000002"

Есть ли способ, без простого форматирования его до двух десятичных разрядов, чтобы правильно хранить двойное хранилище 12,34?

Ответы [ 9 ]

184 голосов
/ 08 февраля 2011

Если вы используете double или float, вы должны использовать округление или ожидать появления ошибок округления.Если вы не можете сделать это, используйте BigDecimal.

Проблема, с которой вы столкнулись, заключается в том, что значение 0,1 не является точным представлением, и, выполнив вычисление дважды, вы составите эту ошибку.

Тем не менее, 100 может быть представлено точно, поэтому попробуйте:

double x = 1234;
x /= 100;
System.out.println(x);

, который печатает:

12.34

Это работает, потому что Double.toString(d) выполняет небольшое количество округлений от вашего имени, ноэто не так многоЕсли вам интересно, как это может выглядеть без округления:

System.out.println(new BigDecimal(0.1));
System.out.println(new BigDecimal(x));

печатает:

0.100000000000000005551115123125782702118158340454101562
12.339999999999999857891452847979962825775146484375

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


Примечание: x / 100 и x * 0.01 не совсем одинаковы, когда речь идет об ошибке округления.Это связано с тем, что ошибка округления для первого выражения зависит от значений x, тогда как 0.01 во втором имеет фиксированную ошибку округления.

for(int i=0;i<200;i++) {
    double d1 = (double) i / 100;
    double d2 = i * 0.01;
    if (d1 != d2)
        System.out.println(d1 + " != "+d2);
}

печатает

0.35 != 0.35000000000000003
0.41 != 0.41000000000000003
0.47 != 0.47000000000000003
0.57 != 0.5700000000000001
0.69 != 0.6900000000000001
0.7 != 0.7000000000000001
0.82 != 0.8200000000000001
0.83 != 0.8300000000000001
0.94 != 0.9400000000000001
0.95 != 0.9500000000000001
1.13 != 1.1300000000000001
1.14 != 1.1400000000000001
1.15 != 1.1500000000000001
1.38 != 1.3800000000000001
1.39 != 1.3900000000000001
1.4 != 1.4000000000000001
1.63 != 1.6300000000000001
1.64 != 1.6400000000000001
1.65 != 1.6500000000000001
1.66 != 1.6600000000000001
1.88 != 1.8800000000000001
1.89 != 1.8900000000000001
1.9 != 1.9000000000000001
1.91 != 1.9100000000000001
52 голосов
/ 08 февраля 2011

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

46 голосов
/ 09 февраля 2011

если это просто форматирование, попробуйте printf

double x = 1234;
for(int i=1;i<=2;i++)
{
  x = x*.1;
}
System.out.printf("%.2f",x);

выход

12.34
26 голосов
/ 12 января 2012

В финансовом программном обеспечении принято использовать целые числа для копеек. В школе нас учили, как использовать фиксированную точку вместо плавающего, но обычно это степени двух. Хранение копеек в целых числах также можно назвать «фиксированной точкой».

int i=1234;
printf("%d.%02d\r\n",i/100,i%100);

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

Для 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 миллионов долларов, и это переполнено на чемпионат мира или Олимпийские игры, я забыл.

Я поклялся в тайне. Ну что ж. В академии, если это хорошо, вы публикуете; в промышленности ты держишь это в секрете.

12 голосов
/ 12 января 2012

вы можете попробовать целочисленное представление чисел

10 голосов
/ 08 февраля 2011

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

7 голосов
/ 02 апреля 2013

Забавно, что во многих постах упоминается использование BigDecimal, но никто не мешает дать правильный ответ на основе BigDecimal? Потому что даже с BigDecimal вы все равно можете пойти не так, как показано в этом коде

String numstr = "1234";
System.out.println(new BigDecimal(numstr).movePointLeft(2));
System.out.println(new BigDecimal(numstr).multiply(new BigDecimal(0.01)));
System.out.println(new BigDecimal(numstr).multiply(new BigDecimal("0.01")));

Дает этот вывод

12.34
12.34000000000000025687785232264559454051777720451354980468750
12.34

В конструкторе BigDecimal специально упоминается, что лучше использовать конструктор String, чем числовой конструктор. Предельная точность также зависит от опционального MathContext.

Согласно BigDecimal Javadoc возможно создать BigDecimal, который будет точно равным 0,1, при условии, что вы используете конструктор String.

5 голосов
/ 12 января 2012

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

Это можно описать в любой приличной книге о вычислении чисел. Например: http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

И чтобы ответить на ваш вопрос:

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

double x = 1234;
for(int i=1;i<=2;i++)
{
  x =  x / 10.0;
}
System.out.println(x);
3 голосов
/ 08 февраля 2011

Нет, поскольку Типы Java с плавающей запятой (в действительности все типы с плавающей запятой) являются компромиссом между размером и точностью.Хотя они очень полезны для многих задач, если вам нужна произвольная точность, вы должны использовать BigDecimal.

...