是否有可能在C#中编写Quake的快速InvSqrt()函数?

时间:2008-11-06 14:25:12

标签: c#

这只是为了满足我自己的好奇心。

是否有这样的实现:

float InvSqrt (float x)
{
   float xhalf = 0.5f*x;
   int i = *(int*)&x;
   i = 0x5f3759df - (i>>1);
   x = *(float*)&i;
   x = x*(1.5f - xhalf*x*x);
   return x;
}
在C#中

?如果存在,请发布代码。

我想我应该提到我正在寻找一个“安全”的实现......无论哪种方式,BitConverter代码都解决了这个问题。工会的想法很有趣。我会测试它并发布我的结果。

编辑: 正如预期的那样,不安全的方法是最快的,然后使用union(在函数内部),然后是BitConverter。这些函数执行了10000000次,我使用System.Diagnostics.Stopwatch类进行计时。计算结果显示在括号中。

Input: 79.67
BitConverter Method: 00:00:01.2809018 (0.1120187)
Union Method: 00:00:00.6838758 (0.1120187)
Unsafe Method: 00:00:00.3376401 (0.1120187)

为了完整性,我测试了内置的Math.Pow方法和“天真”方法(1 / Sqrt(x))。

Math.Pow(x, -0.5): 00:00:01.7133228 (0.112034710535584)
1 / Math.Sqrt(x): 00:00:00.3757084 (0.1120347)

1 / Math.Sqrt()之间的差异非常小,以至于我认为不需要在C#(或任何其他不安全的方法)中采用不安全的快速InvSqrt()方法。除非一个真的需要从CPU中挤出最后一点果汁... 1 / Math.Sqrt()也更准确。

4 个答案:

答案 0 :(得分:13)

您应该能够使用StructLayout和FieldOffset属性伪造一个简单的旧数据(如浮点数和整数)的联合。

[StructLayout(LayoutKind.Explicit, Size=4)]
private struct IntFloat {
    [FieldOffset(0)]
    public float floatValue;

    [FieldOffset(0)]
    public int intValue;

    // redundant assignment to avoid any complaints about uninitialized members
    IntFloat(int x) {
        floatValue = 0;
        intValue = x;
    }

    IntFloat(float x) { 
        intValue = 0;
        floatValue = x;
    }

    public static explicit operator float (IntFloat x) {
        return x.floatValue;
    }

    public static explicit operator int (IntFloat x) { 
        return x.intValue;
    }

    public static explicit operator IntFloat (int i) {
        return new IntFloat(i);
    }
    public static explicit operator IntFloat (float f) { 
        return new IntFloat(f);
    }
}

然后翻译InvSqrt很容易。

答案 1 :(得分:8)

如果您想避免不安全的代码,请使用BitConverter

float InvSqrt(float x)
{
    float xhalf = 0.5f * x;
    int i = BitConverter.ToInt32(BitConverter.GetBytes(x), 0);
    i = 0x5f3759df - (i >> 1);
    x = BitConverter.ToSingle(BitConverter.GetBytes(i), 0);
    x = x * (1.5f - xhalf * x * x);
    return x;
}

否则,C#代码与您提供的C代码完全相同,只是该方法需要标记为不安全:

unsafe float InvSqrt(float x) { ... }

答案 2 :(得分:6)

绝对可以在不安全模式下使用。请注意,即使在Quake 3源代码中使用常量0x5f3759dfnumerical research showed常量0x5f375a86实际上会产生更好的牛顿近似结果。

答案 3 :(得分:1)

我不明白为什么使用不安全的编译器选项是不可能的。