Что происходит в фоновом режиме при конвертации int в float
Я немного не понимаю, как можно бросить int, чтобы плавать, шаг за шагом? Предположим, у меня есть целое число со знаком в двоичном формате. Более того, я хочу, чтобы его бросили плавать вручную. Однако я не могу. Итак, можно ли мне показать, как сделать это преобразование шаг за шагом?
Я делаю это преобразование в с, много раз? лайк;
int a = foo ( );
float f = ( float ) a ;
Но я не понимаю, что происходит на заднем плане. Более того, чтобы хорошо понять, я хочу сделать это преобразование вручную.
РЕДАКТИРОВАТЬ: Если вы знаете много о конверсии, вы также можете предоставить информацию о двойном преобразовании с плавающей точкой. Кроме того, для плавания в Int
2 ответа
Значения с плавающей точкой (в любом случае 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 и бесконечности. Они обозначены специальными комбинациями экспонента / мантисса, и, вероятно, их легче обнаружить, создав прямой эквивалент в новом формате.
Тогда в случае, когда вы переходите от двойного к плавающему, вы, очевидно, имеете меньший диапазон, доступный для вас, поскольку в экспоненте меньше битов. Если ваш двойник находится за пределами диапазона поплавка, вы должны с этим справиться.
Предполагая, что он подойдет, вам необходимо:
- перебазировать показатель степени (смещение отличается для двух типов).
- скопируйте столько битов из мантиссы, сколько потребуется (округление при необходимости).
- заполнение остальной части целевой мантиссы (если есть) нулевыми битами.
Концептуально это довольно просто. float
(в IEEE 754-1985) имеет следующее представление:
- 1 битный знак
- 8-битная экспонента (0 означает денормализованные числа, 1 означает -126, 127 означает 0, 255 означает бесконечность)
- 23-битная мантисса (часть, которая следует за "1")
Так что в основном это примерно:
- определить знак и величину числа
- найдите 24 наиболее значимых и правильно закругленных бита
- скорректировать показатель
- закодировать эти три части в 32-битную форму
При реализации собственного преобразования это легко проверить, поскольку вы можете просто сравнить результаты со встроенным оператором преобразования типов.