Thread-safe floats

The atomic package offers only primitives for integers, but since float32 and float64 are stored in the same data structure that int32 and int64 use, we use them to create an atomic float value.

The trick is to use the math.Floatbits functions to get the representation of a float as an unsigned integer and the math.Floatfrombits functions to transform an unsigned integer to a float. Let's see how this works with a float64:

type f64 uint64

func uf(u uint64) (f float64) { return math.Float64frombits(u) }
func fu(f float64) (u uint64) { return math.Float64bits(f) }

func newF64(f float64) *f64 {
v := f64(fu(f))
return &v
}

func (f *f64) Load() float64 {
return uf(atomic.LoadUint64((*uint64)(f)))
}

func (f *f64) Store(s float64) {
atomic.StoreUint64((*uint64)(f), fu(s))
}

Creating the Add function is a little bit more complicated. We need to get the value with Load, then compare and swap. Since this operation could fail because the load is an atomic operation and compare and swap (CAS) is another, we keep trying it until it succeeds in a loop:

func (f *f64) Add(s float64) float64 {
for {
old := f.Load()
new := old + s
if f.CompareAndSwap(old, new) {
return new
}
}
}

func (f *f64) CompareAndSwap(old, new float64) bool {
return atomic.CompareAndSwapUint64((*uint64)(f), fu(old), fu(new))
}

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.214.155