将无符号整数转换为python中的float

时间:2015-05-08 13:03:51

标签: python ieee-754 floating-point-conversion

我编写了一个从某些设备读取数据的套接字服务器。读取数据后,二进制移位应用于字节。之后,我得到一个整数值,例如repaint(),我想将此数字转换为IEEE 754 float 35.844417572021484。我在struct.unpack中找到了一些解决方案,但在我看来并不合理。首先我们将数字转换为字符串,然后转换为float。

Java中是否有像1108304047这样的简短方法。

我在struct.unpack中找到的解决方案很长。它包含字符串转换,子字符串提取,零填充等。

Float.intBitsToFloat(1108304047)

2 个答案:

答案 0 :(得分:2)

正如您在Java中所看到的,他们正在使用结构来实现这一目标。

/*
 * Find the float corresponding to a given bit pattern
*/
JNIEXPORT jfloat JNICALL
Java_java_lang_Float_intBitsToFloat(JNIEnv *env, jclass unused, jint v)
{
    union {
        int i;
        float f;
    } u;
    u.i = (long)v;
    return (jfloat)u.f;
}

在Python中,不可能这样做,因此您需要使用struct library

  

此模块执行Python值和表示为Python字符串的C结构之间的转换

首先,该号码将转换为long

的表示形式
packed_v = struct.pack('>l', b)

然后解压缩到float

f = struct.unpack('>f', packed_v)[0]

这与Java类似。

def intBitsToFloat(b):
   s = struct.pack('>l', b)
   return struct.unpack('>f', s)[0]

如果我错了,请纠正我。

答案 1 :(得分:0)

ldexpfrexp分解为正数。

如果您可以使用最多2 -16 的相对误差量,则可以仅使用基本算法和ld/frexp分解来表示变换的两侧。

请注意,这比结构黑客慢得多,结构黑客可以更简洁地表示为struct.unpack('f', struct.pack('I', value))

这是分解方法。

def to_bits(x):
  man, exp = math.frexp(x)
  return int((2 * man + (exp + 125)) * 0x800000)

def from_bits(y):
  y -= 0x3e800000
  return math.ldexp(
      float(0x800000 + y & 0x7fffff) / 0x1000000, 
      (y - 0x800000) >> 23)

虽然from_bits函数看起来更可怕,但它实际上只是to_bits的反函数,经过修改后我们只执行单个浮点除法(不是因为速度考虑,只是因为它当我们需要使用浮点数的机器表示时,应该是我们的心态。因此,我将专注于解释正向转型。

推导

回想一下,(正)IEEE 754浮点数表示为偏置指数的元组及其尾数。低23位 m 是尾数,高8位 e (减去最高有效位,我们假设总是为零)代表指数,因此

  

x =(1 + m / 2 23 )* 2 e - 127

man&#39> = m / 2 23 exp' = e - 127,然后0< = man' < 1和 exp' 是一个整数。因此

  

man' + exp' + 127)* 2 23

给出IEEE 754表示。

另一方面,frexp分解会计算一对man, exp = frexp(x),以便 man * 2 exp = x,并且0.5< = man < 1。

思考的一刻将表明 man' = 2 * man - 1和 exp' = exp - 1,因此它的IEEE机器表示是

  

man&#39; + exp&#39; + 127)* 0x800000 =(2 * man + exp < / em> + 125)* 0x800000

错误分析

我们期望多少舍入错误?好吧,我们假设frexp在其分解中没有引入任何错误。不幸的是,这是不可能的,但我们可以放松这一点。

主要功能是计算2 * man + (exp + 125)。为什么? 0x800000是2的完美幂,因此2的幂的浮点乘法几乎总是无损的(除非我们溢出),因为FPU只是将23 << 23添加到其机器表示中(没有触摸尾数,这是出现错误时)。同样,乘法2 * man也是无损的(类似于只将1 << 23添加到机器表示中)。此外,exp和125是整数,因此(exp + 125)也计算精确。

因此,我们只需分析m + e的错误行为,其中1&lt; = m&lt; 1和| e | &LT; 127.在最坏的情况下,m填充了所有23位(对应于m = 2 - 2 -22 )和e = +/- 127.这里,这遗憾的是,它会破坏m的8个最低有效位,因为它必须将m(其指数范围为2 0 )重新规范化为指数范围2 8 ,这意味着丢失8位。但是,由于尾数有24个有效位,因此我们实际上失去了2个 - (24 - 8)的精度,这是错误的上限。

from_bits的类似推理中,您可以证明float(0x800000 + y & 0x7fffff)基本上是在计算操作(1.0f + m),其中m最多可能有23位因此,我们在2 0 的尺度上添加一个精确的数字,而另一个数字的尺度为2 -1 ,所以我们期望失去一位。这表明我们在向后转换中会产生高达2 -22 的相对误差。

这两个变换都会产生非常小的舍入,如果你向to_bits投入一个额外的乘法,你也可以把它的误差降到2 -22

最后的话

不要在生产中这样做。

  1. 您永远不必明确操纵数字的机器表示。
  2. 即使因某种不正当的理由你需要这样做,你也不应该做这个hacky的事情。
  3. 这只是一个看似有趣的聪明的浮动黑客。它并不意味着更多。