Как число с плавающей запятой хранится в памяти c

Обновлено: 01.07.2024

Основная идея представлений с плавающей запятой (в отличие от представлений с фиксированной запятой, используемых, скажем, в ints) заключается в том, что число x записывается как m*be, где m – мантисса или дробная часть, b — основание, e — показатель степени. На современных компьютерах основание почти всегда равно 2, и для большинства представлений с плавающей запятой мантисса будет масштабироваться между 1 и b. Это делается путем настройки показателя степени, например,

Мантисса обычно представлена ​​в основе b как двоичная дробь. Таким образом (в формате очень низкой точности), 1 будет 1.000*2 0 , 2 будет 1.000*2 1 , а 0.375 будет 1.100*2 -2 , где первая 1 после запятой считается как 1/2 , второе как 1/4 и т. д. Обратите внимание, что для правильно масштабированного (или нормализованного) числа с плавающей запятой по основанию 2 цифра перед десятичной запятой всегда равна 1. По этой причине она обычно опускается (хотя для этого требуется специальное представление для 0).

Отрицательные значения обычно обрабатываются путем добавления бита знака, который равен 0 для положительных чисел и 1 для отрицательных чисел.

Любое число, в котором есть десятичная точка, будет интерпретироваться компилятором как число с плавающей запятой. Обратите внимание, что вы должны поставить хотя бы одну цифру после запятой: 2,0, 3,75, -12,6112. Вы можете указать число с плавающей запятой в научном представлении, используя e для показателя степени: 6.022e23.

Типы с плавающей запятой в C поддерживают большинство тех же арифметических и реляционных операторов, что и целые типы; x > y, x / y, x + y имеют смысл, когда x и y< /tt> - это число с плавающей запятой. Если вы смешаете вместе два разных типа с плавающей запятой, менее точный будет расширен, чтобы соответствовать точности более точного; это также работает, если вы смешиваете целочисленные типы и типы с плавающей запятой, как в 2 / 3.0. В отличие от целочисленного деления, при делении с плавающей запятой дробная часть не отбрасывается (хотя может возникнуть ошибка округления: 2.0/3.0 дает 0,66666666666666663, что не совсем точно) . Будьте осторожны, чтобы случайно не использовать целочисленное деление, когда вы хотите использовать деление с плавающей запятой: 2/3 равно 0. Приведения могут использоваться для принудительного деления с плавающей запятой (см. ниже).

Некоторые операторы, работающие с целыми числами, не работают с типами с плавающей запятой. Это % (используйте modf из математической библиотеки, если вам действительно нужно получить остаток с плавающей запятой) и все побитовые операторы ~ , >, &, ^ и |.

Смешанное использование типов с плавающей запятой и целых чисел преобразует целые числа в числа с плавающей запятой.

Числа с плавающей запятой можно преобразовывать в целые типы и из них явным образом с помощью приведения типов. Типичное использование может быть:

Если бы мы не добавили (double) для преобразования sum в double, мы бы в конечном итоге выполнили целочисленное деление. , что усекло бы дробную часть нашего среднего значения.

В другом направлении мы можем написать:

для преобразования float f в int i. При этом преобразовании теряется информация из-за отбрасывания дробной части f: если f было 3,2, i в конечном итоге будет всего 3.

Стандарт IEEE-754 для операций с плавающей запятой — это стандарт для представления и обработки величин с плавающей запятой, которому следуют все современные компьютерные системы. Он определяет несколько стандартных представлений чисел с плавающей запятой, каждое из которых имеет следующий базовый шаблон (конкретный макет здесь для 32-битных float):

Номера битов отсчитываются от младшего бита. Первый бит — это знак (0 — положительный, 1 — отрицательный). Следующие 8 бит представляют собой показатель степени в двоичной записи с превышением 127; это означает, что двоичный шаблон 01111111 = 127 представляет показатель степени 0, 1000000 = 128 представляет собой 1, 01111110 = 126 представляет -1 и так далее. Мантисса умещается в оставшиеся 24 бита, а ее начальная единица удаляется, как описано выше.

Некоторые числа имеют особое представление. Поскольку 0 не может быть представлен в стандартной форме (перед десятичной точкой нет 1), ему дается специальное представление 0 00000000 000000000000000000000000. (Существует также -0 = 1 00000000 000000000000000000000000, что выглядит равным +0, но печатается по-другому.) Числа с показателями степени 11111111 = 255 = 2 128 представляют нечисловые величины, такие как «не число" (NaN), возвращаемое такими операциями, как (0.0/0.0), и положительная или отрицательная бесконечность. Таблица некоторых типичных чисел с плавающей запятой (сгенерированных программой float.c) приведена ниже:

На практике это означает, что 32-битное значение с плавающей запятой (например, float) может представлять собой любое число от 1,17549435e-38 до 3,40282347e+38, где e разделяет показатель степени (с основанием 10).Операции, создающие меньшее значение, будут уменьшаться до 0 (медленно — IEEE 754 допускает «денормализованные» числа с плавающей запятой с пониженной точностью для очень маленьких значений), а операции, создающие большее значение, будут производить inf или вместо этого -inf.

Для 64-битного двойника размер экспоненты и мантиссы больше; это дает диапазон от 1,7976931348623157e+308 до 2,2250738585072014e-308 с аналогичным поведением при потере значимости и переполнении.

Процессоры Intel используют для всех операций еще более крупный 80-битный формат с плавающей запятой. Если вы не объявите свои переменные как long double, это не должно быть видно из C, за исключением того, что некоторые операции, которые в противном случае могли бы привести к ошибкам переполнения, не будут этого делать, при условии, что все задействованные переменные находятся в регистрах (обычно только для локальных переменных и параметров функций).

Как правило, числа с плавающей запятой не являются точными: они могут содержать ошибку округления из-за усечения мантиссы до фиксированного числа бит. Это особенно заметно для больших значений (например, 1e+12 в приведенной выше таблице), но также может наблюдаться в дробях со значениями, которые не являются степенями числа 2 в знаменателе (например, 0,1). ). Ошибка округления часто незаметна в выходных форматах с плавающей запятой по умолчанию, поскольку они производят меньше цифр, чем хранятся внутри, но могут накапливаться со временем, особенно если вы вычитаете числа с плавающей запятой с близкими значениями (это стирает мантисса без стирая ошибку, делая ошибку намного больше по сравнению с оставшимся числом).

Самый простой способ избежать накопления ошибок — использовать высокоточные числа с плавающей запятой (это означает использование double вместо float). На современных процессорах это практически не требует времени, хотя сохранение double вместо float займет вдвое больше места в памяти.

Обратите внимание, что следствием внутренней структуры чисел с плавающей запятой IEEE 754 является то, что небольшие целые числа и дроби с малыми числителями и знаменателями степени двойки могут быть представлены точно — действительно, IEEE Стандарт 754 тщательно определяет операции с плавающей запятой, так что арифметика с такими точными целыми числами даст те же ответы, что и целочисленная арифметика (за исключением, конечно, деления, которое дает остаток). Этот факт иногда можно использовать для получения более высокой точности целочисленных значений, чем это доступно для стандартных целочисленных типов; например, double может точно представлять любое целое число от -2 53 до 2 53, что намного шире, чем значения от 2^-31^ до 2^31^-1. которые подходят для 32-битного int или long. (64-разрядное значение long работает лучше.) Поэтому double` следует рассматривать для приложений, где требуются большие точные целые числа (например, для расчета собственного капитала миллиардера в пенни).

Одним из следствий ошибки округления является то, что очень сложно проверить числа с плавающей запятой на равенство, если только вы не уверены, что получили точное значение, как описано выше. Обычно это не так, например, что (0.1+0.1+0.1) == 0.3 в C. Это может привести к странным результатам, если вы попытаетесь написать что-то вроде for(f = 0.0 ; ж

Любая числовая константа в программе C, содержащая десятичную точку, по умолчанию обрабатывается как двойная. Вы также можете использовать e или E, чтобы добавить показатель степени по основанию 10 (некоторые примеры этого см. в таблице). float по какой-то причине вы можете добавить F в конце, как в 1.0F.

Значения nan, inf и -inf не могут быть записаны в этой форме как константы с плавающей запятой в программе C. , но printf сгенерирует их, и scanf, похоже, их распознает. С некоторыми машинами и компиляторами вы можете использовать макросы INFINITY и NAN для создания бесконечных количеств. Макросы isinf и isnan можно использовать для обнаружения таких величин, если они встречаются.

Многие математические функции для значений с плавающей запятой по умолчанию не связаны с программами на C, но их можно получить путем связывания в математической библиотеке. Примерами могут служить тригонометрические функции sin, cos и tan (плюс более экзотические), sqrt для взятия квадратные корни, pow для возведения в степень, log и exp для логарифмов и показателей степени по основанию e и fmod для случая, когда вы действительно хотите написать x%y, но одна или обе переменные являются двойными. Все функции стандартной математической библиотеки принимают double в качестве аргументов и возвращают значения double; большинство реализаций также предоставляют некоторые дополнительные функции с похожими именами (например, sinf), которые вместо этого используют float, для приложений, где пространство или скорость важнее точности.

Использование математической библиотеки состоит из двух частей. Во-первых, включить строку

где-то в верхней части исходного файла. Это говорит препроцессору вставить объявления функций математической библиотеки, найденные в /usr/include/math.h.

Второй шаг – привязка к математической библиотеке при компиляции. Это делается путем передачи флага -lm в gcc после ваших исходных файлов программы C. Типичная команда может быть такой:

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

Может ли кто-нибудь помочь мне понять, как значения с плавающей запятой хранятся в памяти.

Я сомневаюсь, что здесь значения с плавающей запятой содержат ' .' (например, 3.45 ), как '.' будет представлено в памяти?

Кто-нибудь может пояснить меня диаграммой?

Существует ОГРОМНОЕ разнообразие форматов с плавающей запятой, и все они разные. IEEE с плавающей запятой в настоящее время является наиболее распространенным, но не единственным. Когда я был студентом, мне пришлось изучить формат с плавающей запятой CDC 6600, и у него были некоторые преимущества по сравнению с IEEE, самым большим из которых было 48 бит мантиссы для одинарной точности. IEEE ограничивает примерно 24 битами мантиссы для одинарной точности, поэтому каждый вводный курс по числовым методам в наши дни говорит студентам: «Всегда используйте двойное число, а не число с плавающей запятой».

2 ответа 2

Десятичная точка нигде явно не хранится; это проблема с отображением.

Следующее объяснение является упрощением; Я опускаю много важных деталей, и мои примеры не предназначены для представления какой-либо реальной платформы. Это должно дать вам представление о том, как значения с плавающей запятой представлены в памяти, и о проблемах, связанных с ними, но вам следует найти более авторитетные источники, такие как «Что каждый компьютерный ученый должен знать об арифметике с плавающей запятой».

Начните с представления числа с плавающей запятой в одном из вариантов экспоненциальной записи, используя основание 2 вместо основания 10. Например, значение 3,14159 можно представить как

0,7853975 – это мантисса, также известная как мантисса; это часть числа, содержащая значащие цифры. Это значение умножается на основание 2, возведенное в степень 2, и получается 3,14159.

Числа с плавающей запятой кодируются путем сохранения мантиссы и показателя степени (вместе со знаковым битом).

Типичный 32-битный макет выглядит примерно так:

Как и в целых числах со знаком, старший бит указывает на знак; 0 указывает на положительное значение, 1 указывает на отрицательное.

Следующие 8 бит используются для экспоненты. Показатель степени может быть положительным или отрицательным, но вместо того, чтобы резервировать еще один бит знака, он кодируется таким образом, что 10000000 представляет 0, поэтому 00000000 представляет собой -128, а 11111111 представляет 127.

Остальные биты используются для мантиссы. Каждый бит представляет собой отрицательную степень числа 2, считая слева, поэтому:

Некоторые платформы предполагают наличие "скрытого" начального бита в мантиссе, который всегда равен 1, поэтому значения в мантиссе всегда находятся в диапазоне [0,5, 1). Это позволяет этим платформам хранить значения с несколько большей точностью (подробнее об этом ниже). Мой пример этого не делает.

Поэтому наше значение 3,14159 будет представлено примерно так

Теперь, если вы сложите все биты в мантиссе, вы заметите, что они не составляют в сумме 0,7853975; на самом деле они составляют 0,78539747. Недостаточно битов для хранения значения точно; мы можем хранить только приближение. Количество битов в мантиссе определяет точность или количество значащих цифр, которое можно сохранить. 23 бита дают нам примерно 6 десятичных цифр точности. 64-битные типы с плавающей запятой предлагают достаточно битов в мантиссе, чтобы дать примерно от 12 до 15 цифр точности. Но имейте в виду, что есть значения, которые не могут быть точно представлены независимо от того, сколько битов вы используете. Точно так же, как такие значения, как 1/3, не могут быть представлены конечным числом десятичных цифр, такие значения, как 1/10, не могут быть представлены конечным числом битов. Поскольку значения приблизительны, расчеты с ними также приблизительны, и ошибки округления накапливаются.

Количество битов в показателе степени определяет диапазон (минимальное и максимальное значения, которые вы можете представить). Но по мере того, как вы приближаетесь к своим минимальным и максимальным значениям, размер разрыва между представляемыми значениями увеличивается. То есть, если вы не можете точно представить значения от 0,785397 до 0,785398, то вы не можете точно представить значения от 7,85397 до 7,85398, или значения от 78,5397 до 78,5398, или значения от 785397,0 до 785398,0. Будьте осторожны при умножении очень больших (по величине) чисел на очень маленькие числа.

Я прочитал этот документ, но ничего не понял.

Документ, на который вы ссылаетесь, довольно ясно объясняет это. Что конкретно вам трудно понять?

@MichaelBorgwardt Нет, НЕ ЯСНО. Он объясняет, как хранится показатель степени ПОСЛЕ введения проблемы, которая нуждается в этом объяснении (Но что, если число равно нулю? О боже). Это как в тех криминальных историях, где фишка в том, что вам не показали всю информацию, но главный герой истории все знает.

7 ответов 7

Чтобы понять, как они хранятся, вы должны сначала понять, что они из себя представляют и какие значения они предназначены обрабатывать.

В отличие от целых чисел, значение с плавающей запятой предназначено для представления как очень малых значений, так и очень больших. Для обычных 32-битных значений с плавающей запятой это соответствует значениям в диапазоне от 1,175494351 * 10^-38 до 3,40282347 * 10^+38.

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

Что касается представления, вы можете видеть все обычные числа с плавающей запятой в виде значения в диапазоне от 1,0 до (почти) 2,0, масштабированном со степенью двойки. Итак:

  • 1,0 — это просто 1,0 * 2^0,
  • 2,0 равно 1,0 * 2^1 и
  • -5,0 равно -1,25 * 2^2.

Итак, что необходимо для максимально эффективного кодирования? Что нам действительно нужно?

  • Знак выражения.
  • Показатель степени
  • Значение в диапазоне от 1,0 до (почти) 2,0. Это известно как "мантисса" или мантисса.

Это кодируется следующим образом в соответствии со стандартом IEEE-754 с плавающей запятой.

  • Знак представляет собой один бит.
  • Показатель степени сохраняется как целое число без знака, для 32-битных значений с плавающей запятой это поле занимает 8 бит. 1 представляет наименьший показатель степени, а "все единицы - 1" - наибольший. (0 и "все единицы" используются для кодирования специальных значений, см. ниже.) Значение в середине (127 в 32-битном случае) представляет ноль, это также известно как смещение. .
  • Глядя на мантиссу (значение от 1,0 до (почти) 2,0), можно увидеть, что все возможные значения начинаются с "1" (как в десятичном, так и в двоичном представлении). Это означает, что хранить его нет смысла. Остальные двоичные цифры хранятся в целочисленном поле, в 32-битном случае это поле составляет 23 бита.

В дополнение к обычным значениям с плавающей запятой существует ряд специальных значений:

  • Нуль кодируется как с показателем степени, так и с мантиссом, равным нулю. Знаковый бит используется для представления «плюс ноль» и «минус ноль». Минус ноль полезен, когда результат операции очень мал, но все равно важно знать, откуда пришла операция.
  • плюс-минус бесконечность – представлено с помощью показателя степени "все единицы" и поля нулевой мантиссы.
  • Не число (NaN) – представлено с использованием показателя степени "все единицы" и ненулевой мантиссы.
  • Денормализованные числа – числа меньше наименьшего нормального числа. Представлено с использованием поля нулевой степени и ненулевой мантиссы. Особенность этих чисел заключается в том, что точность (то есть количество цифр, которое может содержать значение) будет падать по мере уменьшения значения просто потому, что в мантиссе для них нет места.

Наконец, ниже приведено несколько конкретных примеров (все значения в шестнадцатеричном формате):

Скаляры типа float хранятся в четырех байтах (32 бита). Используемый формат соответствует стандарту IEEE-754.

Число с плавающей запятой выражается как произведение двух частей: мантиссы и степени двойки. Например:

±мантисса × 2 степени

Мантисса представляет фактические двоичные разряды числа с плавающей запятой.

Мантисса – это 24-битное значение (представляющее примерно семь десятичных цифр), старший бит которого (СЗБ) всегда равен 1 и поэтому не сохраняется. Также имеется бит знака, указывающий, является ли число с плавающей запятой положительным или отрицательным.

Числа с плавающей запятой хранятся на границах байтов в следующем формате:

Адрес+0 Адрес+1< /th> Адрес+2 Адрес+3
Содержание СМОТРЕТЬ EEEE ЭМММ ММММ ММММ ММММ ММММ ММММ

S представляет бит знака, где 1 — отрицательное значение, а 0 — положительное.
E это показатель степени со смещением 127.
M это 24-битная мантисса (сохраненная в 23 битах).

Ноль — это специальное значение, обозначаемое полем экспоненты, равным 0, и мантиссом, равным 0.

В приведенном выше формате число с плавающей запятой -12,5 сохраняется как шестнадцатеричное значение 0xC1480000.В памяти это значение отображается следующим образом:

Адрес+0 Адрес+1 Адрес+2 Адрес+3 Содержание 0xC1 0x48 0x00 0x00

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

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

< td >00000000
Адрес+0 Адрес+1 Адрес+2 Адрес+3
Формат SEEEEEEE EMMMMMMM MMMMMMMM MMMMMMMM
Двоичный 11000001 01001000 00000000
Hex C1 48 00 00

Из этой иллюстрации вы можете определить следующее:

  • Бит знака равен 1, что указывает на отрицательное число.
  • Показатель степени равен 10000010 в двоичном формате или 130 в десятичном формате. Если вычесть 127 из 130, получится 3, что и является показателем степени.
  • Мантисса выглядит как следующее двоичное число:

Слева от мантиссы есть понятная двоичная точка, которой всегда предшествует 1. Эта цифра опущена в сохраненной форме числа с плавающей запятой. Добавление 1 и двоичной точки к началу мантиссы дает следующее значение:

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

Результатом является двоичное число с плавающей запятой. Двоичные цифры слева от десятичной точки представляют степень двойки, соответствующую их положению. Например, 1100 представляет собой (1 × 2 3 ) + (1 × 2 2 ) + (0 × 2 1 ) + (0 × 2 0 ), что равно 12.

Двоичные цифры справа от десятичной точки также представляют степень двойки, соответствующую их положению. Однако силы отрицательные. Например, .100. представляет (1 × 2-1) + (0 × 2-2) + (0 × 2-3) +. что равно 0,5.

Сумма этих значений равна 12,5. Поскольку бит знака был установлен, это число должно быть отрицательным.

Читайте также: