Подтвердить что ты не робот

Можно ли написать Quake fast InvSqrt() в С#?

Это просто для удовлетворения моего любопытства.

Есть ли реализация этого:

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;
}

в С#? Если он существует, отправьте код.

Думаю, я должен был упомянуть, что искал "безопасную" реализацию... В любом случае, код 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() настолько мала, что я не думаю, что нужно использовать метод Unsafe Fast InvSqrt() в С# (или любой другой небезопасный метод). Если действительно не нужно выжимать последний бит сока из CPU... 1/Math.Sqrt() также намного точнее.

4b9b3361

Ответ 1

Вы должны иметь возможность использовать атрибуты 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 легко.

Ответ 2

Используйте 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, который вы указали, за исключением того, что метод должен быть помечен как небезопасный:

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

Ответ 4

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