Является ли точность с плавающей запятой изменяемой или инвариантной?

Я продолжаю получать смешанные ответы о том, имеют ли число с плавающей запятой (т.е. float, double или long double) одно и только одно значение точности или имеют значение точности, которое может меняться.

Одна тема под названием float vs. double precision, по-видимому, означает, что точность с плавающей запятой является абсолютной.

Однако другой вопрос под названием Разница между float и double говорит:

В общем случае double имеет от 15 до 16 десятичных цифр точности

Другой источник говорит,

Переменные типа float обычно имеют точность около 7 значащих цифр

Переменные типа double обычно имеют точность около 16 значащих цифр

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

Ответ 1

Исправлена ​​точность, которая ровно 53 двоичных разряда для двойной точности (или 52, если исключить неявное начало 1). Это означает около 15 десятичных цифр.


ОП попросил меня рассказать о том, почему именно 53 двоичных цифры означают "около" 15 десятичных цифр.

Чтобы понять это интуитивно, рассмотрим менее точный формат с плавающей запятой: вместо 52-битной мантиссы, такой как числа с двойной точностью, мы просто используем 4-битную мантиссу.

Итак, каждый номер будет выглядеть так: (-1) s & times; 2 yyy & times; 1.xxxx(где s - знаковый бит, yyy - показатель степени, а 1.xxxx - нормированная мантисса). Для немедленного обсуждения мы сосредоточимся только на мантиссе, а не на знаке или экспоненте.

Здесь таблица того, что 1.xxxx выглядит как для всех значений xxxx (все округление равно половине по порядку, так же, как работает режим округления с плавающей запятой по умолчанию):

  xxxx  |  1.xxxx  |  value   |  2dd  |  3dd  
--------+----------+----------+-------+--------
  0000  |  1.0000  |  1.0     |  1.0  |  1.00
  0001  |  1.0001  |  1.0625  |  1.1  |  1.06
  0010  |  1.0010  |  1.125   |  1.1  |  1.12
  0011  |  1.0011  |  1.1875  |  1.2  |  1.19
  0100  |  1.0100  |  1.25    |  1.2  |  1.25
  0101  |  1.0101  |  1.3125  |  1.3  |  1.31
  0110  |  1.0110  |  1.375   |  1.4  |  1.38
  0111  |  1.0111  |  1.4375  |  1.4  |  1.44
  1000  |  1.1000  |  1.5     |  1.5  |  1.50
  1001  |  1.1001  |  1.5625  |  1.6  |  1.56
  1010  |  1.1010  |  1.625   |  1.6  |  1.62
  1011  |  1.1011  |  1.6875  |  1.7  |  1.69
  1100  |  1.1100  |  1.75    |  1.8  |  1.75
  1101  |  1.1101  |  1.8125  |  1.8  |  1.81
  1110  |  1.1110  |  1.875   |  1.9  |  1.88
  1111  |  1.1111  |  1.9375  |  1.9  |  1.94

Сколько десятичных цифр вы указываете? Вы могли бы сказать 2, что каждое значение в двухдисковом значении диапазона покрывается, хотя и не однозначно; или вы можете сказать, что 3, который охватывает все уникальные значения, но не обеспечивает охват для всех значений в диапазоне с тремя десятичными знаками.

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


Итак, тогда, что произойдет, если мы вдвое сократим все числа (поэтому мы используем yyy= -1)?

  xxxx  |  1.xxxx  |  value    |  1dd  |  2dd  
--------+----------+-----------+-------+--------
  0000  |  1.0000  |  0.5      |  0.5  |  0.50
  0001  |  1.0001  |  0.53125  |  0.5  |  0.53
  0010  |  1.0010  |  0.5625   |  0.6  |  0.56
  0011  |  1.0011  |  0.59375  |  0.6  |  0.59
  0100  |  1.0100  |  0.625    |  0.6  |  0.62
  0101  |  1.0101  |  0.65625  |  0.7  |  0.66
  0110  |  1.0110  |  0.6875   |  0.7  |  0.69
  0111  |  1.0111  |  0.71875  |  0.7  |  0.72
  1000  |  1.1000  |  0.75     |  0.8  |  0.75
  1001  |  1.1001  |  0.78125  |  0.8  |  0.78
  1010  |  1.1010  |  0.8125   |  0.8  |  0.81
  1011  |  1.1011  |  0.84375  |  0.8  |  0.84
  1100  |  1.1100  |  0.875    |  0.9  |  0.88
  1101  |  1.1101  |  0.90625  |  0.9  |  0.91
  1110  |  1.1110  |  0.9375   |  0.9  |  0.94
  1111  |  1.1111  |  0.96875  |  1.   |  0.97

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

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

Ответ 2

Все современные компьютеры используют двоичную арифметику с плавающей запятой. Это означает, что у нас есть двоичная мантисса, которая обычно имеет 24 бита для одиночной точности, 53 бит для двойной точности и 64 бит для расширенной точности. (Расширенная точность доступна для процессоров x86, но не для ARM или, возможно, для других типов процессоров.)

24, 53 и 64-битные мантиссы означают, что для числа с плавающей запятой между 2 k и 2 k + 1 следующее большее число равно 2 k-23 2 k-52 и 2 k-63 соответственно. Это резолюция. Ошибка округления для каждой операции с плавающей запятой составляет не более половины.

Итак, как это переводится в десятичные числа? Это зависит.

Возьмем k = 0 и 1 ≤ x < 2. Разрешение составляет 2 -23 2 -52 и 2 -63 что составляет около 1,19 раз и 10 -7 2.2 & times; 10 -16 и 1.08 & times; 10 -19 соответственно. Это немного меньше 7, 16 и 19 десятичных знаков. Тогда возьмем k = 3 и 8 ≤ x < 16. Разница между двумя числами с плавающей запятой теперь в 8 раз больше. Для 8 ≤ x < 10 вы получаете чуть больше 6, меньше 15 и чуть более 18 десятичных знаков соответственно. Но при 10 ≤ x < 16 вы получите еще одно десятичное число!

Наибольшее количество десятичных цифр вы получаете, если x только немного меньше 2 k + 1 и только чуть больше 10 n например 1000 ≤ x < 1024. Вы получаете наименьшее число десятичных цифр, если x немного больше 2 k и бит меньше 10 n например 1 & frasl; 1024 ≤ x < 1 & frasl; 1000. Одна и та же двоичная точность может давать десятичную точность, которая изменяется до 1,3 цифр или log 10 (2 & times; 10).

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

Ответ 3

Код 80x86 с использованием его аппаратного сопроцессора (первоначально 8087) обеспечивает три уровня точности: 32-разрядный, 64-разрядный и 80-разрядный. Они очень внимательно следят за стандартом IEEE-754 от 1985 года. В последнем стандарте указывается 128-битный формат. Форматы с плавающей запятой имеют биты 24, 53, 65 и 113 мантисса, которые соответствуют точности 7.22, 15.95, 19.57 и 34.02.

Формула: mantissa_bits/log_2 10, где логарифмическая база двух из десяти составляет 3.321928095.

Хотя точность какой-либо конкретной реализации не меняется, может показаться, когда значение с плавающей запятой преобразуется в десятичное. Обратите внимание, что значение 0.1 не имеет точного двоичного представления. Это повторяющаяся битовая диаграмма (0.0001100110011001100110011001100...), которую мы используем в десятичной системе для 0,33333333333333, чтобы приблизиться к 1/3.

Многие языки часто не поддерживают 80-битный формат. Некоторые компиляторы C могут предлагать long double, который использует либо 80-битные поплавки, либо 128-битные поплавки. Увы, он может также использовать 64-битный float, в зависимости от реализации.

NPU имеет 80-битные регистры и выполняет все операции с использованием всего 80-битного результата. Код, который рассчитывается в стеке NPU, выигрывает от этой дополнительной точности. К сожалению, плохое генерирование кода или плохо написанный код -— могут обрезать или округлить промежуточные вычисления, сохранив их в 32-битной или 64-битной переменной.

Ответ 4

Является ли точность с плавающей запятой изменяемой или инвариантной, и почему?

Как правило, при любых числах в одном диапазоне мощности-2 точность с плавающей запятой инвариантна - фиксированное значение. Абсолютная точность изменяется с каждым шагом 2-го уровня. Во всем диапазоне FP точность приблизительно соответствует величине. Относительно этой относительной бинарной точности в терминах десятичной точности происходит колебание, изменяющееся между DBL_DIG и DBL_DECIMAL_DIG десятичными цифрами - обычно от 15 до 17.


Что такое точность? С FP имеет смысл обсуждать относительную точность.

Числа с плавающей запятой имеют вид:

Sign * Significand * pow (base, exponent)

Они имеют логарифмическое распределение. Существует приблизительно столько разных чисел с плавающей запятой между 100.0 и 3000.0 (диапазон 30x), как между 2.0 и 60.0. Это верно независимо от основного представления хранилища.

1.23456789e100 имеет примерно такую ​​же относительную точность, как 1.23456789e-100.


Большинство компьютеров реализуют double как binary64. Этот формат имеет 53 бит бинарной точности.

Число n между 1.0 и 2.0 имеет ту же абсолютную точность 1 часть в ((2.0-1.0)/pow (2,52).
Числа между 64.0 и 128.0, также n, имеют одинаковую абсолютную точность 1 часть в ((128.0-64.0)/pow (2,52).

Четная группа чисел между степенями 2, имеет одинаковую абсолютную точность.

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

Когда эти числа представлены как десятичные, точность wobbles: Numbers 1.0 to 2.0 имеют еще 1 бит абсолютной точности, чем числа от 2.0 до 4.0. 2 бита, чем от 4,0 до 8,0 и т.д.

C предоставляет DBL_DIG, DBL_DECIMAL_DIG и их сопоставления float и long double. DBL_DIG указывает минимальную относительную десятичную точность. DBL_DECIMAL_DIG можно рассматривать как максимальную относительную десятичную точность.

Обычно это значение, данное double, будет иметь от 15 до 17 десятичных цифр точности.

Рассмотрим 1.0 и следующий его представимый double, цифры не изменяются до 17-й значащей десятичной цифры. Каждый следующий double равен pow(2,-52) или примерно 2.2204e-16.

/*
1 234567890123456789 */
1.000000000000000000...
1.000000000000000222...

Теперь рассмотрим "8.521812787393891" и его следующее представимое число как десятичную строку, используя 16 значащих десятичных цифр. Обе эти строки, преобразованные в double, одинаковы 8.521812787393891142073699..., даже если они отличаются 16-й цифрой. Сказав, что этот double имел 16 цифр точности, было указано выше.

/*
1 234567890123456789 */
8.521812787393891
8.521812787393891142073699...
8.521812787393892

Ответ 5

Нет, это переменная. Отправной точкой является очень слабый стандарт IEEE-754, он только прибивает формат чисел с плавающим указателем, поскольку они хранятся в памяти. Вы можете рассчитывать на 7 цифр точности для одинарной точности, 15 цифр для двойной точности.

Но основным недостатком этого стандарта является то, что он не указывает, как должны выполняться вычисления. И там проблемы, процессор Intel 8087 с плавающей запятой, в частности, вызвал у программистов много бессонных ночей. Существенным недостатком дизайна в этом чипе является то, что он сохраняет значения с плавающей запятой с большим количеством бит, чем формат памяти. 80 бит вместо 32 или 64. Теория, лежащая в основе этого выбора дизайна, заключается в том, что это позволяет быть более точными промежуточными вычислениями и вызывать меньше ошибок округления.

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

Intel переработала свой процессор для решения этой проблемы, набор инструкций SSE вычисляет с таким же количеством бит, что и формат памяти. Тем не менее, медленный, чтобы поймать, перепроектирование генератора кода и оптимизатора компилятора является значительным капиталовложением. Все три компилятора С++ сгруппированы. Но, например, джиттер x86 в .NET Framework по-прежнему генерирует код FPU, он всегда будет.


Тогда возникает системная ошибка, теряющая точность как неизбежный побочный эффект преобразования и вычисления. Сначала конверсия, люди работают в цифрах в базе 10, но процессор использует базу 2. Хорошие круглые числа, которые мы используем, например, 0,1 не могут быть преобразованы в красивые круглые числа на процессоре. 0,1 является совершенным как сумма степеней 10, но нет конечной суммы степеней 2, которые производят одно и то же значение. Конвертируя его, вы получаете бесконечное количество 1s и 0s таким же образом, что вы не можете отлично записать 10/3. Поэтому его нужно усечь, чтобы он соответствовал процессору, и который генерирует значение, равное +/- 0,5 бит от десятичное значение.

И вычисление вызывает ошибку. Умножение или деление удваивает количество бит в результате, округляя его, чтобы вернуть его обратно в сохраненное значение, выдает ошибку +/- 0,5 бит. Вычитание является наиболее опасной операцией и может привести к потере значительных цифр. Если вы, скажем, вычислите 1.234567f - 1.234566f, тогда результат останется только 1 значащая цифра. Это результат мусора. Суммирование разницы между числами, имеющими почти одно и то же значение, очень часто встречается в числовых алгоритмах.

Получение чрезмерных системных ошибок в конечном счете является недостатком в математической модели. Как пример, вы никогда не хотите использовать исключение Гаусса, это очень недружелюбно к точности. И всегда рассматривайте альтернативный подход, LU Decomposition - отличный подход. Однако не так уж и важно, что математик участвовал в построении модели и учитывал ожидаемую точность результата. Обычная книга, как Numerical Recipes, также не уделяет достаточного внимания точности, хотя она косвенно отводит вас от плохих моделей, предлагая лучшую. В конце концов, программист часто сталкивается с проблемой. Ну, это было легко, тогда кто-то мог это сделать, и я был бы неплохо оплачиваемым:)

Ответ 6

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

Второе: Еще одна проблема заключается в выполнении арифметических операций точности. Просто подумайте о 1.0/3.0 или PI. Такие значения не могут быть представлены с ограниченным числом цифр - ни десятичными, ни двоичными. Поэтому значения должны округляться, чтобы вписаться в заданное пространство. Чем больше дробных цифр доступно, тем выше точность.

Теперь подумайте о применении нескольких таких операций, например. PI/3.0. Это потребовало бы округлить в два раза: PI как таковой не является точным, и результат тоже. Это уменьшит точность в два раза, если будет обработано, это ухудшится.

Итак, вернемся к float и double: float в соответствии со стандартом (C11, Приложение F, а также для остальных) меньше доступных бит, поэтому округлость будет менее точной, чем для double. Просто подумайте о десятичном значении с двумя дробными цифрами (m.ff, назовите его float) и одним с четырьмя (m.ffff, назовите его double). Если для всех вычислений используется double, вы можете иметь больше операций, пока ваш результат не будет иметь только 2 правильных дробных числа, чем если бы вы уже начали с float, даже если результат поплавка был бы достаточным.

Обратите внимание, что на некоторых (встроенных) процессорах, таких как ARM Cortex-M4F, аппаратное FPU поддерживает только фолат (одинарная точность), поэтому двойная арифметика будет намного более дорогостоящей. Другие MCU не имеют аппаратного калькулятора с плавающей запятой, поэтому ими нужно моделировать мое программное обеспечение (очень дорого). На большинстве графических процессоров плавать также намного дешевле, чем вдвое, иногда более чем на 10 раз.

Ответ 7

В хранилище имеется точный счетчик цифр в двоичном формате, как объясняют другие ответы.

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

void Kaboom( float a, float b, float c ) // same is true for other floating point types.
{
    float sum1 = a+b+c;
    float sum2 = a+b;
    sum2 += c; // let assume that the compiler did not keep sum2 in a register and the value was write to memory then load again.
    if (sum1 !=sum2)
        throw "kaboom"; // this can happen.
}

Это более вероятно с более сложным вычислением.

Ответ 8

Я собираюсь добавить ответ off-beat здесь и сказать, что, поскольку вы отметили этот вопрос как С++, нет никакой гарантии точности данных с плавающей запятой. Подавляющее большинство реализаций используют IEEE-754 при реализации их типов с плавающей точкой, но это не требуется. Единственное, что требуется на языке С++, это то, что (С++ spec §3.9.1.8):

Существует три типа точек плавания: плавающий, двойной и длинный. Тип double обеспечивает как минимум такую ​​же точность, как float, а double double double обеспечивает как минимум такую ​​же точность, как double. Набор значений типа float является подмножеством набора значений типа double; набор значений типа double является подмножеством набора значений типа long double. Представление значений типов флаговых точек определяется с помощью реализации. Интегральные и гибкие типы коллективно называются арифметическими типами. Специализации стандартного шаблона std:: numeric_limits (18.3) должны указывать максимальное и минимальное значения каждого арифметического типа для реализации.

Ответ 9

Объем пространства, необходимый для хранения float, будет постоянным, а также a double; однако количество полезной точности в относительном выражении обычно изменяется между одной частью в 2 23 и одной частью в 2 24 для float или одной частью в 2 52 и 2 53 для double. Точность очень близка к нулю не так хороша, причем второе наименьшее положительное значение в два раза больше, чем самое маленькое, что в свою очередь будет бесконечно большим нуля. Тем не менее, в большинстве диапазонов точность будет отличаться, как описано выше.

Обратите внимание, что, хотя часто бывает нецелесообразно иметь типы, относительная точность которых колеблется менее чем в два раза по всему диапазону, изменение точности может иногда приводить к расчетам с гораздо менее точными вычислениями, чем казалось бы, что они должны, Рассмотрим, например, 16777215.0f + 4.0f - 4.0f. Все значения были бы точно представлены как float с использованием одного и того же масштаба, а ближайшие значения к большому - +/- одна часть в 16,777,215, но первое сложение дает результат в части диапазона float, где значения разделяются одной частью всего лишь 8 388 610, в результате чего результат округляется до 16 777 220. Следовательно, вычитание 4 дает 16777,216, а не 16 777 215. Для большинства значений float около 16777216 добавление 4.0f и вычитание 4.0f приведет к тому, что исходное значение не изменится, но изменение точности прямо в точке прерывания приведет к отключению результата дополнительным битом в самое низкое место.

Ответ 10

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

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