TheThingsNetwork / go-cayenne-lib

CayenneLPP in Go
MIT License
3 stars 6 forks source link

encoder: incorrect data for signed floats with negative values on non-amd64 architectures #13

Closed deadprogram closed 8 months ago

deadprogram commented 1 year ago

If you try to use this package on non-Intel architectures to encode any float64 values that are negative, the result for that value is all-zeros.

I think this is an example of the same problem: https://github.com/golang/go/issues/43047

Here is a small test case that shows the problem:

package main

import (
    "bytes"
    "encoding/hex"
)

var buf *bytes.Buffer

func main() {
    buf = new(bytes.Buffer)
    AddGPS(2, 43.43382, -5.865784, 179.0000)
    println(hex.EncodeToString(buf.Bytes()))
}

func AddGPS(channel uint8, latitude, longitude, meters float64) {
    valLat := uint32(latitude * 10000)
    valLon := uint32(longitude * 10000)
    valAlt := uint32(meters * 100)
    buf.WriteByte(channel)
    buf.WriteByte(0x88)
    buf.WriteByte(uint8(valLat >> 16))
    buf.WriteByte(uint8(valLat >> 8))
    buf.WriteByte(uint8(valLat))
    buf.WriteByte(uint8(valLon >> 16))
    buf.WriteByte(uint8(valLon >> 8))
    buf.WriteByte(uint8(valLon))
    buf.WriteByte(uint8(valAlt >> 16))
    buf.WriteByte(uint8(valAlt >> 8))
    buf.WriteByte(uint8(valAlt))
}

On amd64 the results of this are:

028806a0a2ff1adf0045ec

However, on other architectures, such as arm64, the result is

028806a0a20000000045ec

Note the 0 bytes for what should have been an encoded negative number.

The solution is to use a different cast to preserve the sign:

valLat := int32(latitude * 10000)

By making this change the correct result is returned on all architectures:

amd64: 028806a0a2ff1adf0045ec non-amd64: 028806a0a2ff1adf0045ec

Please let me know if you would like me to submit a PR with appropriate fixes/tests.