下面的代码通过一些位黑客执行快速平方根倒数运算。该算法可能是由硅图形在20世纪90年代初开发的,它也出现在了《雷神之锤3》中。更多信息
然而,我从GCC C编译器那里得到以下警告:取消引用类型双关指针将违反严格的别名规则
在这种情况下,我应该使用static_cast
,reinterpret_cast
还是dynamic_cast
?
float InverseSquareRoot(float x)
{
float xhalf = 0.5f*x;
int32_t i = *(int32_t*)&x;
i = 0x5f3759df - (i>>1);
x = *(float*)&i;
x = x*(1.5f - xhalf*x*x);
return x;
}
更新
由于委员会给我的反馈,我不再相信这个答案是正确的。但是我想把它留下来作为参考。我有目的地希望委员会能够纠正这个答案(如果它选择这么做的话)。也就是说,根本没有硬件使这个答案不正确,这只是一个委员会的判断。
我添加一个答案并不是为了反驳公认的答案,而是为了增强它。我相信公认的答案既正确又有效(我刚刚投了赞成票)。然而,我想演示另一种同样正确有效的技术:
float InverseSquareRoot(float x)
{
union
{
float as_float;
int32_t as_int;
};
float xhalf = 0.5f*x;
as_float = x;
as_int = 0x5f3759df - (as_int>>1);
as_float = as_float*(1.5f - xhalf*as_float*as_float);
return as_float;
}
使用clang with optimization at-O3,我编译了plasmacel的代码、R.Martinho Fernandes代码和此代码,并逐行比较了装配线。这三个都是相同的。这是由于编译器选择这样编译它。编译器生成不同的、不完整的代码也同样有效。
这里有几个很好的答案可以解决类型双关语问题。
我想解决“快速反平方根”部分。不要在现代处理器上使用此“技巧”。每个主流向量ISA都有一个专用的硬件指令,为您提供快速的平方根反比。它们中的每一个都比这个经常被复制的小黑客更快,更准确。
这些指令都可以通过内部函数获得,所以它们相对容易使用。在SSE中,您希望使用< code>rsqrtss(内部:< code > _ mm _ rsqrt _ ss());在NEON中,您希望使用< code>vrsqrte(内部:< code > vrsqrte _ f32());在AltiVec中,您希望使用< code>frsqrte。大多数GPU ISAs都有类似的指令。这些估计值可以使用相同的牛顿迭代进行优化,NEON甚至有< code>vrsqrts指令在一条指令中完成部分优化,而无需加载常数。
别管石膏了。使用< code>memcpy。
float xhalf = 0.5f*x;
uint32_t i;
assert(sizeof(x) == sizeof(i));
std::memcpy(&i, &x, sizeof(i));
i = 0x5f375a86 - (i>>1);
std::memcpy(&x, &i, sizeof(i));
x = x*(1.5f - xhalf*x*x);
return x;
原始代码尝试初始化int32_t
,方法是首先通过一个int32_t
pointer访问float
对象,这是违反规则的地方。C样式转换相当于reinterpret_cast
,因此将其更改为reinterpret_cast
不会有太大区别。
使用memcpy时的重要区别是,字节从float
复制到int32_t
中,但
对象永远不会通过
lvalue访问,因为
接受指向void的指针,其内部是“神奇的”,不会违反别名规则。
我今天遇到了一些奇怪的事情。下面的代码编译意外,运行正常。 但这一条给出了编译错误 有人能解释一下这种行为吗?
问题内容: 我正在尝试将a 转换为原始类型,反之亦然: 奇怪的是,当我尝试将新分配的分配回a时,结果不过是垃圾。 但是,当我使用原始数据类型作为参数时,似乎相同的算法工作得很好。 问题答案: 改用这些。
这样一个看似简单的数字怎么会“太大”而无法在64位内存中表达呢?
问题内容: 我正在寻找一种将Int的位值强制转换为UInt的方法,反之亦然。例如(为了简单起见,使用8位整数),我想实现以下目标: 首先,我提出了以下解决方案: 但是Apple在“ unsafeBitCast()”文档中指出以下内容: ..注意::破坏了Swift的类型系统的保证;使用时要格外小心。几乎总是有更好的方法来做任何事情。 有谁有更好的方法? 问题答案: 你可以做: 存在许多类似的初始化
据我所知,Closeable接口大约是从Java1.5开始的,而AutoCloseable是在Java1.7中引入的。 我想理解的是为什么Closeable扩展了AutoCloseable而不是相反?这样做是不是因为向后依赖(不能更改可关闭接口),即AutoCloseable需要比Closeable有更广泛的例外?还是我的逻辑错了,事情应该是这样的?
因此,当我编写java文件时: 然后在windows cmd中,我以以下方式编译: 很好,结果是: “严肃的业务逻辑。” 在Netbeans中创建项目时,会添加以下行: 我不能在cmd中编译,只能在IDE中编译。我尝试过manifest.txt,UTF8编码没有BOM,加上文件的换行符。 显示txt: “jar cvfm Program.jar Manifest.txt Program.class