Урок №33. Типы данных с плавающей точкой: float, double и long double
Обновл. 11 Сен 2021 |
Типы данных с плавающей точкой
Есть три типа данных с плавающей точкой: float, double и long double. Язык C++ определяет только их минимальный размер (как и с целочисленными типами). Типы данных с плавающей точкой всегда являются signed (т.е. могут хранить как положительные, так и отрицательные числа).
| Тип | Минимальный размер | Типичный размер | |
| Тип данных с плавающей точкой | float | 4 байта | 4 байта |
| double | 8 байт | 8 байт | |
| long double | 8 байт | 8, 12 или 16 байт |
Объявление переменных разных типов данных с плавающей точкой:
Если нужно использовать целое число с переменной типа с плавающей точкой, то тогда после этого числа нужно поставить разделительную точку и нуль. Это позволяет различать переменные целочисленных типов от переменных типов с плавающей запятой:
Обратите внимание, литералы типа с плавающей точкой по умолчанию относятся к типу double. f в конце числа означает тип float.
Экспоненциальная запись
Обычно, в экспоненциальной записи, в целой части находится только одна цифра, все остальные пишутся после разделительной точки (в дробной части).
На практике экспоненциальная запись может использоваться в операциях присваивания следующим образом:
Double Структура
Определение
Некоторые сведения относятся к предварительной версии продукта, в которую до выпуска могут быть внесены существенные изменения. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.
Представляет число двойной точности с плавающей запятой.
Примеры
В следующем примере кода показано использование Double :
Комментарии
Floating-Point представление и точность
DoubleТип данных хранит значения с плавающей запятой двойной точности в 64-разрядном двоичном формате, как показано в следующей таблице.
| Отделение | Bits |
|---|---|
| Значащим или мантисса | 0-51 |
| Показатель степени | 52-62 |
| Знак (0 = положительный, 1 = отрицательный) | 63 |
Точно так же, как десятичные дроби не могут точно представлять некоторые дробные значения (например, 1/3 или Math.PI ), двоичные дроби не могут представлять некоторые дробные значения. Например, 1/10, которая точно представляется в виде десятичной дроби, представляется в виде. 001100110011 в виде двоичной дроби с шаблоном «0011», повторяющимся до бесконечности. В этом случае значение с плавающей запятой обеспечивает неточное представление числа, которое оно представляет. Выполнение дополнительных математических операций с исходным значением с плавающей запятой часто приводит к увеличению нехватки точности. Например, если сравнить результат умножения на 10, а затем увеличить значение от 0,1 до. 1 9 раз, мы видим, что это сложение, так как оно включало восемь дополнительных операций, создало менее точный результат. Обратите внимание, что это различие очевидно только в том случае, если мы отображаем два Double значения с помощью строки стандартного числового форматаR, которая при необходимости отображает все 17 знаков точности, поддерживаемые Double типом.
Поскольку некоторые числа не могут быть представлены в виде дробных двоичных значений, числа с плавающей запятой могут быть приблизительными только вещественными числами.
Ограниченная точность числа с плавающей запятой имеет несколько последствий:
Два числа с плавающей запятой, которые могут казаться равными при определенной точности, на самом деле отличаются, поскольку их менее значащие цифры различаются. В следующем примере ряд чисел добавляется вместе, а их итог сравнивается с ожидаемым итогом. Хотя два значения выглядят одинаково, вызов Equals метода указывает, что они не являются.
Если изменить элементы форматирования в Console.WriteLine(String, Object, Object) инструкции с <0>и <1>на <0:R>и, <1:R>чтобы отобразить все значащие цифры двух Double значений, то ясно, что эти два значения не равны из-за потери точности во время операций сложения. В этом случае проблему можно устранить, вызвав Math.Round(Double, Int32) метод, чтобы округлить Double значения до нужной точности перед выполнением сравнения.
Математическая операция OR, использующая число с плавающей запятой, может не дать одинакового результата, если используется десятичное число, так как число двоичных с плавающей запятой может не совпадать с десятичным числом. Предыдущий пример демонстрирует это, отображая результат умножения десята на 10 и добавляя. 1 раз.
Если точность числовых операций с дробными значениями важна, можно использовать Decimal вместо Double типа. Если точность числовых операций с целочисленными значениями вне диапазона Int64 UInt64 типов или важна, используйте BigInteger тип.
Значение может не циклически передавалться, если используется число с плавающей запятой. Значение считается циклическим, если операция преобразует исходное число с плавающей запятой в другую форму, операция обратного преобразования преобразует преобразованную форму обратно в число с плавающей запятой, а окончательное число с плавающей запятой не равно исходному числу с плавающей запятой. Цикл обработки может завершиться ошибкой, поскольку одна или несколько наименьших значащих цифр теряются или изменяются при преобразовании. В следующем примере три Double значения преобразуются в строки и сохраняются в файле. Как видно из выходных данных, несмотря на то, что значения выглядят идентичными, восстановленные значения не равны исходным значениям.
В этом случае можно успешно выполнить циклический обмен значениями, используя стандартную строку числового формата «G17» для сохранения полной точности Double значений, как показано в следующем примере.
При использовании со Double значением описатель формата R в некоторых случаях не может успешно выполнить циклический обмен исходным значением. Чтобы обеспечить Double успешный циклический обмен значениями, используйте описатель формата «G17».
Чтобы избежать этой проблемы, используйте либо Double вместо Single типа данных, либо используйте Round метод, чтобы оба значения имели одинаковую точность.
Проверка на равенство
Чтобы считаться равными, два Double значения должны представлять одинаковые значения. Однако из-за различий в точности между значениями или из-за потери точности на одно или оба значения значения с плавающей запятой, которые должны быть одинаковыми, часто становятся неравными из-за различий в их минимально значащих цифрах. В результате вызовы Equals метода для определения того, равны ли два значения, или вызовы CompareTo метода для определения связи между двумя Double значениями, часто дают непредвиденные результаты. Это очевидно в следующем примере, где два очевидных значения могут Double быть неравными, так как первая имеет 15 разрядов точности, а вторая — 17.
В случаях, когда вероятность потери точности может повлиять на результат сравнения, можно принять любые из следующих альтернатив для вызова Equals CompareTo метода или:
Вызовите Math.Round метод, чтобы убедиться, что оба значения имеют одинаковую точность. Следующий пример изменяет предыдущий пример, чтобы использовать этот подход, чтобы два дробных значения были эквивалентными.
Проблема точности по-прежнему применима к округлению средних значений. Дополнительные сведения см. в описании метода Math.Round(Double, Int32, MidpointRounding).
Проверка на приблизительный равенство, а не на равенство. Для этого необходимо определить абсолютную величину, с которой два значения могут различаться, но по-прежнему равны, или определить относительный объем, на который меньшее значение может отличаться от большего.
Double.Epsilon иногда используется в качестве абсолютной меры расстояния между двумя Double значениями при проверке на равенство. Однако Double.Epsilon измеряет наименьшее возможное значение, которое можно добавить или вычесть из, Double значение которого равно нулю. Для большинства положительных и отрицательных Double значений значение Double.Epsilon слишком мало для обнаружения. Таким образом, за исключением нулевых значений, не рекомендуется использовать его в тестах на равенство.
В следующем примере используется второй подход для определения IsApproximatelyEqual метода, который проверяет относительное различие между двумя значениями. Он также отличается от результата вызовов IsApproximatelyEqual метода и Equals(Double) метода.
Floating-Point значения и исключения
В отличие от операций с целочисленными типами, которые создают исключения в случае переполнения или недопустимых операций, таких как деление на ноль, операции с значениями с плавающей запятой не создают исключения. Вместо этого в исключительных ситуациях результат операции с плавающей запятой равен нулю, плюс бесконечность, отрицательная бесконечность или не является числом (NaN):
Если результат операции с плавающей запятой слишком мал для конечного формата, результат равен нулю. Это может произойти при умножении двух очень маленьких чисел, как показано в следующем примере.
PositiveInfinity также результаты из деления на ноль с положительным делимым и NegativeInfinity результатом деления на ноль с отрицательным делимым.
Преобразования типов и двойная структура
В Double структуре не определены явные или неявные операторы преобразования. вместо этого преобразования реализуются компилятором.
Обратите внимание, что преобразование значения некоторых числовых типов в Double значение может привести к утрате точности. Как показано в примере, возможна утрата точности при преобразовании Decimal значений, Int64 и UInt64 в Double значения.
| Тип результирующего значения | Результат |
|---|---|
| Любой целочисленный тип | OverflowExceptionИсключение, если преобразование происходит в проверяемом контексте. Если преобразование происходит в непроверяемом контексте (по умолчанию в C#), операция преобразования выполняется успешно, но значение переполняется. |
| Decimal | Исключение OverflowException. |
| Single | Single.NegativeInfinity для отрицательных значений. Single.PositiveInfinity для положительных значений. |
Обратите внимание, что при преобразовании Double значения в другой числовой тип может произойти утрата точности. в случае преобразования в любой из целочисленных типов, как показано в выходных данных примера, дробный компонент теряется, когда Double значение округляется (как в Visual Basic) или усекается (как в C#). Для преобразований Decimal в Single значения и Double значение может не иметь точного представления в целевом типе данных.
В следующем примере число значений преобразуется Double в несколько других числовых типов. преобразования выполняются в проверяемом контексте в Visual Basic (по умолчанию) и в C# (из-за ключевого слова checked ). Выходные данные в примере показывают результат для преобразований в проверяемом непроверяемом контексте. вы можете выполнять преобразования в непроверенном контексте в Visual Basic путем компиляции с помощью /removeintchecks+ параметра компилятора и в C#, закомментируя checked инструкцию.
Floating-Point функциональности
DoubleСтруктура и связанные типы предоставляют методы для выполнения операций в следующих областях:
IsNaN IsInfinity IsPositiveInfinity IsNegativeInfinity Для проверки этих специальных значений можно также вызвать методы,, и.
Можно также манипулировать отдельными битами Double значения. BitConverter.DoubleToInt64BitsМетод сохраняет Double битовый шаблон значения в 64-разрядном целом число. BitConverter.GetBytes(Double)Метод возвращает свой битовый шаблон в массиве байтов.
Округление. Округление часто используется как метод снижения влияния различий между значениями, вызванными проблемами представления и точности с плавающей запятой. Можно округлить Double значение, вызвав Math.Round метод.
Однако преобразование Int64 Single значений и может привести к утрате точности. В следующей таблице перечислены различия в точности для каждого из этих типов:
| Type | Максимальная точность | Внутренняя точность |
|---|---|---|
| Double | 15 | 17 |
| Int64 | 19 десятичных знаков | 19 десятичных знаков |
| Single | 7 десятичных знаков | 9 десятичных знаков |
Представляет наименьшее положительное значение Double больше нуля. Это поле является константой.
Представляет наибольшее возможное значение типа Double. Это поле является константой.
Представляет минимально допустимое значение типа Double. Это поле является константой.
Представляет значение, не являющееся числом ( NaN ). Это поле является константой.
Представляет минус бесконечность. Это поле является константой.
Представляет плюс бесконечность. Это поле является константой.
Методы
Сравнивает данный экземпляр с заданным числом двойной точности с плавающей запятой и возвращает целое число, которое показывает, является ли значение данного экземпляра меньше, больше или равно значению заданного числа двойной точности с плавающей запятой.
Сравнивает данный экземпляр с указанным объектом и возвращает целое число, которое показывает, является ли значение данного экземпляра меньше, больше или равно значению заданного объекта.
Возвращает значение, позволяющее определить, представляют ли этот экземпляр и заданный объект Double одно и то же значение.
Возвращает значение, показывающее, равен ли данный экземпляр заданному объекту.
Возвращает хэш-код данного экземпляра.
Возвращает TypeCode для типа значения Double.
Определяет, является ли указанное значение конечным (нулевым, поднормальным или нормальным).
Возвращает значение, позволяющее определить, равно ли данное число плюс или минус бесконечности.
Возвращает значение, показывающее, что указанное значение не является числом (NaN).
Определяет, является ли заданное значение отрицательным.
Возвращает значение, позволяющее определить, равно ли данное число минус бесконечности.
Определяет, является ли заданное значение нормальным.
Возвращает значение, показывающее, равно ли данное число плюс бесконечности.
Определяет, является ли заданное значение поднормальным.
Преобразует диапазон символов, содержащий строковое представление числа в указанном стиле и с использованием формата, соответствующего данному языку и региональным параметрам, в эквивалентное ему число с плавающей запятой двойной точности.
Преобразует строковое представление числа в эквивалентное ему число двойной точности с плавающей запятой.
Преобразует строковое представление числа, выраженное в заданном формате, связанном с языком и региональными параметрами, в эквивалентное ему число двойной точности с плавающей запятой.
Преобразует строковое представление числа указанного стиля в эквивалентное ему число двойной точности с плавающей запятой.
Преобразует строковое представление числа указанного стиля, выраженное в формате, соответствующем определенному языку и региональным параметрам, в эквивалентное ему число двойной точности с плавающей запятой.
Преобразует числовое значение данного экземпляра в эквивалентное ему строковое представление.
Преобразует числовое значение данного экземпляра в эквивалентное ему строковое представление с использованием указанных сведений об особенностях форматирования для данного языка и региональных параметров.
Преобразует числовое значение данного экземпляра в эквивалентное строковое представление с использованием указанного формата.
Преобразует числовое значение данного экземпляра в эквивалентное ему строковое представление с использованием указанного формата и сведений об особенностях форматирования для данного языка и региональных параметров.
Пытается отформатировать значение текущего двойного экземпляра в указанный диапазон символов.
Преобразует представление диапазона числа указанного стиля, выраженное в формате, соответствующем определенному языку и региональным параметрам, в эквивалентное ему число двойной точности с плавающей запятой. Возвращает значение, указывающее, успешно ли выполнено преобразование.
Преобразует диапазон символов, содержащий строковое представление числа в указанном стиле и с использованием формата, соответствующего данному языку и региональным параметрам, в эквивалентное ему число с плавающей запятой двойной точности. Возвращает значение, указывающее, успешно ли выполнено преобразование.
Преобразует строковое представление числа в эквивалентное ему число двойной точности с плавающей запятой. Возвращает значение, указывающее, успешно ли выполнено преобразование.
Преобразует строковое представление числа указанного стиля, выраженное в формате, соответствующем определенному языку и региональным параметрам, в эквивалентное ему число двойной точности с плавающей запятой. Возвращает значение, указывающее, успешно ли выполнено преобразование.
Операторы
Возвращает значение, указывающее, равны ли два заданных значения Double.
Возвращает значение, указывающее, действительно ли заданное значение Double больше другого заданного значения Double.
Возвращает значение, указывающее, действительно ли заданное значение Double больше или равно другому заданному значению Double.
Возвращает значение, указывающее, не равны ли два заданных значения Double.
Возвращает значение, указывающее, действительно ли заданное значение Double меньше другого заданного значения Double.
Возвращает значение, указывающее, действительно ли заданное значение Double меньше или равно другому заданному значению Double.
Явные реализации интерфейса
Сравнивает текущий экземпляр с другим объектом того же типа и возвращает целое число, которое показывает, расположен ли текущий экземпляр перед, после или на той же позиции в порядке сортировки, что и другой объект.
Возвращает TypeCode для этого экземпляра.
Описание этого члена см. в разделе ToBoolean(IFormatProvider).
Описание этого члена см. в разделе ToByte(IFormatProvider).
Это преобразование не поддерживается. При попытке использовать этот метод выбрасывается исключение InvalidCastException.
Это преобразование не поддерживается. При попытке использовать этот метод выбрасывается исключение InvalidCastException.
Описание этого члена см. в разделе ToDecimal(IFormatProvider).
Описание этого члена см. в разделе ToDouble(IFormatProvider).
Описание этого члена см. в разделе ToInt16(IFormatProvider).
Описание этого члена см. в разделе ToInt32(IFormatProvider).
Описание этого члена см. в разделе ToInt64(IFormatProvider).
Описание этого члена см. в разделе ToSByte(IFormatProvider).
Описание этого члена см. в разделе ToSingle(IFormatProvider).
Описание этого члена см. в разделе ToType(Type, IFormatProvider).
Описание этого члена см. в разделе ToUInt16(IFormatProvider).
Описание этого члена см. в разделе ToUInt32(IFormatProvider).
Описание этого члена см. в разделе ToUInt64(IFormatProvider).
Применяется к
Потокобезопасность
Все члены этого типа являются потокобезопасными. Члены, которые могут изменить состояние экземпляра, в действительности возвращают новый экземпляр, инициализированный новым значением. Как с любым другим типом, чтение и запись общей переменной, которая содержит экземпляр этого типа, должны быть защищены блокировкой для обеспечения потокобезопасности.
1. Тип double
В вещественных числах дробная часть записывается после точки. Например, 123.456, или 2.5, или 100.00 или 0.01. Такие числа еще называют числами с плавающей точкой — floating point number – компьютерное название для вещественных чисел.
2. Создание переменной типа double
Тип double используется для хранения вещественных чисел. Чтобы создать в коде переменную, которая будет способна хранить вещественные числа, нужно воспользоваться командой:
Где имя — это имя переменной. Примеры:
| Команда | Описание |
|---|---|
| Создается вещественная переменная price | |
| Создается вещественная переменная weight | |
| Создается вещественная переменная lightSpeed |
И даже сразу присваивать им значения:
| Команда | Примечание |
|---|---|
| В переменной хранится значение 5.0 | |
| В переменной хранится значение 2.0 |
3. Присваивание целых и вещественных чисел
Во-первых, переменным типа double можно присваивать как вещественные, так и целые числа. При присваивании целых чисел они просто преобразовываются в вещественные. Хотя иногда при этом возможна небольшая потеря точности.
| Команда | Примечание |
|---|---|
| В переменной хранится значение 5.0 | |
| В переменной хранится значение 2.0 | |
| В переменной x хранится значение 1000000.0 |
Во-вторых, если в каком-то выражении участвуют целое и вещественное число, целое сначала преобразуется в вещественное и только потом взаимодействует с другим вещественным числом.
| Команда | Примечание |
|---|---|
| В переменной x хранится значение 5000.0 | |
| На экран будет выведено число 10 | |
| На экран будет выведено число 10.0 |
И наконец, есть возможность присваивать переменным типа int вещественные числа. Дробная часть числа при этом отбрасывается — число округляется вниз до целого.
Также компилятор требует, чтобы этот факт программист задокументировал явно (чтобы другие программисты понимали, что тут происходит отбрасывание дробной части). Общий вид этого выражения в коде такой:
Double что за тип данных
Плавающая запятая даёт много преимуществ, но « заплыв» этот полон моментов, требующих понимания и тщательного подхода. Числа с плавающей запятой используются слишком часто, чтобы пренебрегать их глубоким пониманием. Поэтому нужно хорошо разбираться, как представлены числа с плавающей запятой и как работают вычисления с ними.
Экскурс
Число с плавающей запятой представлено в следующем виде:
$$N = [s] \times m \times 2^
где \(m\) — мантисса ( 23 бита — дробная часть мантиссы и неявно заданный ведущий бит, всегда равный единице, поскольку мантисса хранится в нормализованном виде), \(e\) — смещённая экспонента/порядок ( 8 бит). Один бит отводится под знак ( \(s\), бит равен нулю — число положительное, единице — отрицательное).
Рассмотрим какой-нибудь пример. Число \(3<,>5\) будет представлено в следующем виде:
Бит знака равен нулю, это говорит о том, что число положительное.
Смещённая экспонента равна \(128_<10>\) ( \(10000000_<2>\)), смещена она на \(127\), следовательно, в действительности получаем:
Нормализованная мантисса равна \([1<,>]11000000000000000000000_<2>\), то есть \(1<,>75_<10>\):
$$N = m \times 2^
Инвертируем бит знака :
Теперь число стало отрицательным: \(-3<,>5\).
Рассмотрим несколько исключений. Как представить число \(0\), если мантисса всегда хранится в нормализованном виде, то есть больше либо равна \(1\)? Решение такое — условимся обозначать \(0\), приравнивая все биты смещённого порядка и мантиссы нулю:
Ноль бывает и отрицательным:
Плюс и минус бесконечность обозначаются приравниванием всех битов смещённого порядка единице, а битов мантиссы — нулю:
В некоторых случаях результат не определён, для этого есть метка \(NaN\), что значит not a number ( не число). Приравняем все биты смещённого порядка и мантиссы единице ( по правде говоря, достаточно того, чтобы хотя бы один бит мантиссы равнялся единице):
Более подробно о типах с плавающей запятой можно прочитать в обучающих статьях. Описанный подход к представлению чисел является компромиссом между точностью, диапазоном значений и скоростью выполнения операций с числами с плавающей запятой. Во-первых, число хранится в двоичной форме, но не всякое десятичное число можно представить в виде непериодической дроби в двоичной системе; это одна из причин потери точности ( \(0<,>1_ <10>= 0<,>000(1100)_<2>\)). Во-вторых, значащих цифр ( цифр мантиссы) не так уж и много: 24 двоичных цифры, или 7,2 десятичных, поэтому придётся много округлять. Но есть и менее очевидные моменты.
Нарушение свойства ассоциативности
Вычислим \(sin(x)\), разложив эту функцию в ряд Тейлора:
Напишем небольшую функцию, которая будет реализовывать эти вычисления. Тип float вместо double выбран для того, чтобы показать, как быстро накапливается погрешность; никаких дополнительных действий не производится, код исключительно демонстрационный. Целью не является показать, что семи членов ряда недостаточно, цель — показать нарушение свойства ассоциативности операций:
хотя свойство коммутативности сохраняется:
Теперь напишем аналогичную функцию, но сделаем так, чтобы те же элементы ряда суммировались в обратном порядке.
Посмотрим на это с точки зрения машины:
До этого момента мнение компьютера и человека, казалось бы, сходится. Но результатом в первом случае окажется \(0<,>0000211327\) ( \(2.11327e<->005\)), а во втором случае — \(0<,>0000212193\) ( \(2.12193e<->005\)), совпадают лишь две значащие цифры!
Разгадка проста: у чисел представленного ряда шесть различных ( двоичных) порядков: \(1\), \(2\), \(1\), \(-1\), \(-4\), \(-8\), \(-12\). Когда складываются ( вычитаются) два числа одного порядка или близких порядков, потери точности, как правило, небольшие. Если бы мы складывали огромное число и много маленьких чисел одного порядка, то мы заметили бы, что лучше в плане точности сперва сложить все маленькие числа, а затем уже прибавить большое. Рассмотрим обратный сценарий: сложим большое и первое маленькое число; поскольку порядки значительно различаются, маленькое число будет ( фигурально выражаясь) « раздавлено» большим из-за приведения порядков; получилось новое большое число, не очень точное, но пока ещё достаточно близкое к точному результату; к получившемуся большому числу прибавляем второе маленькое, порядки снова значительно различаются, снова маленькое число оказывается раздавленным, уже две « жертвы». И так далее. Погрешность накопилась достаточно большая.
Сложение ( вычитание) чисел с одинаковым порядком тоже не проходит без округлений, но погрешности, как правило, минимальны.
Большие числа
Множество чисел, представимых с помощью float ( и double ), конечно. Из этого, а также из того, что количество разрядов мантиссы весьма ограничено, сразу же следует, что очень большие числа представить не получится; придётся изобретать пути обхода переполнения. Менее очевидным следствием из способа задания числа в виде, описанном выше, является то, что пространство таких чисел не является равномерным. Что это значит?
Возьмём порядок \(e = 0\) и рассмотрим два стоящих подряд числа \(N_<1>\) и \(N_<2>\): у первого мантисса \(m_<1>=[1<,>]00000000000000000000001_<2>\), у второго — \(m_<2>=[1<,>]00000000000000000000010_<2>\).
Воспользуемся конвертером: \(N_<1>=1<,>0000001_<10>\), \(N_ <2>= 1<,>0000002_<10>\). Разница между ними равна \(0<,>0000001_<10>\).
Теперь возьмём порядок \(e = 127\) и снова рассмотрим два стоящих подряд числа: у первого мантисса \(m_<3>=[1<,>]00000000000000000000001_<2>\), у второго — \(m_<4>=[1<,>]00000000000000000000010_<2>\).
\(N_<3>=1<,>701412 \times 10^<38>_<10>\), \(N_ <4>= 1<,>7014122 \times 10^<38>_<10>\). Разница между ними равна \(0<,>0000002 \times 10^<38>_<10>\). В рамках этого типа данных нет никакого способа задать некоторое число \(N\), находящееся в интервале между \(N_<3>\) и \(N_<4>\). На секунду, \(0<,>0000002 \times 10^<38>\) — это 20 нониллионов, иначе говоря, двойка и 31 ноль! Маленький шаг для мантиссы и огромный скачок для всего числа.
Каждый из этих диапазонов разбивается на равное количество интервалов. Следовательно, от \(1<,>0\) до \(1<,>9999999\), от \(16<,>0\) до \(31<,>999998\), от \(1<,>7014118 \times 10^<38>\) до \(3<,>4028235 \times 10^<38>\) находится одинаковое количество доступных значений — \((2^ <23>— 2)\) ( поскольку мантисса, за исключением скрытого бита, имеет \(23\) бита; минус сами границы).
Всё сказанное в равной степени касается отрицательных чисел и чисел с отрицательными порядками.
Заключение
Когда в программе используются числа с плавающей запятой, необходимо обращать внимание на операции сложения и вычитания из-за нарушения свойства ассоциативности вследствие ограниченной точности типа float ( и double ). Особенно в том случае, когда порядки чисел сильно различаются. Другой проблемой является большой шаг между ближайшими представимыми числами больши́х порядков. Что и было показано.
Здесь были освещены только эти два аспекта. Также следует помнить о том, что нельзя непосредственно сравнивать два числа с плавающей запятой ( if (x == y) ); что у диапазона есть ограничения; что следует использовать логарифм, когда происходят вычисления с использованием огромных чисел. Ну и совсем не следует забывать о бесконечностях и метке \(NaN\). Из экзотики — флаги компилятора, касающиеся точности промежуточных результатов, из простых вещей ( хотя и не менее коварных, чем что-либо упомянутое выше) — особенности приведения типов. В общем, в выражении « вагон и маленькая тележка» эта заметка — даже не тележка, а маленькая горстка. Маленькая, но достаточно важная.
И последнее. Когда требуется точность вычислений ( например, при финансовых расчётах), следует использовать собственные или сторонние классы чисел с фиксированной запятой. Если речь идёт о языке, поддерживающем такие типы, следует использовать их. Таковым, например, является тип decimal в языке C♯.




















