[英]What does line is supposed to mean?
float sqrt_approx(float z) {
int val_int = *(int*)&z; /* Same bits, but as an int */
/*
* To justify the following code, prove that
*
* ((((val_int / 2^m) - b) / 2) + b) * 2^m = ((val_int - 2^m) / 2) + ((b + 1) / 2) * 2^m)
*
* where
*
* b = exponent bias
* m = number of mantissa bits
*
* .
*/
val_int -= 1 << 23; /* Subtract 2^m. */
val_int >>= 1; /* Divide by 2. */
val_int += 1 << 29; /* Add ((b + 1) / 2) * 2^m. */
return *(float*)&val_int; /* Interpret again as float */
}
我正在阅读有关计算平方根的方法的 wiki 文章。 我来到了这段代码,并在这一行加注了星标。
int val_int = *(int*)&z; /* Same bits, but as an int */
为什么他们将 z 转换为 int 指针然后取消引用它? 为什么不直接说val_int = z;
为什么要使用指针? PS:我是初学者。
这称为类型双关语。 这种特殊用法违反了严格的别名规则
通过获取浮点值 z 的地址,并将其重新解释为 integer 值的地址,作者试图访问表示此浮点数的内存字节,但方便的是int
。
它与 int val_int = z; 不同。 这会将浮点值转换为 integer,从而导致 memory 中的位不同。
除了严格的别名问题之外,这里的一个大问题是代码对任何目标系统上的int
大小和字节序进行了假设。 因此,代码不可移植。
访问z
字节的正确方法是char
数组:
const uint8_t* zb = (const uint8_t*)&z;
然后,您可以使用特定的字节顺序从这些构造一个适当大小的 integer :
uint32_t int_val = ((uint32_t)zb[0]) |
(((uint32_t)zb[1]) << 8) |
(((uint32_t)zb[2]) << 16) |
(((uint32_t)zb[3]) << 24);
这类似于一个更简单的调用,假设您使用的是 little-endian 系统:
uint32_t int_val;
memcpy(&int_val, &z, sizeof(int_val));
但这不是全貌,因为float
字节序是标准化的(至少,假设您的代码针对的是IEEE 754 ),而int
是系统相关的。
至此,整个例子就崩溃了。 在基本层面上,原始代码是(假设)基于技巧的快速近似。 如果您想“正确”地执行这些技巧,那会变得有些混乱。
发生的情况是int val_int = *(int*)&z
浮点数的位重新解释为整数或更确切地说是位域,并直接对浮点数的符号、尾数和指数进行操作,而不是依赖于处理器的操作。
int val_int = z
将应用从 float 到 int 的转换——一个完全不同的操作。
通常,不建议此类操作,因为在不同的平台上,尾数、指数和符号的解释和位置可能有不同的约定。 int
也可能具有不同的大小。 此外,最可以肯定的是,本地操作更高效、更可靠。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.