2008-11-06 2 views
30

Это просто для удовлетворения моего любопытства.Возможно ли записать функцию быстрого доступа Quake QuS в 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 решает проблему. Идея союза интересна. Я проверю и опубликую результаты.

Редактировать: Как и ожидалось, небезопасный метод является самым быстрым, за которым следует объединение (внутри функции), за которым следует битконвертер. Функции выполнялись 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 (х)).

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

Разница между 1/Math.Sqrt() настолько мала, что я не думаю, что нужно прибегнуть к методу небезопасный Fast InvSqrt() в C# (или любого другого опасного метода). Если один действительно должен выжать этот последний бит сока из CPU ... 1/Math.Sqrt() также намного точнее.

+0

Для полноты вы должны запустить тест, используя также «1/math.Sqrt()». – 2008-11-06 15:38:15

+0

Я сделал, и еще один сценарий. Я буду обновлять тесты, как только смогу проверить результаты. – ilitirit 2008-11-06 15:46:37

+0

Думаю, вам нужно было запустить тесты с большим набором, чтобы они заняли несколько секунд. – Gleno 2011-09-25 05:47:43

ответ

13

Вы должны быть в состоянии использовать StructLayout и Аргументы FieldOffset для подделки объединения для простых старых данных, таких как float и ints.

[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

Я не понимаю, почему это невозможно, используя опцию небезопасного компилятора.

+0

http://msdn.microsoft.com/en-us/library/chfa2zb8(VS.80).aspx – VVS 2008-11-06 14:41:30

7

Используйте 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) { ... } 
6

Определенно возможно в небезопасном режиме. Обратите внимание, что, хотя в исходном коде Quake 3 была использована константа 0x5f3759df, numerical research showed, константа 0x5f375a86 действительно дает лучшие результаты для аппроксимаций Ньютона.

Смежные вопросы