Почему у программирования поплыла запятая?
Числа с плавающей запятой в англоязычной литературе называются числа с плавающей точкой (от англ. floating point). Такое различие связано с тем, что в русскоязычной литературе принято отделять дробную часть числа запятой, а в европейской и американской - точкой.
Хранение чисел с плавающей запятой работает по стандарту IEEE 754 (1985 г). Для работы с числами с плавающей запятой на аппаратурном уровне к обычному процессору добавляют математический сопроцессор (FPU, floating point unit).
Рисунок демонстрирует, как распределяются биты в числах с плавающей запятой разных разрядностей, где
- S - Sign (знак);
- E - Exponent (8(11) разрядов поля порядка, экспонента);
- M - Mantissa (23(52) бита мантиссы, дробная часть числа).
Также на рисунке показана так называемая, мнимая единица, она всегда есть в самом старшем разряде мантиссы, поэтому её всегда подразумевают, но в явном виде не хранят, экономя один бит информации.
Если попытаться уложить весь стандарт в два предложения, то получится примерно следующее: получить число в соответствующих разрядностях возможно по формулам:
Например: +0,5 = 2 в -1й степени поэтому, число будет записано как
0_01111110_00000000000000000000000,
то есть знак равен 0, мантисса равна 0, порядок равен 127 - 1 = 126, чтобы получить следующие результаты вычислений:
- -1 в нулевой степени - положительный знак;
- умножить на порядок 2 в степени 126-127, то есть в -1й = 0,5;
- и умножить на мантиссу 1 + 0.
То есть, 1 * 0,5 * (1 + 0) = 0,5.
Отсюда становится очевидно, что чем сложнее мантисса и чем меньше порядок, тем более точные и интересные числа мы можем получить.
А запятая-то куда поплыла?
Возьмём для примера число -0,15625
, чтобы понять как его записывать, откинем знак, это будет единица в разряде, отвечающем за знак, и посчитаем мантиссу с порядком. Представим число как положительное и будем от него последовательно отнимать числа, являющиеся отрицательными степенями двойки, чтобы получить максимально близкое к нулю значение.
Очевидно, что -1 и -2 степени отнять не получится, поскольку мы явно уходим за границу нуля, а вот -3 прекрасно отнимается, значит порядок будет 127 - 3 = 124, осталось понять, что получится в мантиссе. Видим, что оставшееся после первого вычитания (0,15625 - 0,125) число - это 2 в -5й степени. Значит в мантиссе пишем 01
и остальные нули, то есть слева направо указываем, какие степени после -3 будут нужны, а именно, -4 не нужна, ставим 0, а -5 нужна, ставим 1. Довольно очевидно, что при подсчёте других чисел нам так не повезёт, поэтому точность представления может страдать, мы будем отнимать степени двойки пока не получим 0 или пока не закончится мантисса.
Отсюда и "уплывание" точности, отсюда же и разделение на float/double, чем больше битов есть для хранения значения, тем более высокую точность можно записать.
Для полноты картины, приведу пару математических выкладок, по только что описанному алгоритму
и по формуле, представленной в самом начале
чтобы можно было самостоятельно посчитать на калькуляторе и убедиться в тождественности выражений. Так, число с плавающей запятой возможно посчитать двумя способами: по приведённой формуле, или последовательно складывая разряды мантиссы умноженные на двойку в степени порядка, уменьшая порядок на каждом шагу.
Может, есть какие-то особенности?
Конечно, куда же без них. Посмотрим на диапазон возможных значений чисел с плавающей запятой и отметим некоторые особенности:
- возможен как положительный, так и отрицательный ноль (в целых числах ноль всегда положительный);
- есть огромная зона, отмеченная на рисунке, которая являет собой непредставимые числа, слишком большие для хранения внутри такой переменной или настолько маленькие, что мнимая единица в мантиссе отсутствует;
- в таком числе можно хранить значения положительной и отрицательной бесконечности;
- при работе с такими числами появляется понятие не-числа, при этом важно помнить, что
NaN
не равенNaN
.