Подтвердить что ты не робот

Почему для цикла, использующего double, не удается завершить

Я просматриваю старые экзаменационные вопросы (в настоящее время первый год uni.), и мне интересно, может ли кто-нибудь объяснить более подробно, почему следующий цикл for не заканчивается, когда это предполагается. Почему это происходит? Я понимаю, что он пропускает 100.0 из-за ошибки округления или чего-то еще, но почему?

for(double i = 0.0; i != 100; i = i +0.1){
    System.out.println(i);
}
4b9b3361

Ответ 1

Число 0,1 не может быть точно представлено в двоичном формате, так как 1/3 не может быть точно представлено в десятичной форме, поэтому вы не можете гарантировать, что:

0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1==1

Это потому, что в binary:

0.1=(binary)0.00011001100110011001100110011001....... forever

Однако двойник не может содержать бесконечную точность и, следовательно, так же, как мы приближаемся от 1/3 до 0,33333333, так что бинарное представление приближается к 0,1.


Расширенная десятичная аналогия

В десятичном формате вы можете обнаружить, что

1/3+1/3+1/3
=0.333+0.333+0.333
=0.999

Это точно такая же проблема. Это не следует рассматривать как слабость чисел с плавающей запятой, так как наша собственная десятичная система имеет те же трудности (но для разных чисел кто-то, у кого есть система base-3, будет странно, что мы изо всех сил пытались представить 1/3). Однако это проблема, о которой нужно знать.

Demo

A живая демонстрация, предоставленная Андреа Лигиосом, показывает, что эти ошибки растут.

Ответ 2

Компьютеры (по крайней мере, текущие) работают с двоичными данными. Кроме того, существует ограничение длины для компьютеров для обработки в их арифметических логических единицах (например, 32 бита, 64 бита и т.д.). Представление целых чисел в двоичной форме просто наоборот, мы не можем сказать то же самое для плавающих точек. 64 bits floating point representation

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

Если мы посмотрим на максимальное значение double в java (Double.MAX_VALUE), это 1.7976931348623157E308 ( > 10 ^ 307). только с 64 битами, огромные числа могут быть представлены, однако проблема заключается в точности.

Операторы "==" и "! =" сравнивают числа поразрядным образом, в вашем случае 0,1 + 0,1 + 0,1 не равно 0,3 в виде битов, которые они представлены.

В качестве вывода, чтобы соответствовать огромным числам с плавающей запятой в несколько бит, умные инженеры решили пожертвовать точностью. Если вы работаете с плавающими точками, вам не следует использовать "==" или "! =", Если вы не уверены, что делаете.

Ответ 3

Как правило, никогда не используйте double для итерации из-за ошибок округления (0.1 может выглядеть красиво, если написано в базе 10, но попробуйте записать его в base 2 &mdash, что и используется double). То, что вы должны сделать, это использовать обычную переменную int для итерации и вычисления double из нее.

for (int i = 0; i < 1000; i++)
  System.out.println(i/10.0);

Ответ 4

Прежде всего, я расскажу кое-что о двойниках. Это будет иметь место в базе десять для удобства понимания.

Возьмите значение в одну треть и попробуйте выразить его в базе десять. Вы получаете 0.3333333333333.... Пусть говорят, что нам нужно округлить его до 4-х мест. Мы получаем 0.3333. Теперь добавьте еще 1/3. Мы получаем 0.6666333333333...., который округляется до 0.6666. Добавьте еще один 1/3. Мы получаем 0.9999, а не 1.

То же самое происходит с базой два и одна десятая. Поскольку вы идете на 0,1 10, а 0,1 10 - это повторяющееся двоичное значение (например, 0.1666666... в базе 10), у вас будет достаточно ошибки, чтобы пропустить сто, когда вы туда попадете.

1/2 может быть представлен в базовой десятке просто отлично, и 1/5 может также. Это связано с тем, что простые множители знаменателя являются подмножеством факторов базы. Это не относится к одной трети в базе десять или одна десятая в базе два.

Ответ 5

Он должен быть для (double a = 0,0; a < 100,0; a = a + 0,01)

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