在3D图形编程中,经常要求平方根或平方根的倒数,例如:求向量的长度或将向量归一化。C数学函数库中的sqrt具有理想的精度,但对于3D游戏程式来说速度太慢。我们希望能够在保证足够的精度的同时,进一步提高速度。
java版
Carmack在QUAKE3中使用了下面的算法,它第一次在公众场合出现的时候,几乎震住了所有的人。据说该算法其实并不是Carmack发明的,它真正的作者是Nvidia的Gary Tarolli(未经证实)。
//
// 计算参数x的平方根的倒数
//
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#
unsafe版本
/// <summary>
///
/// Carmack在QUAKE3中使用的计算平方根的函数
///
/// 在3D图形编程中,经常要求平方根或平方根的倒数,例如:求向量的长度或将向量归一化。C数学函数库中的sqrt具有理想的精度,但对于3D游戏程式来说速度太慢。我们希望能够在保证足够的精度的同时,进一步提高速度。
/// Carmack在QUAKE3中使用了下面的算法,它第一次在公众场合出现的时候,几乎震住了所有的人。据说该算法其实并不是Carmack发明的,它真正的作者是Nvidia的Gary Tarolli(未经证实)。
///
/// 计算参数x的平方根的倒数
///
/// 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;
/// }
/// http://www.codemaestro.com/reviews/review00000105.html.
/// http://www.beyond3d.com/content/articles/8/
/// </remarks>
public static float InverseSqrtFast(float x)
{
//return OpenGLTK.MathInverseSqrtFast.InverseSqrtFast(x);
unsafe
{
float xhalf = 0.5f * x;
int i = *(int*)&x; // Read bits as integer.
i = 0x5f375a86 - (i >> 1); // Make an initial guess for Newton-Raphson approximation
x = *(float*)&i; // Convert bits back to float
x = x * (1.5f - xhalf * x * x); // Perform left single Newton-Raphson step.
return x;
}
}
不带unsafe版本
///
/// Carmack在QUAKE3中使用的计算平方根的函数
///
/// 在3D图形编程中,经常要求平方根或平方根的倒数,例如:求向量的长度或将向量归一化。C数学函数库中的sqrt具有理想的精度,但对于3D游戏程式来说速度太慢。我们希望能够在保证足够的精度的同时,进一步提高速度。
/// Carmack在QUAKE3中使用了下面的算法,它第一次在公众场合出现的时候,几乎震住了所有的人。据说该算法其实并不是Carmack发明的,它真正的作者是Nvidia的Gary Tarolli(未经证实)。
///
/// 计算参数x的平方根的倒数
///
/// 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# 版 by 大师♂罗莊
public static float InverseSqrtFast(float x)
{
//这个算法依赖于浮点数的内部表示和字节顺序,所以是不具移植性的。如果放到Mac上跑就会挂掉。如果想具备可移植性,还是乖乖用sqrt好了。
float xhalf = 0.5f * x;
int i = 0x5f375a86 - (floatToIntBits(x) >> 1); //MagicNumber:0x5f3759df,0x5f375a86
x = intBitsToFloat(i);
x = x * (1.5f - xhalf * x * x); //Newton-Raphson Method based on Taylor Series
return x;
}
/// <summary>
/// java Float.intBitsToFloat C#版
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
private static float intBitsToFloat(int i)
{
byte[] bytes = BitConverter.GetBytes(i);
float f = BitConverter.ToSingle(bytes, 0);
return f;
}
public static int changingendianness(float x)
{
byte[] bytes = BitConverter.GetBytes(x);
Array.Reverse(bytes);
return BitConverter.ToInt32(bytes, 0);
}
private static int floatToIntBits(float x)
{
byte[] bytes = BitConverter.GetBytes(x);
int i = BitConverter.ToInt32(bytes, 0);
return i;
}
java版
请自行搜索《John Carmark 神奇魔数开方的Java实现 》