В первые же дни изучения Java я наткнулся на такой любопытный вид примитивов, как числа с плавающей точкой. Меня сразу заинтересовали их особенности и, тем более, способ записи в двоичном коде (что взаимосвязано). В отличие от какого-либо диапазона целых чисел, даже в очень малом промежутке (например от 1 до 2) их бесконечное множество. И имея конечный размер памяти, невозможно выразить это множество. Так как же они выражены в двоичном коде и как работают? Увы, объяснения в вики и достаточно клёвой статьи на хабре тут не дали мне полного понимания, хотя заложили базу. Осознание пришло лишь после этой статьи-разбора наутро после прочтения.
(почерпнул из этой статьи на Хабре
Ещё в учебниках школьного курса все сталкивались с непривычным способом записи очень больших или очень малых чисел вида 1,2×103 или 1,2E3, что равно 1,2×1000 = 1200. Это называется способ записи через экспоненту. В данном случае мы имеем дело с выражением числа по формуле: N=M×np, где
Довольно часто основание порядка подразумевается, как 10 и записывают лишь мантиссу и значение степени основания, разделяя их буквой E. В нашем примере я привёл равнозначные записи 1,2×103 и 1,2E3 Если всё понятно, и ностальгический экскурс в школьную программу мы закончили, то сейчас рекомендую забыть это, т. к. при формировании числа с плавающей точкой мы имеем дело со степенями двойки, а не десятки, т.е.
И что мы имеем? В итоге мы также имеем двоичное число, которое состоит из мантиссы — часть, которую будем возводить в степень и саму степень. Кроме этого, так же как принято и у целочисленных типов, в числах с плавающей точкой есть бит, который определяет знак — будет число положительным или отрицательным. В качестве примера предлагаю рассмотреть тип float
, который состоит из 32 бит. С числами двойной точности double
логика такая же, только в два раза больше бит.
Из 32 бит, первый старший отводится на знак, следующие 8 бит отводятся на экспоненту — степень, на которую будем возводить мантиссу, а остальные 23 бита — на мантиссу.
Для демонстрации давайте посмотрим на пример пример:
С первым битом всё очень просто. Если значение первого бита 0, то число, которое мы получим будет положительным. Если бит равен 1, то число будет отрицательным.
Следующий блок из 8 бит — блок с экспонентой.
Экспонента записывается как обычное восьмибитное число, а чтоб получить требуемую степень нам нужно из полученного числа вычесть 127 В нашем случае восемь бит экспоненты — это 10000001. Это соответствует числу 129. Если есть вопрос, как это посчитать, то на картинке быстрый ответ. Развёрнутый можно получить на любом курсе булевой алгебры. 1×27 + 0×26 + 0×25 + 0×24 + 0×23 + 0×22 + 0×21 + 1×2
Теперь о мантиссе. Она состоит из 23 бит, однако в начале всегда подразумевается ещё одна единица, на которую биты не выделяются.
Это сделано в целях целесообразности и экономии. Одно и то же число можно выражать разными степенями, добавляя к мантиссе нули перед или после запятой.
Проще всего это понять с десятичной экспонентой: 120 000 = 1,2×105 = 0,12×106 = 0,012×107 = 0,0012×108 и т.д.
Однако, введя фиксированное число в голове мантиссы мы каждый раз будем получать новые числа. Примем как данность, что перед нашими 23 битами будет ещё один с единицей. Обычно этот бит от остальных отделают точкой, которая, впрочем, ничего не значит. Просто так удобнее
1 . 11100000000000000000000
Теперь полученную мантиссу нужно возводить в степень слева направо, уменьшая с каждым шагом степень на одну. Стартуем со значения степени, который мы получили в результате вычисления, т. е. 2 (Я специально выбрал простой пример, чтоб не писать каждое значение степени двойки и в приведенной таблице не вычислял их, когда соответствующий бит равен нулю)
Стандартное число с плавающей точкой типа float
состоит из 32 бит, первый бит — знак (+ или -), следующие восемь — экспонента, следующие 23 — мантисса
По знаку — если бит 0 — число положительное. Если бит 1 — отрицательное. По экспоненте — побитно переводим в десятичное число (первый слева бит — 128, второй — 64, третий — 32, четвёртый — 16, пятый — 8, шестой — 4, седьмой — 2, восьмой — 1
Число с плавающей запятой состоит из:
Нормальной формой числа с плавающей запятой называется такая форма, в которой мантисса (без учёта знака) находится на полуинтервале [0; 1).
В вычислительных машинах показатель степени принято отделять от мантиссы буквой «E» (exponent). Например, число 1,528535047 × 10 -25 в большинстве языков программирования высокого уровня записывается как 1.528535047E-25.
Существует несколько способов того, как строки из цифр могут представлять числа:
Запись числа в форме с плавающей запятой позволяет производить вычисления над широким диапазоном величин, сочетая фиксированное количество разрядов и точность. Например, в десятичной системе предоставления чисел с плавающей запятой (3 разряда) операцию умножения, которую мы бы записали как
в нормальной форме представляется в виде
В формате с фиксированной запятой мы бы получили вынужденное округление
Мы потеряли крайний правый разряд числа, так как данный формат не позволяет запятой «плавать» по записи числа.
Диапазон чисел, которые можно записать данным способом, зависит от количества бит, отведённых для представления мантиссы и показателя. На обычной 32-битной вычислительной машине, использующей двойную точность (64 бита), мантисса составляет 52 бита + 1 знаковый, показатель — 11 бит. Таким образом получаем диапазон точности примерно от 4,94 × 10−324 до 1.79 × 10308 (от 2−52 × 2−1022 до ~1 × 21024). Пара значений показателя зарезервирована для обеспечения возможности представления специальных чисел. К ним относятся значения бесконечность), получающихся в результате операций типа деления на ноль нуля, положительных и отрицательных чисел. 2\text{kg}/\text{s}\). Если мы изменим только последнюю цифру с \(8\) на \(9{-15}\), в котором по-прежнему 7 цифр, но только 21 десятичный знак.
Можно легко спутать термины точность и точность , особенно если посмотреть на результат вычисления на компьютере. Точность числа с плавающей запятой всегда равна \(d\) двоичным разрядам, но не все эти разряды могут точно представлять предполагаемое значение.
Предположим, что \(x\) представляет интерес, а \(\tilde{x}\) является его приближением. Абсолютная точность из \(\tilde{x}\) равно
\[|\tilde{x} — x|,\]
, а относительная точность равна
\[\frac{|\tilde{x} — x|}{|x|},\]
Абсолютная точность имеет те же единицы измерения, что и \(x\), а относительная точность безразмерна. Мы также можем выразить относительную точность как
(5)\[\text{точные цифры} = -\log_{10} \left| \frac{\tilde{x}-x}{x} \right|.\]
Мы часто округляем это число до целого числа, но имеет смысл говорить о «почти семизначном» или «десяти с половиной цифрах». цифры». 9{-1022}\), что является наименьшим положительным числом двойной точности. Первое связано с относительной точностью, а второе — с абсолютной точностью. Чтобы приблизиться к нулю, всегда требуется сдвиг мышления в сторону абсолютной точности, потому что любая конечная ошибка бесконечна по отношению к нулю.
Следует отметить еще одно значение двойной точности: NaN, что означает Not a Number . Это результат неопределенной арифметической операции, такой как 0/0.
Компьютерная арифметика выполняется над числами с плавающей запятой и возвращает результаты с плавающей запятой. Мы предполагаем существование машинно-аналоговых операций для вещественных функций, таких как \(+\), \(-\), \(\times\), \(/\), \(\sqrt{\quad}\), и так далее. Не вдаваясь в подробности, предположим, что каждая элементарная машинная операция создает результат с плавающей запятой, относительная погрешность которого ограничена \(\macheps\). Например, если \(x\) и \(y\) находятся в \(\float\), то для машинного сложения \(\oplus\) мы имеем оценку 9{-16}\), что очень мало — не только в повседневном исчислении, но и по отношению к операндам, которые по модулю близки к 1. С другой стороны, разница столь же велика, как и сам точный результат! Мы формализуем и обобщим это наблюдение в следующем разделе. В то же время имейте в виду, что точность вычислений с плавающей запятой не может считаться само собой разумеющейся. Даже в идеале мы не должны ожидать, что два математически эквивалентных результата будут равны, а только то, что они будут относительно близки друг к другу.
✍ Рассмотрим множество с плавающей запятой \(\float\), определяемое (1) и (2) с \(d=4\).
(a) Сколько элементов \(\float\) есть в действительном интервале \([1/2,4]\), включая конечные точки?
(b) Какой элемент \(\float\) ближе всего к действительному числу \(1/10\)?
(c) Какое наименьшее натуральное число не входит в \(\float\)?
✍ Докажите, что (3) эквивалентно (4). (Это означает сначала показать, что из (3) следует (4), а затем отдельно, что из (4) следует (3).)
⌨ Существуют гораздо лучшие рациональные приближения к \(\pi\), чем \(22/7\). Для каждого из приведенных ниже найдите его абсолютную и относительную точность и (округлив до целого числа) количество точных цифр. (В Julia переменная pi
по умолчанию настроена на приближение с плавающей запятой к \(\pi\).)
(а) 355/113
(б) 103638/32989
✍ IEEE 754 одинарная точность указывает, что 23 двоичных бита используются для мантиссы \(f\) в (2). Поскольку они требуют меньше места для хранения и могут работать быстрее, чем значения двойной точности, значения одинарной точности могут быть полезны в приложениях с низкой точностью.
(a) В десятичной системе счисления какое первое число одинарной точности больше \(1\) в этой системе?
(b) Какое наименьшее положительное целое число не является числом одинарной точности?
⌨ Джулия определяет функцию nextfloat
, которая возвращает следующее большее значение заданного числа с плавающей запятой. 2}{2n}=1.\] 94\):
r = randn(10000) # рисуем случайные числа х = сумма (знак (z) для z в r)
Выполнить миллион случайных обходов, вычислив средние значения \(x_{10000}\) и \(|x_{10000}|\). Сравните их с \(\alpha_n=0\) и \(\beta_n\приблизительно \sqrt{2n/\pi}\) в \(n=10000\).
Термины «машинный эпсилон», «машинная точность» и «округление единиц» не используются последовательно в ссылках, но для наших целей различия незначительны.
На самом деле, есть несколько еще меньших денормализованных чисел, которые имеют меньшую точность, но мы не будем использовать этот уровень детализации.
ВЕБ-САЙТ FABIEN SANGLARD
29 августа 2017 г.
Визуальное объяснение операций с плавающей запятой
Я закончил с тем, что следует, и я решил включить его в книгу. Я не утверждаю, что это мое изобретение, но я никогда не видел, чтобы плавающие точки объяснялись таким образом. Я надеюсь, что это поможет нескольким людям вроде меня, у которых немного аллергия на математические обозначения.
Как обычно объясняют числа с плавающей запятой
В языке C плавает представляют собой 32-битный контейнер, соответствующий стандарту IEEE 754. Их назначение – хранить и позволять операции по приближению действительных чисел. То, как я видел их объяснение до сих пор, выглядит следующим образом. 32 бита разделены на три части: 9{(E-127)} $$
Как все ненавидят числа с плавающей запятой, когда им объясняют. Обычно я переворачиваю стол. Может быть, у меня аллергия на математическую нотацию, но что-то просто не щелкает, когда я это читаю. Такое ощущение, что учишься
нарисовать сову.
Многие считают арифметику с плавающей запятой эзотерической темой.— Дэвид Голдберг
Другой способ объяснить…
Хотя это правильно, такой способ объяснения плавающей запятой оставляет некоторых из нас совершенно непонятными. невежественный. К счастью, есть другой способ объяснить это. Вместо экспоненты подумайте Окна между двумя последовательными степенями двух целых чисел. Вместо мантиссы подумайте смещения в этом окне. 9{23} = 8388608 $ ведер. С окном и смещением вы можете аппроксимировать число. Окно – отличный механизм для защиты от перелива. Как только вы достигли максимума в окне (например, [2,4]), вы можете «переместить» его вправо и представить число в следующем окне (например, [4,8]). Это стоит лишь немного точности, так как окно становится вдвое больше.
На следующем рисунке показано, как будет закодировано число 6.1. Окно должно начинаться с 4 и простираться до следующей степени двойки, равной 8.