我编写了一个从某些设备读取数据的套接字服务器。读取数据后,二进制移位应用于字节。之后,我得到一个整数值,例如repaint()
,我想将此数字转换为IEEE 754 float 35.844417572021484。我在struct.unpack中找到了一些解决方案,但在我看来并不合理。首先我们将数字转换为字符串,然后转换为float。
Java中是否有像1108304047
这样的简短方法。
我在struct.unpack中找到的解决方案很长。它包含字符串转换,子字符串提取,零填充等。
Float.intBitsToFloat(1108304047)
答案 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)
ldexp
和frexp
分解为正数。如果您可以使用最多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 。
不要在生产中这样做。
这只是一个看似有趣的聪明的浮动黑客。它并不意味着更多。