使用位操作将整数转换为浮点数在C中的某些整数上打破

在类赋值上工作时,我试图仅使用位操作将整数强制转换为浮点数(限于任何整数/无符号运算,包括||,&&。也if,while)。 我的代码适用于大多数值,但有些值不能生成我正在寻找的结果。

例如,如果x是0x807fffff,我得到0xceff0001,但正确的结果应该是0xceff0000。 我想我的尾数和四舍五入都缺少一些东西,但不能把它固定下来。 我已经查看了SO上的其他一些线程以及转换为int-to-float和如何手动操作

unsigned dl22(int x) { int tmin = 0x1 << 31; int tmax = ~tmin; unsigned signBit = 0; unsigned exponent; unsigned mantissa; int bias = 127; if (x == 0) { return 0; } if (x == tmin) { return 0xcf << 24; } if (x < 0) { signBit = x & tmin; x = (~x + 1); } exponent = bias + 31; while ( ( x & tmin) == 0 ) { exponent--; x <<= 1; } exponent <> 8); mantissa = (x >> 8) & mantissaMask; return (signBit | exponent | mantissa); } 

编辑/更新找到一个可行的解决方案 – 见下文

您的代码会根据您提供的示例为我生成预期输出。 然而,正如在评论中所讨论的那样,从C的角度来看,它确实表现出未定义的行为 – 不仅在tmin的计算中,而且出于同样的原因,在计算指数的循环中也是如此。 无论此代码产生的结果在不同环境之间的变化,都将遵循未定义的行为或假设[ unsigned ] int的大小对于正在使用的C实现不正确。

然而,如果我们假设(不安全)

  1. int的移位操作就好像左操作数被重新解释为具有相同位模式的unsigned int ,操作,并且结果位模式被重新解释为int ,并且
  2. intunsigned int至少为32位宽,

然后你的代码似乎是正确的,模数舍入。

如果输入int的绝对值具有超过24个有效二进制数字(即它至少为24 ),则转换中将丢失一些精度。 在这种情况下,正确的结果将取决于您打算实施的FP舍入模式。 错误的舍入结果将在最后一个位置关闭1个单位; 影响的结果数取决于舍入模式。

简单地截断/移出额外的位会产生向零模式的转换。 这是标准的舍入模式之一,但不是默认值。 默认的舍入模式是舍入到最接近的可表示数字,并且解决了关系,有利于具有最低有效位0(舍入到偶数)的结果; 还有其他三种标准模式。 要实现除了舍入为零之外的任何模式,您需要在缩放之后和关闭它们之前捕获有效数字的8个最低有效位。 这些以及取决于所选舍入模式的其他细节将决定如何应用正确的舍入。

当以零到零模式转换时,大约一半的32位二进制补码数将以不同于在任何一种其他模式中转换时进行舍入。 哪些数字表示差异取决于您考虑的舍入模式。

我最初没有提到我试图模仿U2F联盟声明:

 float u2f(unsigned u) { union { unsigned u; float f; } a; au = u; return af; } 

感谢ieee-754-bit-manipulation-rounding-error中提供的指导,我能够通过在my语句之后添加以下内容来管理舍入问题。 这澄清了正在发生的四舍五入。

 lsb = (x >> 8) & 1; roundBit = (x >> 7) & 1; stickyBitFlag = !!(x & 0x7F); exponent <<= 23; int mantissaMask = ~(tmin >> 8); mantissa = (x >> 8); mantissa &= mantissaMask; roundBit = (roundBit & stickyBitFlag) | (roundBit & lsb); return (signBit | exponent | mantissa) + roundBit;