我正在将数据从 iPhone 传输到从设备,其中传输需要 16 位数据值。现在我有一个需要传输的浮点值,但我怎么知道它有多大,以及它是如何以十六进制表示的?
需要十六进制表示,因为我可能需要切换上下半字节。
我正在将数据从 iPhone 传输到从设备,其中传输需要 16 位数据值。现在我有一个需要传输的浮点值,但我怎么知道它有多大,以及它是如何以十六进制表示的?
需要十六进制表示,因为我可能需要切换上下半字节。
在 iOS 设备上,浮点数与符号位、有偏指数和编码的有效数一起存储。
对于 32 位浮点数 ( float
),指数为 8 位,偏置 127,编码的有效位为 23 位。对于 64 位浮点数 ( double
),指数为 11 位,偏置 1023,编码的有效位为 52 位。
下面介绍 32 位浮点数。它来自记忆;我没有仔细检查过。64 位浮点数类似。
unsigned int E = (union { float f; unsigned int u; }) { F }.u;
考虑一个浮点数F。用 定义 E。在 iOS 设备(和许多其他普通计算机)上,E 将包含 F 的编码,即表示它的位。
让我们成为E>>31
。它是符号位。如果 F 为正(包括 +0)则为 0,如果 F 为负(包括 -0)则为 1。
让 e 成为E>>23 & 0xff
。那是有偏的指数。无偏(实际)指数是 e-127。
设 f 为E & 0x7fffff
。那是编码的有效数字。如果 0 < e < 255,则实际有效位为 1. f 2,其中f是 f 的 23 位,以二进制数字形式写入。因此,如果 f 为 0x600000,则 f 在二进制中为 1100000000000000000000 2,因此实际有效位为 1.1100000000000000000000 2,即 1.11 2,即 1+1/2+1/4 = 1.75。
这种编码表示的数字加起来是 (-1) s •2 e-127 •1。f 2 . 因此,对于编码 0x40600000,s 为 0,e 为 0x100 = 128,f 为 1100000000000000000000 2,因此值为 (-1) 0 •2 128-127 •1.75 = 1•2•1.75 = 3.5。
有一些特殊情况。如果 e 为 0,则实际有效数字为 0. f 2而不是 1. f 2。即隐含的 1 变为 0。注意,如果 f 为零,则表示的值为 0。符号对于浮点零仍然有意义;+0 和 -0 的行为略有不同。
如果 e 为 255 且 f 为 0,则表示的值是无限的,可以是 +infinity 或 -infinity,具体取决于符号。
如果 e 为 255 且 f 不为 0,则该值为 NaN,具有一些实现定义的语义。
要对浮点值进行编码,您需要确定符号,然后计算不大于该值大小的 2 的最大幂。2 的幂为您提供无偏指数。然后将值除以 2 的幂并将其四舍五入以适合有效数字,这就是有效数字。有一些特殊情况,当舍入将有效数推至 2(您必须调整指数)以及当指数大到足以下溢或小到足以下溢到非正规范围(低于偏置指数为 1 或更大)。
如果您想了解详细信息,请在网上查找IEEE 浮点标准。但要以十六进制表示,只需生成数字,获取各个字节,然后生成它们的十六进制表示。您需要知道的两件事是值的长度(例如,sizeof(double)
)以及它是存储“大端”还是“小端”。iOS 设备总是“小端”,这意味着值的最低有效字节具有最低的内存地址。
获取字节的直接方法是创建一个or的 Cunion
和一个适当长度的. 将浮点值存储到联合中,然后检索字节以转换为十六进制。您还可以使用适当指针类型的强制转换来执行此“别名”。float
double
unsigned char
unsigned char
union {
float f;
double d;
unsigned char c[8];
} foo;
foo.d = 3.14156;
for(int i=0;i<8;i++) printf("%02X",foo.c[i]);
对于不能使用联合和别名的 Java 用户,Float 和 Double 上有以下类方法:
Float.floatToRawIntBits(floatValue)
Double.doubleToRawLongBits(doubleValue)
和他们的倒数
Float.intBitsToDouble(intValue)
Double.longBitsToDouble(longValue)
请注意,它们不会进行类似强制转换的转换(例如,4.1 float不会转换为 4 int),而是将位模式不变地从 float 转换为 integer 格式。