Golang中float64的位运算操作解析

Golang中float64的位运算操作解析 我读到一篇关于如何快速计算 1/sqrt(x) 的文章。其思路是利用按位对数运算和指针操作(涉及指数和尾数的浮点数表示)。

例如:

var y float64
var i int64
i = *(long *)&y // 邪恶的浮点数位级操作

我不太理解这个操作。当我在 Go Playground 上尝试运行它时,出现了错误:

func main() {
    var y = 2.15
    var i int64
    i = *(int64 *)&y   // 错误:语法错误:遇到 ')',期望表达式
    i = 0x5f3759df - (i>>1)
    y = *(float64*)&i // 错误:语法错误:遇到 ')',期望表达式
    fmt.Println(y)
}

有人可以为我解惑吗?谢谢


更多关于Golang中float64的位运算操作解析的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

你好 Petrus 非常感谢你的帮助。 更重要的是,我从你这里学到了如何在 Go 语言中进行底层的浮点数位操作。 再次非常感谢!

更多关于Golang中float64的位运算操作解析的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


// Fast inverse square root
// https://en.wikipedia.org/wiki/Fast_inverse_square_root

package main

import (
	"fmt"
	"math"
)

func RSqrt(number float64) float64 {
	const threehalfs = 1.5

	x2 := number * 0.5
	y := number
	i := int64(math.Float64bits(y))   // evil floating point bit level hacking
	i = 0x5FE6EB50C7B537A9 - (i >> 1) // what the f@#!?
	y = math.Float64frombits(uint64(i))
	y = y * (threehalfs - (x2 * y * y)) // 1st iteration
	y = y * (threehalfs - (x2 * y * y)) // 2nd iteration
	y = y * (threehalfs - (x2 * y * y)) // 3rd iteration
	y = y * (threehalfs - (x2 * y * y)) // 4th iteration

	return y
}

func main() {
	number := 0.15625
	fmt.Printf("%g\n", number)

	rsqrt := RSqrt(number)
	fmt.Printf("%g\n", rsqrt)

	rsqrt = 1 / math.Sqrt(number)
	fmt.Printf("%g\n", rsqrt)
}

https://play.golang.org/p/o5w-TaqKjZQ

0.15625
2.5298221281347035
2.5298221281347035

在Go语言中,直接进行浮点数的位运算操作需要使用unsafe包和类型转换,因为Go的类型系统比C更严格。你遇到的错误是因为Go不支持C风格的指针类型转换语法。

以下是正确的实现方式:

package main

import (
	"fmt"
	"unsafe"
)

func main() {
	var y float64 = 2.15
	var i int64
	
	// 将float64的位模式转换为int64
	i = *(*int64)(unsafe.Pointer(&y))
	
	// 快速反平方根近似计算(32位版本,这里调整为64位)
	// 注意:原始魔法常数0x5f3759df是针对32位float的
	// 对于64位float,需要不同的常数
	const magic64 = 0x5FE6EB50C7B537A9
	i = magic64 - (i >> 1)
	
	// 将int64的位模式转换回float64
	y = *(*float64)(unsafe.Pointer(&i))
	
	fmt.Printf("近似值: %v\n", y)
	fmt.Printf("精确值: %v\n", 1.0/2.15)
}

对于32位浮点数的快速反平方根算法(原始Quake III算法):

package main

import (
	"fmt"
	"unsafe"
)

func fastInvSqrt32(x float32) float32 {
	const threehalfs float32 = 1.5
	
	i := *(*int32)(unsafe.Pointer(&x))
	i = 0x5f3759df - (i >> 1)
	y := *(*float32)(unsafe.Pointer(&i))
	y = y * (threehalfs - (0.5 * x * y * y))
	
	return y
}

func main() {
	x := float32(4.0)
	result := fastInvSqrt32(x)
	fmt.Printf("1/sqrt(%.1f) ≈ %v\n", x, result)
	fmt.Printf("精确值: %v\n", 1.0/2.0) // sqrt(4)=2, 所以1/sqrt(4)=0.5
}

关键点说明:

  1. Go使用unsafe.Pointer进行指针类型转换
  2. *(*int64)(unsafe.Pointer(&y)) 将float64指针转换为int64指针并解引用
  3. 原始魔法常数0x5f3759df是针对32位float的,64位版本需要不同的常数
  4. 这种操作绕过了类型系统,需要谨慎使用

对于64位浮点数的快速反平方根近似:

func fastInvSqrt64(x float64) float64 {
	const magic64 = 0x5FE6EB50C7B537A9
	const threehalfs = 1.5
	
	i := *(*int64)(unsafe.Pointer(&x))
	i = magic64 - (i >> 1)
	y := *(*float64)(unsafe.Pointer(&i))
	y = y * (threehalfs - (0.5 * x * y * y))
	
	return y
}

注意:这种位操作技巧依赖于特定的浮点数表示(IEEE 754),并且现代CPU通常有专门的指令(如rsqrtss)来实现类似功能,性能可能更好。

回到顶部