Представления с плавающей точкой, кажется, делают целочисленную арифметику правильно - почему?

javascript c++ floating-point

989 просмотра

8 ответа

Я немного поигрался с числами с плавающей запятой, и на основании того, что я узнал о них в прошлом, тот факт, что в 0.1 + 0.2итоге получается нечто подобное 0.30000000000000004, меня не удивляет.

Что делает меня удивляет, однако, является то , что целочисленная арифметика всегда кажется работать нормально и не имеет какие - либо из этих артефактов.

Я впервые заметил это в JavaScript (Chrome V8 в node.js):

0.1 + 0.2 == 0.3 // false, NOT surprising
123456789012 + 18 == 123456789030  // true
22334455667788 + 998877665544 == 23333333333332 // true
1048576 / 1024 == 1024  // true

C ++ (gcc в Mac OS X), похоже, обладает такими же свойствами.

В итоге получается, что целые числа просто - из-за отсутствия лучшего слова - работают . Только когда я начинаю использовать десятичные числа, все становится шатко.

Это особенность проекта, математический артефакт или некоторая оптимизация, выполняемая компиляторами и средами выполнения?

Автор: MarcWan Источник Размещён: 12.11.2019 09:46

Ответы (8)


3 плюса

Решение

Я пишу это в предположении, что Javascript использует представление с плавающей запятой двойной точности для всех чисел.

Некоторые числа имеют точное представление в формате с плавающей запятой, в частности, все целые числа, такие что |x| < 2^53. Некоторые числа не имеют, в частности, таких дробей, как 0,1 или 0,2, которые становятся бесконечными дробями в двоичном представлении.

Если все операнды и результат операции имеют точное представление, тогда было бы безопасно сравнить результат с помощью ==.

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

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

Почему десятичные числа не могут быть представлены точно в двоичном формате?

Автор: Andrey Размещён: 25.10.2012 09:13

4 плюса

Это особенность проекта, математический артефакт или некоторая оптимизация, выполняемая компиляторами и средами выполнения?

Это особенность реальных чисел. Теорема из современной алгебры (современная алгебра, а не алгебра средней школы; по математике берут класс в современной алгебре после их базового исчисления и классов линейной алгебры) говорит, что для некоторого положительного целого числа b любое положительное действительное число r может быть выражено как r = a * b p , где a в [1, b ), а p - некоторое целое число. Например, 1024 10 = 1,024 10 * 10 3 . Именно эта теорема оправдывает наше использование научных обозначений.

Это число а может быть классифицировано как терминальное (например, 1,0), повторяющееся (1/3 = 0,333 ...) или неповторяющееся (представление числа пи). Здесь есть небольшая проблема с номерами терминалов. Любой номер терминала также может быть представлен как повторяющийся номер. Например, 0,999 ... и 1 - это одно и то же число. Эта неоднозначность в представлении может быть решена путем указания того, что числа, которые могут быть представлены как номера терминалов, представлены как таковые.

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

Здесь возникает проблема с тем, как реальные данные представлены в компьютере. Так же, как intи long long intне представляют все целые числа, floatи doubleне представляют все действительные числа. Схема , используемая на большинстве компьютера , чтобы представить действительное число г является представление в виде г = а * 2 р , но с мантиссой (или мантиссой) усечено до определенного числа бит , и показатель степени р ограничивается к некоторому конечному числу , Это означает, что некоторые целые числа не могут быть представлены точно. Например, даже если гугол (10 100) является целым числом, это представление с плавающей точкой не является точным. Базовое представление гугола - это 333-битное число. Эта 333-битная мантисса усекается до 52 + 1 бит.

Следствием этого является то, что арифметика двойной точности больше не является точной, даже для целых чисел, если рассматриваемые целые числа больше 2 53 . Попробуйте свой эксперимент, используя тип unsigned long long intзначений от 2 53 до 2 64 . Вы обнаружите, что арифметика двойной точности больше не является точной для этих больших целых чисел.

Автор: David Hammen Размещён: 25.10.2012 10:16

2 плюса

Целые числа в пределах представимого диапазона точно представлены машиной, а плавающие - нет (ну, большинство из них).

Если под «базовой целочисленной математикой» вы понимаете «особенность», то да, вы можете предположить, что правильно реализовать арифметику - это особенность.

Автор: Luchian Grigore Размещён: 25.10.2012 08:40

2 плюса

Причина в том, что вы можете представлять каждое целое число (1, 2, 3, ...) точно в двоичном формате (0001, 0010, 0011, ...)

Вот почему целые числа всегда правильные, потому что 0011 - 0001 всегда 0010. Проблема с числами с плавающей запятой состоит в том, что часть после точки не может быть точно преобразована в двоичную.

Автор: LuigiEdlCarno Размещён: 25.10.2012 08:41

1 плюс

Все случаи, когда вы говорите «работа», это те случаи, когда числа, которые вы указали, могут быть представлены точно в формате с плавающей запятой. Вы обнаружите, что сложение 0.25, 0.5 и 0.125 тоже работает, потому что они также могут быть представлены точно в двоичном числе с плавающей запятой.

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

Автор: jcoder Размещён: 25.10.2012 08:40

1 плюс

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

Смотрите: https://stackoverflow.com/a/9650037/140740 для полного объяснения.

Автор: DigitalRoss Размещён: 16.11.2012 01:37

0 плюса

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

Автор: Aki Suihkonen Размещён: 25.10.2012 08:44

-1 плюса

Все числа с плавающей точкой не могут быть представлены. это из-за способа их кодирования. Вики-страница объясняет это лучше меня: http://en.wikipedia.org/wiki/IEEE_754-1985 . Поэтому, когда вы пытаетесь сравнить число с плавающей запятой, вы должны использовать дельту:

myFloat - expectedFloat < delta

Вы можете использовать наименьшее представимое число с плавающей запятой как дельта.

Автор: Patrice Bernassola Размещён: 25.10.2012 08:44
Вопросы из категории :
32x32