Следующий код, по-видимому, преобразуется из одинарной точности в двойную точность. Я оставлю это в качестве упражнения для читателя, чтобы реализовать сужающую версию. Это должно помочь вам начать. Самое сложное - получить правильное положение бит в значении. Я включаю некоторые комментарии, которые включают в себя то, что происходит.
double
extend_float(float f)
{
unsigned char flt_bits[sizeof(float)];
unsigned char dbl_bits[sizeof(double)] = {0};
unsigned char sign_bit;
unsigned char exponent;
unsigned int significand;
double out;
memcpy(&flt_bits[0], &f, sizeof(flt_bits));
/// printf("---------------------------------------\n");
/// printf("float = %f\n", f);
#if LITTLE_ENDIAN
reverse_bytes(flt_bits, sizeof(flt_bits));
#endif
/// dump_bits(&flt_bits[0], sizeof(flt_bits));
/* IEEE 754 single precision
* 1 sign bit flt_bits[0] & 0x80
* 8 exponent bits flt_bits[0] & 0x7F | flt_bits[1] & 0x80
* 23 fractional bits flt_bits[1] & 0x7F | flt_bits[2] & 0xFF |
* flt_bits[3] & 0xFF
*
* E = 0 & F = 0 -> +/- zero
* E = 0 & F != 0 -> sub-normal
* E = 127 & F = 0 -> +/- INF
* E = 127 & F != 0 -> NaN
*/
sign_bit = (flt_bits[0] & 0x80) >> 7;
exponent = ((flt_bits[0] & 0x7F) << 1) | ((flt_bits[1] & 0x80) >> 7);
significand = (((flt_bits[1] & 0x7F) << 16) |
(flt_bits[2] << 8) |
(flt_bits[3]));
/* IEEE 754 double precision
* 1 sign bit dbl_bits[0] & 0x80
* 11 exponent bits dbl_bits[0] & 0x7F | dbl_bits[1] & 0xF0
* 52 fractional bits dbl_bits[1] & 0x0F | dbl_bits[2] & 0xFF
* dbl_bits[3] & 0xFF | dbl_bits[4] & 0xFF
* dbl_bits[5] & 0xFF | dbl_bits[6] & 0xFF
* dbl_bits[7] & 0xFF
*
* E = 0 & F = 0 -> +/- zero
* E = 0 & F != 0 -> sub-normal
* E = x7FF & F = 0 -> +/- INF
* E = x7FF & F != 0 -> NaN
*/
dbl_bits[0] = flt_bits[0] & 0x80; /* pass the sign bit along */
if (exponent == 0) {
if (significand == 0) { /* +/- zero */
/* nothing left to do for the outgoing double */
} else { /* sub-normal number */
/* not sure ... pass on the significand?? */
}
} else if (exponent == 0xFF) { /* +/-INF and NaN */
dbl_bits[0] |= 0x7F;
dbl_bits[1] = 0xF0;
/* pass on the significand */
} else { /* normal number */
signed int int_exp = exponent;
int_exp -= 127; /* IEEE754 single precision exponent bias */
int_exp += 1023; /* IEEE754 double precision exponent bias */
dbl_bits[0] |= (int_exp & 0x7F0) >> 4; /* 7 bits */
dbl_bits[1] = (int_exp & 0x00F) << 4; /* 4 bits */
}
if (significand != 0) {
/* pass on the significand most-significant-bit first */
dbl_bits[1] |= (flt_bits[1] & 0x78) >> 3; /* 4 bits */
dbl_bits[2] = (((flt_bits[1] & 0x07) << 5) | /* 3 bits */
((flt_bits[2] & 0xF8) >> 3)); /* 5 bits */
dbl_bits[3] = (((flt_bits[2] & 0x07) << 5) | /* 3 bits */
((flt_bits[3] & 0xF8) >> 3)); /* 5 bits */
dbl_bits[4] = ((flt_bits[3] & 0x07) << 5); /* 3 bits */
}
///dump_bits(&dbl_bits[0], sizeof(dbl_bits));
#if LITTLE_ENDIAN
reverse_bytes(&dbl_bits[0], sizeof(dbl_bits));
#endif
memcpy(&out, &dbl_bits[0], sizeof(out));
return out;
}
Я оставил несколько printf
строк, но закомментировал их в комментариях в стиле C ++. Вам нужно будет предоставить соответствующие определения для reverse_bytes
, LITTLE_ENDIAN
и dump_bits
. Я не хотел испортить вам все веселье. Записи в Википедии о одинарной точности и двойной точности числа очень хороши.
Если вы собираетесь много работать с числами с плавающей запятой, вам следует прочитать "Что должен знать каждый компьютерный специалист об арифметике с плавающей запятой" Дэвида Голдберга и
«Как печатать числа с плавающей запятой точно» от Стила и Уайта. Это две наиболее информативные статьи, когда дело доходит до понимания того, как работают числа с плавающей запятой.