gnolang / gno

Gno: An interpreted, stack-based Go virtual machine to build succinct and composable apps + Gno.land: a blockchain for timeless code and fair open-source
https://gno.land/
Other
842 stars 343 forks source link

Test and document floating point determinsm #312

Open jaekwon opened 1 year ago

jaekwon commented 1 year ago

https://github.com/gnolang/gno/pull/306 introduced floating point and decimals, but it isn't clear if it actually is deterministic across go versions and architectures. This Issue should remain open until genesis, to ensure that the genesis validators can stay in sync.

ajnavarro commented 9 months ago

I Did some research, and Go is using the IEEE 754 standard for floats:

Also, I tried with different CPU architectures and go versions checking the binary representation, which was always the same.

Protobuf, and by extension amino, are encoding floats using little-endian.

thehowl commented 7 months ago

To provide some context on this issue, see this article.

We solve a lot of headaches by not allowing float multiply-accumulate within Gno already. That seems to be a great cause of pain in floating point determinism, particularly because x86 seems to have 80-bit precision[^1] for intermediate results.

In order to make this issue actionable, I propose the following tasks:

[^1]: See http://yosefk.com/blog/consistency-how-to-defeat-the-purpose-of-ieee-floating-point.html.

thehowl commented 7 months ago

So, working on #1153, go 1.21 modifies this function and adds this interesting comment:

func isOddInt(x float64) bool {
    if Abs(x) >= (1 << 53) {
        // 1 << 53 is the largest exact integer in the float64 format.
        // Any number outside this range will be truncated before the decimal point and therefore will always be
        // an even integer.
        // Without this check and if x overflows int64 the int64(xi) conversion below may produce incorrect results
        // on some architectures (and does so on arm64). See issue #57465.
        return false
    }

    xi, xf := Modf(x)
    return xf == 0 && int64(xi)&1 == 1
}

I'm slowly starting to think that if we want to have floating points in Gno, they should have a software implementation which uses no hardware floating points under the hood...