Перемещение десятичных знаков в двойной

Итак, у меня есть двойной набор, равный 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 правильно?

Ответы

Ответ 1

Если вы используете 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

Ответ 2

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

Ответ 3

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

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

Выход

12.34

Ответ 4

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

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. У них был свой формат formatn() с кодами за гроши. Причиной для преобразования было 32-битные целые числа, которых уже недостаточно. +/- 2 миллиарда пенни составляют 20 миллионов долларов, и это переполнение для Кубка мира или Олимпиады, я забыл.

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

Ответ 5

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

int i =1234;
int q = i /100;
int r = i % 100;

System.out.printf("%d.%02d",q, r);

Ответ 6

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

Ответ 7

Забавно, что в многочисленных сообщениях упоминается использование 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.

Ответ 8

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

Об этом говорит любая достойная книга о хрусте. Например: 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);

Ответ 9

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