在没有双精度类型的C编译器上解析双精度IEEE浮点

我正在使用8位AVR芯片。 64位double没有数据类型(double只映射到32位float)。 但是,我将通过串口接收64位双打,并且需要通过串行输出64位双打。

如何在不进行转换的情况下将64位双精度转换为32位浮点数并再次返回? 32位和64位的格式都遵循IEEE 754.当然,我假设转换为32位浮点时精度会下降。

为了从64位转换为32位浮点数,我正在尝试这样做:

// Script originally from http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1281990303 float convert(uint8_t *in) { union { float real; uint8_t base[4]; } u; uint16_t expd = ((in[7] & 127) <> 4); uint16_t expf = expd ? (expd - 1024) + 128 : 0; u.base[3] = (in[7] & 128) + (expf >> 1); u.base[2] = ((expf & 1) << 7) + ((in[6] & 15) <> 5); u.base[1] = ((in[5] & 0x1f) <> 5); u.base[0] = ((in[4] & 0x1f) <> 5); return u.real; } 

对于像1.0和2.0这样的数字,上面的工作,但是当我测试传入1.1作为64位双,输出有点(字面上,不是双关语!),虽然这可能是一个问题我的测试。 看到:

 // Comparison of bits for a float in Java and the bits for a float in C after // converted from a 64-bit double. Last bit is different. // Java code can be found at https://gist.github.com/912636 JAVA FLOAT: 00111111 10001100 11001100 11001101 C CONVERTED FLOAT: 00111111 10001100 11001100 11001100 

IEEE指定了五种不同的舍入模式 ,但默认使用的是舍入到舍入的 一半 。 所以你有一个10001100 11001100 11001100 11001100forms的尾数…你必须将其四舍五入到24位。 将位从0(最高有效)编号,位24为1; 但这还不足以告诉你是否要将第23位上升到第二位。 如果所有剩余的比特都是0,则不会向上舍入,因为第23位为0(偶数)。 但是剩下的比特不是零,所以你在所有情况下都要整理。

一些例子:

10001100 11001100 11001100 10000000 …(全零)不会向上舍入,因为第23位已经是偶数。

10001100 11001100 11001101 10000000 …(全零)向上舍入,因为第23位是奇数。

10001100 11001100 1100110x 10000000 … 0001总是向上舍入,因为其余位不全为零。

10001100 11001100 1100110x 0xxxxxxx …从不向上舍入,因为第24位为零。

以下代码似乎从单精度转换为双精度。 我将把它作为练习留给读者来实现缩小版本。 这应该让你开始。 最难的部分是使有效位中的位位置正确。 我提供了一些评论,其中包括正在发生的事情。

 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_bytesLITTLE_ENDIANdump_bits提供适当的定义。 我不想为你们破坏所有的乐趣。 关于单精度和双精度数的维基百科条目非常好。

如果你要修改浮点数很多,你应该阅读David Goldberg 撰写的“每个计算机科学家应该知道什么关于浮点运算”和Steele和White的“如何精确打印浮点数” 。 在理解浮点数如何工作时,它们是两篇内容最丰富的文章。

http://www.google.com/search?q=c+convert+ieee+754+double+single

最初的结果之一是:

http://www.mathworks.com/matlabcentral/fileexchange/23173

该代码显示了如何将IEEE-754 double转换为类似IEEE-754(1,5,10)的浮动格式。 此代码包含大量注释,并提及您可能陷入的典型陷阱。

这不完全是你想要的,但它是一个很好的起点。

我知道在AVR的GCC中只有一个完整的IEEE754双实现,你可以在这里找到它。

您将需要存档,然后使用存档替换存档中的avr_f64.c

库需要大约21K闪存和310字节的RAM。

原帖可以在这里找到。 我从原始post中提取了所有重要信息并在此处显示,因为我认为您需要有一个帐户才能登录论坛。