gofloating-pointadditionatomic

Go atomic.AddFloat32()


I need a function to atomically add float32 values in Go. This is what came up with based on some C code I found:

package atomic

import (
    "sync/atomic"
    "unsafe"
    "math"
)

func AddFloat32(addr *float32, delta float32) (new float32) {
    unsafeAddr := (*uint32)(unsafe.Pointer(addr))

    for {
        oldValue := math.Float32bits(*addr)
        new       = *addr + delta
        newValue := math.Float32bits(new)

        if atomic.CompareAndSwapUint32(unsafeAddr, oldValue, newValue) {
            return
        }
    }
}

Should it work (i.e really be atomic)? Is there a better/faster way to do it in Go?


Solution

  • Look for some code from the Go standard library to adapt. For example, from go/src/sync/atomic/64bit_arm.go,

    func addUint64(val *uint64, delta uint64) (new uint64) {
        for {
            old := *val
            new = old + delta
            if CompareAndSwapUint64(val, old, new) {
                break
            }
        }
        return
    }
    

    For float32 that becomes,

    package main
    
    import (
        "fmt"
        "math"
        "sync/atomic"
        "unsafe"
    )
    
    func AddFloat32(val *float32, delta float32) (new float32) {
        for {
            old := *val
            new = old + delta
            if atomic.CompareAndSwapUint32(
                (*uint32)(unsafe.Pointer(val)),
                math.Float32bits(old),
                math.Float32bits(new),
            ) {
                break
            }
        }
        return
    }
    
    func main() {
        val, delta := float32(math.Pi), float32(math.E)
        fmt.Println(val, delta, val+delta)
        new := AddFloat32(&val, delta)
        fmt.Println(val, new)
    }
    

    Output:

    3.1415927 2.7182817 5.8598747
    5.8598747 5.8598747