Пользователь Ivan
Ivan
2 уровень
Харьков

Что внутри числа с плавающей точкой и как оно работает

Статья из группы Random

Содержание:

Изображение: http://pikabu.ru/

Вступление

В первые же дни изучения Java я наткнулся на такой любопытный вид примитивов, как числа с плавающей точкой. Меня сразу заинтересовали их особенности и, тем более, способ записи в двоичном коде (что взаимосвязано). В отличие от какого-либо диапазона целых чисел, даже в очень малом промежутке (например от 1 до 2) их бесконечное множество. И имея конечный размер памяти, невозможно выразить это множество. Так как же они выражены в двоичном коде и как работают? Увы, объяснения в вики и достаточно клёвой статьи на хабре тут не дали мне полного понимания, хотя заложили базу. Осознание пришло лишь после этой статьи-разбора наутро после прочтения.

Экскурс в историю

(почерпнул из этой статьи на Хабре) В 60-70 гг, когда компьютеры были большими, а программы — маленькими, ещё не было единого стандарта вычислений, как и стандарта выражения самого числа с плавающей точкой. Каждый компьютер делал это по-своему, и ошибки были у каждого свои. Но в середине 70-х компания Intel решила сделать новые процессоры с поддерживаемой "улучшенной" арифметикой и заодно стандартизировать её. Для разработки привлекли профессоров Уильяма Кэхэна и Джона Палмера (нет, не автора книг про пиво). Не обошлось без драм, но всё же новый стандарт был разработан. Сейчас этот стандарт называют IEEE754

Формат записи числа с плавающей точкой

Ещё в учебниках школьного курса все сталкивались с непривычным способом записи очень больших или очень малых чисел вида 1,2×103 или 1,2E3, что равно 1,2×1000 = 1200. Это называется способ записи через экспоненту. В данном случае мы имеем дело с выражением числа по формуле: N=M×np, где
  • N = 1200 — получаемое число
  • M = 1,2 — мантисса — дробная часть, без учёта порядков
  • n = 10 — основание порядка. В данном случае и когда речь не идёт о компьютерах, основанием выступает цифра 10
  • p = 3 — степень основания
Довольно часто основание порядка подразумевается, как 10 и записывают лишь мантиссу и значение степени основания, разделяя их буквой E. В нашем примере я привёл равнозначные записи 1,2×103 и 1,2E3 Если всё понятно, и ностальгический экскурс в школьную программу мы закончили, то сейчас рекомендую забыть это, т. к. при формировании числа с плавающей точкой мы имеем дело со степенями двойки, а не десятки, т.е. n = 2, вся стройная формула 1,2E3 ломается и это здорово сломало мне мозг.

Знак и степень

И что мы имеем? В итоге мы также имеем двоичное число, которое состоит из мантиссы — часть, которую будем возводить в степень и саму степень. Кроме этого, так же как принято и у целочисленных типов, в числах с плавающей точкой есть бит, который определяет знак — будет число положительным или отрицательным. В качестве примера предлагаю рассмотреть тип float, который состоит из 32 бит. С числами двойной точности double логика такая же, только в два раза больше бит. Из 32 бит, первый старший отводится на знак, следующие 8 бит отводятся на экспоненту — степень, на которую будем возводить мантиссу, а остальные 23 бита — на мантиссу. Для демонстрации давайте посмотрим на пример пример: Что внутри числа с плавающей точкой и как оно работает - 1С первым битом всё очень просто. Если значение первого бита 0, то число, которое мы получим будет положительным. Если бит равен 1, то число будет отрицательным. Следующий блок из 8 бит — блок с экспонентой. Экспонента записывается как обычное восьмибитное число, а чтоб получить требуемую степень нам нужно из полученного числа вычесть 127 В нашем случае восемь бит экспоненты — это 10000001. Это соответствует числу 129. Если есть вопрос, как это посчитать, то на картинке быстрый ответ. Развёрнутый можно получить на любом курсе булевой алгебры. Что внутри числа с плавающей точкой и как оно работает - 21×27 + 0×26 + 0×25 + 0×24 + 0×23 + 0×22 + 0×21 + 1×20 = 1×128 + 1×1 = 128+1=129 Не сложно посчитать, что максимальное число, которое мы можем получить из этих 8 бит 111111112 = 25510 (подстрочные 2 и 10 означают двоичную и десятеричную системы исчисления) Однако, если использовать только положительные значения степени (от 0 и до 255), то полученные числа будут иметь много чисел перед запятой, но не после? Чтоб получать отрицательные значения степени, из сформированной экспоненты нужно вычитать 127. Таким образом, диапазон степеней будет от -127 до 128. Если использовать наш пример, то нужная степень будет 129-127 = 2. Пока запоминаем это число.

Мантисса

Теперь о мантиссе. Она состоит из 23 бит, однако в начале всегда подразумевается ещё одна единица, на которую биты не выделяются. Это сделано в целях целесообразности и экономии. Одно и то же число можно выражать разными степенями, добавляя к мантиссе нули перед или после запятой. Проще всего это понять с десятичной экспонентой: 120 000 = 1,2×105 = 0,12×106 = 0,012×107 = 0,0012×108 и т.д. Однако, введя фиксированное число в голове мантиссы мы каждый раз будем получать новые числа. Примем как данность, что перед нашими 23 битами будет ещё один с единицей. Обычно этот бит от остальных отделают точкой, которая, впрочем, ничего не значит. Просто так удобнее 1 . 11100000000000000000000 Что внутри числа с плавающей точкой и как оно работает - 3Теперь полученную мантиссу нужно возводить в степень слева направо, уменьшая с каждым шагом степень на одну. Стартуем со значения степени, который мы получили в результате вычисления, т. е. 2 (Я специально выбрал простой пример, чтоб не писать каждое значение степени двойки и в приведенной таблице не вычислял их, когда соответствующий бит равен нулю) Что внутри числа с плавающей точкой и как оно работает - 41×22 + 1×21 + 1×20 + 1×2-1 = 1×4 + 1×2 + 1×1 + 1×0,5 = 4+2+1+0,5 = 7,5 и получили результат 7,5, правильность можно проверить, например, по этой ссылке

Итоги

Стандартное число с плавающей точкой типа float состоит из 32 бит, первый бит — знак (+ или -), следующие восемь — экспонента, следующие 23 — мантисса По знаку — если бит 0 — число положительное. Если бит 1 — отрицательное. По экспоненте — побитно переводим в десятичное число (первый слева бит — 128, второй — 64, третий — 32, четвёртый — 16, пятый — 8, шестой — 4, седьмой — 2, восьмой — 1), из полученного числа вычитаем 127, получаем степень с которой будем стартовать. По мантиссе — к имеющимся 23 битам спереди дописываем ещё один бит со значением 1 и с него начинаем возводить в полученную нами степень, с каждым следующим битом декрементируя эту степень. That's all folks, kids! Что внутри числа с плавающей точкой и как оно работает - 5P. S.: В виде домашнего задания, используя эту статью, оставьте в комментариях свои версии, почему при большом количестве арифметических операций с числами с плавающей точкой возникают ошибки точности
Комментарии (6)
Чтобы просмотреть все комментарии или оставить свой,
перейдите в полную версию
Oleksii 27 уровень, Харьков
17 мая 2021
неудачно выбрано "полученное в результате вычисления значение степени" = 2 Реально усложняет восприятие информации. Спасибо за работу, осилил и усвоил.
barracuda 41 уровень, Санкт-Петербург Expert
7 декабря 2020
нормально заходит в час ночи.
Юрий 19 уровень, Байконур
6 декабря 2020
а как преобразовать в двоичную форму представления любое число? просто по примеру наглядно объяснено как из 0 10000001 11100000000000000000000 получили 7.5. А вот как из 7.5 получить двоичную форму представления непонятно.
Дмитрий Романюк 14 уровень, Санкт-Петербург
26 января 2020
теперь хотя бы имею представление как эти числа представляются в двоичной системе
HF 41 уровень, Харьков
25 апреля 2019
Это мое любимое число, спасибо за статью!
Сергеев Виктор 40 уровень, Санкт-Петербург Master
22 апреля 2019
сложно, но интересно, спасибо )