Почему я вижу двойную переменную, инициализированную для некоторого значения, такого как 21.4, как 21.399999618530273?

StackOverflow https://stackoverflow.com/questions/177506

  •  05-07-2019
  •  | 
  •  

Вопрос

double r = 11.631;
double theta = 21.4;

В отладчике они отображаются как 11.631000000000000 и 21.399999618530273 .

Как я могу избежать этого?

Это было полезно?

Решение

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

Кстати, печать этих значений во время выполнения часто все еще приводит к правильным результатам, по крайней мере, с использованием современных компиляторов C ++. Для большинства операций это не является большой проблемой.

Другие советы

Мне понравилось объяснение Джоэля , которое имеет дело с похожим двоичным плавающим проблема точечной точности в Excel 2007:

  

Посмотрите, как много в конце 0110 0110 0110? Это потому, что 0.1 не имеет точного представления в двоичном формате ... это повторяющееся двоичное число. Это похоже на то, как 1/3 не имеет представления в десятичном виде. 1/3 - это 0,33333333, и вы должны продолжать писать 3 навсегда. Если вы потеряли терпение, вы получите что-то неточное.

     

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

Если у вас есть значение, например:

double theta = 21.4;

И ты хочешь сделать:

if (theta == 21.4)
{
}

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

if (fabs(theta - 21.4) <= 1e-6)
{
}

Это частично зависит от платформы, и мы не знаем, какую платформу вы используете.

Это также частично случай, когда вы знаете, что вы на самом деле хотите увидеть. Отладчик показывает вам - в некоторой степени, в любом случае - точное значение, хранящееся в вашей переменной. В моей статье о двоичных числах с плавающей запятой в .NET есть класс C # , который позволяет увидеть абсолютно точное число, хранящееся в двойном числе. В данный момент онлайн-версия не работает - я постараюсь разместить ее на другом сайте.

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

Используйте тип decimal с фиксированной точкой, если вы хотите стабильности в пределах точности. Есть накладные расходы, и вы должны явно привести, если вы хотите преобразовать в число с плавающей запятой. Если вы преобразуете в число с плавающей запятой, вы снова будете проявлять нестабильность, которая вас беспокоит.

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

Мне приходит в голову, что вы могли бы использовать перегрузку операторов, чтобы сделать сравнения эпсилонов прозрачными. Это было бы очень круто.

<Ч>

Для представлений с показателем мантиссы EPSILON должен быть вычислен так, чтобы он оставался в пределах представимой точности. Для числа N Эпсилон = N / 10E + 14

System.Double.Epsilon - это наименьшее представимое положительное значение для типа Double . Он слишком мал для нашей цели. Прочитайте рекомендации Microsoft по тестированию на равенство

Я сталкивался с этим раньше ( в мой блог ) - я думаю, что сюрпризом является то, что «иррациональные» цифры разные.

Под «иррациональным» здесь я имею в виду тот факт, что они не могут быть точно представлены в этом формате. Действительные иррациональные числа (такие как & # 960; - pi) не могут быть точно представлены вообще.

Большинство людей знакомы с тем, что 1/3 не работает в десятичном формате: 0,3333333333333 ...

Странно то, что 1.1 не работает в float. Люди ожидают, что десятичные значения будут работать с числами с плавающей запятой, потому что они думают о них:

  

1,1 - 11 x 10 ^ -1

Когда на самом деле они в базе-2

  

1.1 - 154811237190861 x 2 ^ -47

Вы не можете избежать этого, вы просто должны привыкнуть к тому, что некоторые поплавки «иррациональны», так же, как 1/3.

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

Мне кажется, что 21.399999618530273 является представлением одинарной точности (с плавающей точкой) для 21.4. Похоже, что отладчик сбрасывает с двойной до плавающей.

Если вы используете Java и вам нужна точность, используйте класс BigDecimal для вычислений с плавающей запятой. Это медленнее, но безопаснее.

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

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

Если вам нужна фиксированная точность, возможно, вам следует попробовать числа с фиксированной точкой. Или даже целые числа. Я, например, часто использую int (1000 * x) для передачи на отладочный пейджер.

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

Улучшение отладки с помощью атрибутов отображения отладчика

См. Общая десятичная арифметика

Также обратите внимание, сравнивая поплавки, см. этот ответ для получения дополнительной информации.

По словам Javadoc

" Если хотя бы один из операндов числового оператора имеет тип double, тогда
    Операция выполняется с использованием 64-битной арифметики с плавающей точкой, и результат
    числовой оператор является значением типа double. Если другой операнд не является двойным, это
    сначала расширен (& # 167; 5.1.5), чтобы набрать double с помощью числового продвижения (& # 167; 5.6). "

Вот источник

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top