go语言实现sqrt的方法(重写Quake III卡马克快开平方根)
在3D图形编程中,经常要求平方根或平方根的倒数,例如:求向量的长度或将向量归一化。C数学函数库中的具有理想的精度,但对于3D游戏程式来说速度太慢。我们希望能够在保证足够的精度的同时,进一步提高速度。
根据John Carmack在Quake III源代码中SquareRootFloat,该功能可以计算浮点数的平方根的倒数,比常规的
float SquareRootFloat(float number) { long i; float x, y; const float f = 1.5F; x = number * 0.5F; y = number; i = * ( long * ) &y; i = 0x5f3759df - ( i >> 1 ); y = * ( float * ) &i; y = y * ( f - ( x * y * y ) ); y = y * ( f - ( x * y * y ) ); return number * y; }
由专家数学家(Chris Lomont)开发的一个稍好一点的常数,该常数试图找出原始算法的工作原理:
float InvSqrt(float c) { 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; }
func InvSqrt(x float32) float32 { var xhalf float32 = 0.5 * x // get bits for floating VALUE i := math.Float32bits(x) // gives initial guess y0 i = 0x5f375a86 - (i >> 1) // convert bits BACK to float x = math.Float32frombits(i) // Newton step, repeating increases accuracy x = x * (1.5 - xhalf*x*x) x = x * (1.5 - xhalf*x*x) x = x * (1.5 - xhalf*x*x) return 1 / x }
math.Float32bits 底层使用了unsafe.pointer,进一步改写后变成
func InvSqrt(x float32) float32 { var xhalf float32 = 0.5 * x // get bits for floating VALUE i := *(*uint32)(unsafe.Pointer(&x)) // gives initial guess y0 i = 0x5f375a86 - (i >> 1) // convert bits BACK to float x = *(*float32)(unsafe.Pointer(&i)) // Newton step, repeating increases accuracy x = x * (1.5 - xhalf*x*x) x = x * (1.5 - xhalf*x*x) x = x * (1.5 - xhalf*x*x) return 1 / x }
最后运行3的平方根倒数得到的结果与go自带math.sqrt结果做对比
发表评论: