2013-09-09 4 views
0

Рассмотрим следующий код в Java:Java, Float.parseFloat(), System.out.printf() несоответствие

String input = "33.3"; 
float num = Float.parseFloat(input); 
System.out.printf("num: %f\n",num);  

Почему выход кода выше

num: 33.299999?

это не должно быть

num: 33.300000?

Я был бы очень признателен, если бы кто-нибудь мог мне это объяснить.

+0

Точка плавающей точки. Проверьте http://floating-point-gui.de – yshavit

+0

http://stackoverflow.com/q/6713673/1053938 – jonhopkins

+0

Спасибо за указатели @yshavit – Froggy

ответ

1

Проблема в том, что плавание имеет конечную точность.

7

Вы жертву ошибки с плавающей запятой. В основании 2, 33.3 технически повторяющийся двоичный (по аналогии с повторяющимся десятичной системой), так как при записи, как m/n с m и n целыми числами и gcd(m,n)=1, простые множителями n не являются подмножеством из главных факторов 2. Это также означает, что он не может быть записан как сумма конечного числа членов m*(2^n), где m и n являются целыми числами.

Аналогичный пример происходит с 7/6 в базе 10.

_ 
1.16 

становится

1.16666667 

, который затем читать в буквальном смысле, а не равна 7/6.

+5

В двоичном коде нет десятичной point, только двоичная точка, поэтому это повторяющийся * двоичный * (не повторяющийся * десятичный *) – Bohemian

+0

@Bohemian. Вы правы, я должен использовать семантически правильный термин. – hexafraction

+1

@Bohemian Even 0.1 является повторяющимся двоичным, поскольку это не простая сумма степеней 2. –

3

Не используйте float, если вы можете этого избежать. Проблема здесь заключается в том, что %f обрабатывает его как двойной, когда он не имеет ту точность

String input = "33.3"; 
double num = Double.parseDouble(input); 
System.out.printf("num: %f\n",num); 

отпечатки

33.300000 

Это происходит потому, что 33.3f! = 33,3, как float имеет меньшую точность. Чтобы увидеть фактические значения, вы можете использовать BigDecimal, который точно отражает фактическое значение.

System.out.println("33.3f = " + new BigDecimal(33.3f)); 
System.out.println("33.3 = " + new BigDecimal(33.3)); 

печатает

33.3f = 33.299999237060546875 
33.3 = 33.2999999999999971578290569595992565155029296875 

Как вы можете видеть истинное значение, представленное немного слишком мал, в обоих случаях. В случае, как %f показывает 6 знаков после запятой, даже если флоат не точен до 8 десятичных знаков. double с точностью до 15-16 знаков после запятой, и поэтому вы не увидите ошибку, если значение не намного больше. например один триллион или более.

+0

Проблема в том, что технически даже _that_ не точно 33.3, что не представляется в любой двоичной системе с плавающей запятой. –

+0

@ ChrisJester-Young За исключением, возможно, одного, который использует 2 целых числа с фиксированной точностью, близких к рациональному числу. – hexafraction

+0

@ ChrisJester-Young Но когда вы распечатаете его, он округляет его до правильного значения. –

1

32-разрядные номера с плавающей запятой содержат достаточную точность для примерно 7 десятичных знаков точности. 33.29999 - 7 знаков после запятой. Изменить «вход» на 3.3 - вы должны увидеть 3.300000 Измените «вход» на 333,3 - вы увидите что-то вроде: 333.299988 Использование 64-битного числа с плавающей запятой даст вам больше точности (15-17 знаков после запятой места).

Смежные вопросы