Значения с плавающей точкой (в любом случае IEEE754) в основном состоят из трех компонентов:
- знак
s
; - серия битов экспоненты
e
;и - серия битов мантиссы
m
.
Точность определяет, сколько битов доступно для показателя степени и мантиссы.Давайте рассмотрим значение 0,1 для плавающей запятой одинарной точности:
s eeeeeeee mmmmmmmmmmmmmmmmmmmmmmm 1/n
0 01111011 10011001100110011001101
||||||||||||||||||||||+- 8388608
|||||||||||||||||||||+-- 4194304
||||||||||||||||||||+--- 2097152
|||||||||||||||||||+---- 1048576
||||||||||||||||||+----- 524288
|||||||||||||||||+------ 262144
||||||||||||||||+------- 131072
|||||||||||||||+-------- 65536
||||||||||||||+--------- 32768
|||||||||||||+---------- 16384
||||||||||||+----------- 8192
|||||||||||+------------ 4096
||||||||||+------------- 2048
|||||||||+-------------- 1024
||||||||+--------------- 512
|||||||+---------------- 256
||||||+----------------- 128
|||||+------------------ 64
||||+------------------- 32
|||+-------------------- 16
||+--------------------- 8
|+---------------------- 4
+----------------------- 2
Знак положительный, это довольно просто.
Показатель степени 64+32+16+8+2+1 = 123 - 127 bias = -4
, поэтому множитель 2 -4 или 1/16
.Смещение есть, так что вы можете получить действительно маленькие числа (например, 10 -30 ), а также большие.
Мантисса короткая.Он состоит из 1
(неявная база) плюс (для всех этих битов, каждый из которых стоит 1 / (2 n ), так как n
начинается с 1
и увеличивается вправо), {1/2, 1/16, 1/32, 1/256, 1/512, 1/4096, 1/8192, 1/65536, 1/131072, 1/1048576, 1/2097152, 1/8388608}
.
Когда вы сложите все это, вы получите 1.60000002384185791015625
.
Когда вы умножите это на множитель 2 -4 , вы получите 0.100000001490116119384765625
Вот почему они говорят, что вы не можете представлять 0.1
точно в виде числа с плавающей запятой IEEE754.
С точки зрения преобразования целых чисел в числа с плавающей запятой, если в мантиссе столько битов (включаянеявный 1), вы можете просто передать целочисленную битовую комбинацию и выбрать правильный показатель степени.Там не будет потеря точности.Например, IEEE754 двойной точности (64 бита, 52/53 из которых являются мантиссами) без проблем принимает 32-битное целое число.
Если в вашем целом числе больше битов (например, 32-битное)целое число и 32-разрядное число с плавающей запятой одинарной точности, которое имеет только 23/24 бита мантиссы), тогда вам нужно масштабировать целое число.
Это включает в себя удаление наименьших значащих бит (фактически округление), чтобывписаться в биты мантиссы.Это, конечно, подразумевает потерю точности, но это неизбежно.
Давайте посмотрим на конкретное значение, 123456789
.Следующая программа выводит биты каждого типа данных.
#include <stdio.h>
static void dumpBits (char *desc, unsigned char *addr, size_t sz) {
unsigned char mask;
printf ("%s:\n ", desc);
while (sz-- != 0) {
putchar (' ');
for (mask = 0x80; mask > 0; mask >>= 1, addr++)
if (((addr[sz]) & mask) == 0)
putchar ('0');
else
putchar ('1');
}
putchar ('\n');
}
int main (void) {
int intNum = 123456789;
float fltNum = intNum;
double dblNum = intNum;
printf ("%d %f %f\n",intNum, fltNum, dblNum);
dumpBits ("Integer", (unsigned char *)(&intNum), sizeof (int));
dumpBits ("Float", (unsigned char *)(&fltNum), sizeof (float));
dumpBits ("Double", (unsigned char *)(&dblNum), sizeof (double));
return 0;
}
Вывод в моей системе выглядит следующим образом:
123456789 123456792.000000 123456789.000000
integer:
00000111 01011011 11001101 00010101
float:
01001100 11101011 01111001 10100011
double:
01000001 10011101 01101111 00110100 01010100 00000000 00000000 00000000
И мы рассмотрим их по одному за раз.Сначала целое число, простые степени двух:
00000111 01011011 11001101 00010101
||| | || || || || | | | +-> 1
||| | || || || || | | +---> 4
||| | || || || || | +-----> 16
||| | || || || || +----------> 256
||| | || || || |+------------> 1024
||| | || || || +-------------> 2048
||| | || || |+----------------> 16384
||| | || || +-----------------> 32768
||| | || |+-------------------> 65536
||| | || +--------------------> 131072
||| | |+----------------------> 524288
||| | +-----------------------> 1048576
||| +-------------------------> 4194304
||+----------------------------> 16777216
|+-----------------------------> 33554432
+------------------------------> 67108864
==========
123456789
Теперь давайте посмотрим на число с плавающей запятой одинарной точности.Обратите внимание на битовую комбинацию мантиссы, совпадающую с целым числом, как почти идеальное совпадение:
mantissa: 11 01011011 11001101 00011 (spaced out).
integer: 00000111 01011011 11001101 00010101 (untouched).
Слева от мантиссы есть бит неявный 1
, и он также округленна другом конце, откуда и происходит эта потеря точности (значение меняется с 123456789
на 123456792
, как в выходных данных этой программы выше).
Обработка значений:
s eeeeeeee mmmmmmmmmmmmmmmmmmmmmmm 1/n
0 10011001 11010110111100110100011
|| | || |||| || | |+- 8388608
|| | || |||| || | +-- 4194304
|| | || |||| || +------ 262144
|| | || |||| |+-------- 65536
|| | || |||| +--------- 32768
|| | || |||+------------ 4096
|| | || ||+------------- 2048
|| | || |+-------------- 1024
|| | || +--------------- 512
|| | |+----------------- 128
|| | +------------------ 64
|| +-------------------- 16
|+---------------------- 4
+----------------------- 2
Знак положительный.Показатель степени равен 128+16+8+1 = 153 - 127 bias = 26
, поэтому множитель равен 2 26 или 67108864
.
Мантисса равна 1
(неявное основание) плюс (как описано выше), {1/2, 1/4, 1/16, 1/64, 1/128, 1/512, 1/1024, 1/2048, 1/4096, 1/32768, 1/65536, 1/262144, 1/4194304, 1/8388608}
.Когда вы складываете все это, вы получаете 1.83964955806732177734375
.
Когда вы умножаете это на множитель 2 26 , вы получаете 123456792
, то же самое, что и вывод программы.
Вывод двойной битовой маски:
s eeeeeeeeeee mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
0 10000011001 1101011011110011010001010100000000000000000000000000
Я не собираюсь пройти через процесс определения значения этого зверя :-) Однако, я будет показывать мантиссу рядом с целочисленным форматом, чтобы показать общее представление битов:
mantissa: 11 01011011 11001101 00010101 000...000 (spaced out).
integer: 00000111 01011011 11001101 00010101 (untouched).
Вы можете снова увидеть сходство с неявным битом слева и значительно большую доступность битов наПраво, поэтому в этом случае нет потери точности.
С точки зрения преобразования между числами с плавающей запятой и двойными, это также достаточно легко понять.
Сначала вы должныпроверьте специальные значения, такие как NaN и бесконечности.Они обозначены специальными комбинациями экспонента / мантисса, и, вероятно, их легче обнаружить, создав прямой эквивалент в новом формате.
Тогда в случае, когда вы переходите от двойного к плавающему, у вас, очевидно, меньше доступного диапазона, поскольку в показателе степени меньше битов.Если ваш двойник находится за пределами диапазона с плавающей точкой, вам нужно с этим справиться.
Предполагая, что он подойдет, вам необходимо:
- перебазировать показатель степени (смещение отличаетсядля этих двух типов).
- скопируйте столько битов из мантиссы, сколько потребуется (округление, если необходимо).
- добавление остальной части целевой мантиссы (если есть) с нулевыми битами.