Очевидная неточность при увеличении и отображении двойных значений

Возможный дубликат:
Сохранение точности с помощью Doubles в java
Перемещение десятичных знаков в двойном формате

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

public class test {
    public static void main(String[] args){

        for (double f=1.36; f<1.40; f+=0.01) System.out.println(f); 
             //Prints 1.36
             //       1.37
             //       1.3800000000000001   ???????
             //       1.3900000000000001   ???????

            System.out.println(1.36); //Prints 1.36
            System.out.println(1.37); //Prints 1.37
            System.out.println(1.38); //Prints 1.38
            System.out.println(1.39); //Prints 1.39
    }
}

Может кто-нибудь пролить свет? Если это ошибка, как лучше всего исправить ее в коде? Какое-нибудь волшебное решение?


person Whimusical    schedule 16.05.2012    source источник
comment
docs.oracle.com/cd/E19957-01/806- 3568 / ncg_goldberg.html   -  person NPE    schedule 16.05.2012
comment
Даже JEval не обрабатывает должным образом такие операции, как 1400 * 0.001 (именно так я отслеживал ошибку) из-за этого, поэтому я не думаю, что это такой глупый вопрос, как голосование против.   -  person Whimusical    schedule 16.05.2012
comment
@ user1352530 он обрабатывается правильно в том смысле, что результат точно такой, как указано (что касается отрицательных голосов, возможно, потому, что этот вопрос, вероятно, задается один раз в день, поэтому при небольшом поиске вы, вероятно, найдете что-то, например ссылку, которую я опубликовал выше, который находится в FAQ по Java).   -  person assylias    schedule 16.05.2012
comment
Упс ... извините, если так. Я действительно искал, но вроде мало. Сложно задавать вопросы по теме, если вы не знаете, ошибка ли это или что-то у вас плохо получается, а если это числа, напечатайте, поскольку я видел, что jEval также терпит неудачу, и не знаю, хватило ли у кого-то терпения пойти пробовать любой номер, который терпит неудачу, как я, хотя это было необычно (на самом деле никогда не видел его за 7 лет).   -  person Whimusical    schedule 16.05.2012
comment
Это был не я, но если вы будете искать здесь двойную или плавающую точку, вы найдете множество вопросов от людей, которые также обнаружили, что числа с плавающей запятой не могут представлять все числа в своем диапазоне.   -  person Tony Hopkinson    schedule 16.05.2012
comment
Честно говоря, я посмотрел в Google на предмет большого количества связанных проблем, даже поместил переполнение стека в запрос и не нашел его, английский не является моим языком, и я не знал, как описать проблему, поскольку все мои вопросы (они всегда заканчиваются переписывать ) :п. Я попробую ТАК в следующий раз напрямую, кажется, то, что вы не можете найти здесь, вы не найдете нигде ...   -  person Whimusical    schedule 16.05.2012


Ответы (2)


Если вы отформатируете свой печатный вывод таким образом, вы не увидите всех этих десятичных знаков:

System.out.printf("%.2f",yourVariable);
person Mohamed Jameel    schedule 16.05.2012
comment
Спасибо, но мой var исходит из строки типа 2.333333333333, и я не знаю maxDecimals, поэтому я не могу ограничить / округлить их и сформировать - person Whimusical; 17.05.2012
comment
не беспокойтесь, это будет работать для всей десятичной длины. - person Mohamed Jameel; 17.05.2012
comment
Я имел в виду, что если я не ошибаюсь, ваше решение округляется до 2 десятичных знаков, что идеально, если мое значение в переменной составляет 1,300000000001 = 1,30, но не, если переменная имеет значение 1,34565667777, потому что тогда я теряю всю точность - person Whimusical; 18.05.2012

Пытаться

for (int i= 136; i <= 140; i++) 
    System.out.println(i / 100.0);

отпечатки

1.36
1.37
1.38
1.39
1.4

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

person Peter Lawrey    schedule 16.05.2012
comment
Я собираюсь изучить основы через ссылку aix (ответчик), но я, честно говоря, не могу понять, как самый популярный язык программирования не обрабатывает безопасно и прозрачно такую ​​базовую операцию, которую может сделать любой калькулятор ... тем более для JEval, который действует как один из тех. Спасибо! - person Whimusical; 16.05.2012
comment
Он использует en.wikipedia.org/wiki/IEEE_754-2008 арифметику с плавающей запятой, язык программирования и поддерживает ЦП. Калькуляторы обычно скрывают больше цифр и делают другие вещи, чтобы уменьшить очевидные ошибки округления. Java просто использует типы float и double, поддерживаемые вашим процессором. Существенное изменение функциональности приведет к увеличению накладных расходов и сделает ее несовместимой с другими языками. - person Peter Lawrey; 16.05.2012
comment
Вау, я не могу в это поверить. Сегодня я узнал, что в 2012 году для компьютеров не существует оптимального способа разрешить базовое значение 14 * 0,1 (1,4000000000000001) и сохранить его в переменной :) - person Whimusical; 16.05.2012
comment
@ user1352530 - Это неправильный взгляд на это. Вместо этого подумайте о том факте, что в десятичных числах вы не можете представить 1/3 конечным числом цифр, то есть ответ 0,33333 ... (повторяется бесконечно). В двоичной арифметике 1/10 - это повторяющееся десятичное число - вам понадобится бесконечное количество битов для хранения точного значения - и это просто факт жизни. Мы занимаемся этим в вычислениях, форматируя выходные данные для потребления человеком. - person Ernest Friedman-Hill; 16.05.2012
comment
Но что делает мой калькулятор за 5 евро (тоже двоичный), которого Java не может сделать в качестве альтернативы, по крайней мере? Печать 14 * 0.1 должна быть тривиальной. Я не хочу форматировать, потому что если я округлюсь, мне нужно будет ограничить максимально допустимое количество десятичных знаков. Затем, если я поставлю низкую тройку, я потеряю большую часть цифр для больших чисел, но если я поставлю высокую, то чего-то вроде 0,1 достаточно, чтобы все испортить, - person Whimusical; 16.05.2012
comment
@ user1352530 Если вы хотите точно контролировать десятичные разряды, вы можете использовать BigDecimal, но это может быть больше проблем, чем его ценность. Вам всегда нужно знать об ограничениях используемых вами типов, иначе вы столкнетесь с проблемами. - person Peter Lawrey; 16.05.2012