From b4cfed3b0a2a61720d6241c6636e82599ab4b598 Mon Sep 17 00:00:00 2001 From: Morgan Bazalgette Date: Fri, 27 Sep 2024 12:31:22 +0200 Subject: [PATCH 1/6] feat: add software floating point package --- gnovm/pkg/gnolang/internal/softfloat/copy.sh | 32 + .../internal/softfloat/runtime_softfloat64.go | 631 ++++++++++++++++++ .../softfloat/runtime_softfloat64_test.go | 204 ++++++ .../gnolang/internal/softfloat/softfloat.go | 61 ++ 4 files changed, 928 insertions(+) create mode 100644 gnovm/pkg/gnolang/internal/softfloat/copy.sh create mode 100644 gnovm/pkg/gnolang/internal/softfloat/runtime_softfloat64.go create mode 100644 gnovm/pkg/gnolang/internal/softfloat/runtime_softfloat64_test.go create mode 100644 gnovm/pkg/gnolang/internal/softfloat/softfloat.go diff --git a/gnovm/pkg/gnolang/internal/softfloat/copy.sh b/gnovm/pkg/gnolang/internal/softfloat/copy.sh new file mode 100644 index 00000000000..89566cbb6fc --- /dev/null +++ b/gnovm/pkg/gnolang/internal/softfloat/copy.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +# softfloat64.go: +# - add header +# - change package name +cat > runtime_softfloat64.go << EOF +// Code generated by copy.sh. DO NOT EDIT. +// This file is copied from \$GOROOT/src/runtime/softfloat64.go. +// It is the software floating point implementation used by the Go runtime. + +EOF +cat "$GOROOT/src/runtime/softfloat64.go" >> ./runtime_softfloat64.go +sed -i 's/^package runtime$/package softfloat/' runtime_softfloat64.go + +# softfloat64_test.go: +# - add header +# - change package name +# - change import to right package +# - change GOARCH to runtime.GOARCH, and import the "runtime" package +cat > runtime_softfloat64_test.go << EOF +// Code generated by copy.sh. DO NOT EDIT. +// This file is copied from \$GOROOT/src/runtime/softfloat64_test.go. +// It is the tests for the software floating point implementation +// used by the Go runtime. + +EOF +cat "$GOROOT/src/runtime/softfloat64_test.go" >> ./runtime_softfloat64_test.go +sed -i 's/^package runtime_test$/package softfloat_test/ +s#^\t\. "runtime"$#\t. "github.com/gnolang/gno/gnovm/pkg/gnolang/internal/softfloat"# +s/GOARCH/runtime.GOARCH/g +16a\ + "runtime"' runtime_softfloat64_test.go diff --git a/gnovm/pkg/gnolang/internal/softfloat/runtime_softfloat64.go b/gnovm/pkg/gnolang/internal/softfloat/runtime_softfloat64.go new file mode 100644 index 00000000000..cf2ad5afd8a --- /dev/null +++ b/gnovm/pkg/gnolang/internal/softfloat/runtime_softfloat64.go @@ -0,0 +1,631 @@ +// Code generated by copy.sh. DO NOT EDIT. +// This file is copied from $GOROOT/src/runtime/softfloat64.go. +// It is the software floating point implementation used by the Go runtime. + +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Software IEEE754 64-bit floating point. +// Only referred to (and thus linked in) by softfloat targets +// and by tests in this directory. + +package softfloat + +const ( + mantbits64 uint = 52 + expbits64 uint = 11 + bias64 = -1<<(expbits64-1) + 1 + + nan64 uint64 = (1<>mantbits64) & (1<>mantbits32) & (1<= 4<>= 1 + exp++ + } + if mant >= 2<= 4<>= 1 + exp++ + } + } + mant >>= 1 + exp++ + } + if exp >= 1<>= 1 + exp++ + } + if mant&1 != 0 && (trunc != 0 || mant&2 != 0) { + mant++ + } + mant >>= 1 + exp++ + if mant < 1<= 4<>= 1 + exp++ + } + if mant >= 2<= 4<>= 1 + exp++ + } + } + mant >>= 1 + exp++ + } + if exp >= 1<>= 1 + exp++ + } + if mant&1 != 0 && (trunc != 0 || mant&2 != 0) { + mant++ + } + mant >>= 1 + exp++ + if mant < 1<>= shift + if fs == gs { + fm += gm + } else { + fm -= gm + if trunc != 0 { + fm-- + } + } + if fm == 0 { + fs = 0 + } + return fpack64(fs, fm, fe-2, trunc) +} + +func fsub64(f, g uint64) uint64 { + return fadd64(f, fneg64(g)) +} + +func fneg64(f uint64) uint64 { + return f ^ (1 << (mantbits64 + expbits64)) +} + +func fmul64(f, g uint64) uint64 { + fs, fm, fe, fi, fn := funpack64(f) + gs, gm, ge, gi, gn := funpack64(g) + + // Special cases. + switch { + case fn || gn: // NaN * g or f * NaN = NaN + return nan64 + + case fi && gi: // Inf * Inf = Inf (with sign adjusted) + return f ^ gs + + case fi && gm == 0, fm == 0 && gi: // 0 * Inf = Inf * 0 = NaN + return nan64 + + case fm == 0: // 0 * x = 0 (with sign adjusted) + return f ^ gs + + case gm == 0: // x * 0 = 0 (with sign adjusted) + return g ^ fs + } + + // 53-bit * 53-bit = 107- or 108-bit + lo, hi := mullu(fm, gm) + shift := mantbits64 - 1 + trunc := lo & (1<>shift + return fpack64(fs^gs, mant, fe+ge-1, trunc) +} + +func fdiv64(f, g uint64) uint64 { + fs, fm, fe, fi, fn := funpack64(f) + gs, gm, ge, gi, gn := funpack64(g) + + // Special cases. + switch { + case fn || gn: // NaN / g = f / NaN = NaN + return nan64 + + case fi && gi: // ±Inf / ±Inf = NaN + return nan64 + + case !fi && !gi && fm == 0 && gm == 0: // 0 / 0 = NaN + return nan64 + + case fi, !gi && gm == 0: // Inf / g = f / 0 = Inf + return fs ^ gs ^ inf64 + + case gi, fm == 0: // f / Inf = 0 / g = Inf + return fs ^ gs ^ 0 + } + _, _, _, _ = fi, fn, gi, gn + + // 53-bit<<54 / 53-bit = 53- or 54-bit. + shift := mantbits64 + 2 + q, r := divlu(fm>>(64-shift), fm<> 32) + if fi { + return fs32 ^ inf32 + } + const d = mantbits64 - mantbits32 - 1 + return fpack32(fs32, uint32(fm>>d), fe-1, uint32(fm&(1< gs: // f < 0, g > 0 + return -1, false + + case fs < gs: // f > 0, g < 0 + return +1, false + + // Same sign, not NaN. + // Can compare encodings directly now. + // Reverse for sign. + case fs == 0 && f < g, fs != 0 && f > g: + return -1, false + + case fs == 0 && f > g, fs != 0 && f < g: + return +1, false + } + + // f == g + return 0, false +} + +func f64toint(f uint64) (val int64, ok bool) { + fs, fm, fe, fi, fn := funpack64(f) + + switch { + case fi, fn: // NaN + return 0, false + + case fe < -1: // f < 0.5 + return 0, false + + case fe > 63: // f >= 2^63 + if fs != 0 && fm == 0 { // f == -2^63 + return -1 << 63, true + } + if fs != 0 { + return 0, false + } + return 0, false + } + + for fe > int(mantbits64) { + fe-- + fm <<= 1 + } + for fe < int(mantbits64) { + fe++ + fm >>= 1 + } + val = int64(fm) + if fs != 0 { + val = -val + } + return val, true +} + +func fintto64(val int64) (f uint64) { + fs := uint64(val) & (1 << 63) + mant := uint64(val) + if fs != 0 { + mant = -mant + } + return fpack64(fs, mant, int(mantbits64), 0) +} +func fintto32(val int64) (f uint32) { + fs := uint64(val) & (1 << 63) + mant := uint64(val) + if fs != 0 { + mant = -mant + } + // Reduce mantissa size until it fits into a uint32. + // Keep track of the bits we throw away, and if any are + // nonzero or them into the lowest bit. + exp := int(mantbits32) + var trunc uint32 + for mant >= 1<<32 { + trunc |= uint32(mant) & 1 + mant >>= 1 + exp++ + } + + return fpack32(uint32(fs>>32), uint32(mant), exp, trunc) +} + +// 64x64 -> 128 multiply. +// adapted from hacker's delight. +func mullu(u, v uint64) (lo, hi uint64) { + const ( + s = 32 + mask = 1<> s + v0 := v & mask + v1 := v >> s + w0 := u0 * v0 + t := u1*v0 + w0>>s + w1 := t & mask + w2 := t >> s + w1 += u0 * v1 + return u * v, u1*v1 + w2 + w1>>s +} + +// 128/64 -> 64 quotient, 64 remainder. +// adapted from hacker's delight +func divlu(u1, u0, v uint64) (q, r uint64) { + const b = 1 << 32 + + if u1 >= v { + return 1<<64 - 1, 1<<64 - 1 + } + + // s = nlz(v); v <<= s + s := uint(0) + for v&(1<<63) == 0 { + s++ + v <<= 1 + } + + vn1 := v >> 32 + vn0 := v & (1<<32 - 1) + un32 := u1<>(64-s) + un10 := u0 << s + un1 := un10 >> 32 + un0 := un10 & (1<<32 - 1) + q1 := un32 / vn1 + rhat := un32 - q1*vn1 + +again1: + if q1 >= b || q1*vn0 > b*rhat+un1 { + q1-- + rhat += vn1 + if rhat < b { + goto again1 + } + } + + un21 := un32*b + un1 - q1*v + q0 := un21 / vn1 + rhat = un21 - q0*vn1 + +again2: + if q0 >= b || q0*vn0 > b*rhat+un0 { + q0-- + rhat += vn1 + if rhat < b { + goto again2 + } + } + + return q1*b + q0, (un21*b + un0 - q0*v) >> s +} + +func fadd32(x, y uint32) uint32 { + return f64to32(fadd64(f32to64(x), f32to64(y))) +} + +func fmul32(x, y uint32) uint32 { + return f64to32(fmul64(f32to64(x), f32to64(y))) +} + +func fdiv32(x, y uint32) uint32 { + // TODO: are there double-rounding problems here? See issue 48807. + return f64to32(fdiv64(f32to64(x), f32to64(y))) +} + +func feq32(x, y uint32) bool { + cmp, nan := fcmp64(f32to64(x), f32to64(y)) + return cmp == 0 && !nan +} + +func fgt32(x, y uint32) bool { + cmp, nan := fcmp64(f32to64(x), f32to64(y)) + return cmp >= 1 && !nan +} + +func fge32(x, y uint32) bool { + cmp, nan := fcmp64(f32to64(x), f32to64(y)) + return cmp >= 0 && !nan +} + +func feq64(x, y uint64) bool { + cmp, nan := fcmp64(x, y) + return cmp == 0 && !nan +} + +func fgt64(x, y uint64) bool { + cmp, nan := fcmp64(x, y) + return cmp >= 1 && !nan +} + +func fge64(x, y uint64) bool { + cmp, nan := fcmp64(x, y) + return cmp >= 0 && !nan +} + +func fint32to32(x int32) uint32 { + return fintto32(int64(x)) +} + +func fint32to64(x int32) uint64 { + return fintto64(int64(x)) +} + +func fint64to32(x int64) uint32 { + return fintto32(x) +} + +func fint64to64(x int64) uint64 { + return fintto64(x) +} + +func f32toint32(x uint32) int32 { + val, _ := f64toint(f32to64(x)) + return int32(val) +} + +func f32toint64(x uint32) int64 { + val, _ := f64toint(f32to64(x)) + return val +} + +func f64toint32(x uint64) int32 { + val, _ := f64toint(x) + return int32(val) +} + +func f64toint64(x uint64) int64 { + val, _ := f64toint(x) + return val +} + +func f64touint64(x uint64) uint64 { + var m uint64 = 0x43e0000000000000 // float64 1<<63 + if fgt64(m, x) { + return uint64(f64toint64(x)) + } + y := fadd64(x, -m) + z := uint64(f64toint64(y)) + return z | (1 << 63) +} + +func f32touint64(x uint32) uint64 { + var m uint32 = 0x5f000000 // float32 1<<63 + if fgt32(m, x) { + return uint64(f32toint64(x)) + } + y := fadd32(x, -m) + z := uint64(f32toint64(y)) + return z | (1 << 63) +} + +func fuint64to64(x uint64) uint64 { + if int64(x) >= 0 { + return fint64to64(int64(x)) + } + // See ../cmd/compile/internal/ssagen/ssa.go:uint64Tofloat + y := x & 1 + z := x >> 1 + z = z | y + r := fint64to64(int64(z)) + return fadd64(r, r) +} + +func fuint64to32(x uint64) uint32 { + if int64(x) >= 0 { + return fint64to32(int64(x)) + } + // See ../cmd/compile/internal/ssagen/ssa.go:uint64Tofloat + y := x & 1 + z := x >> 1 + z = z | y + r := fint64to32(int64(z)) + return fadd32(r, r) +} diff --git a/gnovm/pkg/gnolang/internal/softfloat/runtime_softfloat64_test.go b/gnovm/pkg/gnolang/internal/softfloat/runtime_softfloat64_test.go new file mode 100644 index 00000000000..c57fe08b0ef --- /dev/null +++ b/gnovm/pkg/gnolang/internal/softfloat/runtime_softfloat64_test.go @@ -0,0 +1,204 @@ +// Code generated by copy.sh. DO NOT EDIT. +// This file is copied from $GOROOT/src/runtime/softfloat64_test.go. +// It is the tests for the software floating point implementation +// used by the Go runtime. + +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package softfloat_test + +import ( + "math" + "math/rand" + . "github.com/gnolang/gno/gnovm/pkg/gnolang/internal/softfloat" + "testing" + "runtime" +) + +// turn uint64 op into float64 op +func fop(f func(x, y uint64) uint64) func(x, y float64) float64 { + return func(x, y float64) float64 { + bx := math.Float64bits(x) + by := math.Float64bits(y) + return math.Float64frombits(f(bx, by)) + } +} + +func add(x, y float64) float64 { return x + y } +func sub(x, y float64) float64 { return x - y } +func mul(x, y float64) float64 { return x * y } +func div(x, y float64) float64 { return x / y } + +func TestFloat64(t *testing.T) { + base := []float64{ + 0, + math.Copysign(0, -1), + -1, + 1, + math.NaN(), + math.Inf(+1), + math.Inf(-1), + 0.1, + 1.5, + 1.9999999999999998, // all 1s mantissa + 1.3333333333333333, // 1.010101010101... + 1.1428571428571428, // 1.001001001001... + 1.112536929253601e-308, // first normal + 2, + 4, + 8, + 16, + 32, + 64, + 128, + 256, + 3, + 12, + 1234, + 123456, + -0.1, + -1.5, + -1.9999999999999998, + -1.3333333333333333, + -1.1428571428571428, + -2, + -3, + 1e-200, + 1e-300, + 1e-310, + 5e-324, + 1e-105, + 1e-305, + 1e+200, + 1e+306, + 1e+307, + 1e+308, + } + all := make([]float64, 200) + copy(all, base) + for i := len(base); i < len(all); i++ { + all[i] = rand.NormFloat64() + } + + test(t, "+", add, fop(Fadd64), all) + test(t, "-", sub, fop(Fsub64), all) + if runtime.GOARCH != "386" { // 386 is not precise! + test(t, "*", mul, fop(Fmul64), all) + test(t, "/", div, fop(Fdiv64), all) + } +} + +// 64 -hw-> 32 -hw-> 64 +func trunc32(f float64) float64 { + return float64(float32(f)) +} + +// 64 -sw->32 -hw-> 64 +func to32sw(f float64) float64 { + return float64(math.Float32frombits(F64to32(math.Float64bits(f)))) +} + +// 64 -hw->32 -sw-> 64 +func to64sw(f float64) float64 { + return math.Float64frombits(F32to64(math.Float32bits(float32(f)))) +} + +// float64 -hw-> int64 -hw-> float64 +func hwint64(f float64) float64 { + return float64(int64(f)) +} + +// float64 -hw-> int32 -hw-> float64 +func hwint32(f float64) float64 { + return float64(int32(f)) +} + +// float64 -sw-> int64 -hw-> float64 +func toint64sw(f float64) float64 { + i, ok := F64toint(math.Float64bits(f)) + if !ok { + // There's no right answer for out of range. + // Match the hardware to pass the test. + i = int64(f) + } + return float64(i) +} + +// float64 -hw-> int64 -sw-> float64 +func fromint64sw(f float64) float64 { + return math.Float64frombits(Fintto64(int64(f))) +} + +var nerr int + +func err(t *testing.T, format string, args ...any) { + t.Errorf(format, args...) + + // cut errors off after a while. + // otherwise we spend all our time + // allocating memory to hold the + // formatted output. + if nerr++; nerr >= 10 { + t.Fatal("too many errors") + } +} + +func test(t *testing.T, op string, hw, sw func(float64, float64) float64, all []float64) { + for _, f := range all { + for _, g := range all { + h := hw(f, g) + s := sw(f, g) + if !same(h, s) { + err(t, "%g %s %g = sw %g, hw %g\n", f, op, g, s, h) + } + testu(t, "to32", trunc32, to32sw, h) + testu(t, "to64", trunc32, to64sw, h) + testu(t, "toint64", hwint64, toint64sw, h) + testu(t, "fromint64", hwint64, fromint64sw, h) + testcmp(t, f, h) + testcmp(t, h, f) + testcmp(t, g, h) + testcmp(t, h, g) + } + } +} + +func testu(t *testing.T, op string, hw, sw func(float64) float64, v float64) { + h := hw(v) + s := sw(v) + if !same(h, s) { + err(t, "%s %g = sw %g, hw %g\n", op, v, s, h) + } +} + +func hwcmp(f, g float64) (cmp int, isnan bool) { + switch { + case f < g: + return -1, false + case f > g: + return +1, false + case f == g: + return 0, false + } + return 0, true // must be NaN +} + +func testcmp(t *testing.T, f, g float64) { + hcmp, hisnan := hwcmp(f, g) + scmp, sisnan := Fcmp64(math.Float64bits(f), math.Float64bits(g)) + if int32(hcmp) != scmp || hisnan != sisnan { + err(t, "cmp(%g, %g) = sw %v, %v, hw %v, %v\n", f, g, scmp, sisnan, hcmp, hisnan) + } +} + +func same(f, g float64) bool { + if math.IsNaN(f) && math.IsNaN(g) { + return true + } + if math.Copysign(1, f) != math.Copysign(1, g) { + return false + } + return f == g +} diff --git a/gnovm/pkg/gnolang/internal/softfloat/softfloat.go b/gnovm/pkg/gnolang/internal/softfloat/softfloat.go new file mode 100644 index 00000000000..5e4791b79e9 --- /dev/null +++ b/gnovm/pkg/gnolang/internal/softfloat/softfloat.go @@ -0,0 +1,61 @@ +// Package softfloat is a copy of the Go runtime's softfloat64.go file. +// It is a pure software floating point implementation. It can be used to +// perform determinstic, hardware-independent floating point computations. +// +// This package uses shortnames to refer to its different operations. Here is a +// quick reference: +// +// add f + g +// sub f - g +// mul f * g +// div f / g +// neg (- f) +// eq f == g +// gt f > g +// ge f >= g +package softfloat + +// This file mostly exports the functions from runtime_softfloat64.go + +//go:generate sh copy.sh + +func Fadd64(f, g uint64) uint64 { return fadd64(f, g) } +func Fsub64(f, g uint64) uint64 { return fsub64(f, g) } +func Fmul64(f, g uint64) uint64 { return fmul64(f, g) } +func Fdiv64(f, g uint64) uint64 { return fdiv64(f, g) } +func Fneg64(f uint64) uint64 { return fneg64(f) } +func Feq64(f, g uint64) bool { return feq64(f, g) } +func Fgt64(f, g uint64) bool { return fgt64(f, g) } +func Fge64(f, g uint64) bool { return fge64(f, g) } + +func Fadd32(f, g uint32) uint32 { return fadd32(f, g) } +func Fsub32(f, g uint32) uint32 { return fadd32(f, Fneg32(g)) } +func Fmul32(f, g uint32) uint32 { return fmul32(f, g) } +func Fdiv32(f, g uint32) uint32 { return fdiv32(f, g) } +func Feq32(f, g uint32) bool { return feq32(f, g) } +func Fgt32(f, g uint32) bool { return fgt32(f, g) } +func Fge32(f, g uint32) bool { return fge32(f, g) } + +func Fneg32(f uint32) uint32 { + // Not defined in runtime - this is a copy similar to fneg64. + return f ^ (1 << (mantbits32 + expbits32)) +} + +// Conversions + +func Fintto64(val int64) (f uint64) { return fintto64(val) } +func Fintto32(val int64) (f uint32) { return fintto32(val) } + +func F32to64(f uint32) uint64 { return f32to64(f) } +func F32toint32(x uint32) int32 { return f32toint32(x) } +func F32toint64(x uint32) int64 { return f32toint64(x) } +func F32touint64(x uint32) uint64 { return f32touint64(x) } +func F64to32(f uint64) uint32 { return f64to32(f) } +func F64toint(f uint64) (val int64, ok bool) { return f64toint(f) } +func F64toint32(x uint64) int32 { return f64toint32(x) } +func F64toint64(x uint64) int64 { return f64toint64(x) } +func F64touint64(x uint64) uint64 { return f64touint64(x) } +func Fint32to32(x int32) uint32 { return fint32to32(x) } +func Fint32to64(x int32) uint64 { return fint32to64(x) } +func Fint64to32(x int64) uint32 { return fint64to32(x) } +func Fint64to64(x int64) uint64 { return fint64to64(x) } From 5c6ce77e511b5e595b57193ef021d0fabf2993da Mon Sep 17 00:00:00 2001 From: Morgan Bazalgette Date: Wed, 2 Oct 2024 11:10:57 +0200 Subject: [PATCH 2/6] add Fcmp64 --- gnovm/pkg/gnolang/internal/softfloat/softfloat.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gnovm/pkg/gnolang/internal/softfloat/softfloat.go b/gnovm/pkg/gnolang/internal/softfloat/softfloat.go index 5e4791b79e9..96ac86f6053 100644 --- a/gnovm/pkg/gnolang/internal/softfloat/softfloat.go +++ b/gnovm/pkg/gnolang/internal/softfloat/softfloat.go @@ -36,6 +36,8 @@ func Feq32(f, g uint32) bool { return feq32(f, g) } func Fgt32(f, g uint32) bool { return fgt32(f, g) } func Fge32(f, g uint32) bool { return fge32(f, g) } +func Fcmp64(f, g uint64) (cmp int32, isnan bool) { return fcmp64(f, g) } + func Fneg32(f uint32) uint32 { // Not defined in runtime - this is a copy similar to fneg64. return f ^ (1 << (mantbits32 + expbits32)) From 4fabee430e5e17a1b6d2d853fe4811ca6df37094 Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Sat, 23 Nov 2024 15:36:32 +0100 Subject: [PATCH 3/6] feat: use soft float --- gnovm/pkg/gnolang/gonative.go | 19 +- .../softfloat/runtime_softfloat64_test.go | 14 +- .../gnolang/internal/softfloat/softfloat.go | 114 ++++++++---- gnovm/pkg/gnolang/op_binary.go | 53 +++--- gnovm/pkg/gnolang/values.go | 26 +-- gnovm/pkg/gnolang/values_conversions.go | 167 ++++++++++-------- gnovm/pkg/gnolang/values_conversions_test.go | 3 +- gnovm/tests/files/float8.gno | 77 ++++++++ 8 files changed, 310 insertions(+), 163 deletions(-) create mode 100644 gnovm/tests/files/float8.gno diff --git a/gnovm/pkg/gnolang/gonative.go b/gnovm/pkg/gnolang/gonative.go index fe92f5bcd23..41ff092a417 100644 --- a/gnovm/pkg/gnolang/gonative.go +++ b/gnovm/pkg/gnolang/gonative.go @@ -2,7 +2,10 @@ package gnolang import ( "fmt" + "math" "reflect" + + "github.com/gnolang/gno/gnovm/pkg/gnolang/internal/softfloat" ) // NOTE @@ -379,9 +382,9 @@ func go2GnoValue(alloc *Allocator, rv reflect.Value) (tv TypedValue) { case reflect.Uint64: tv.SetUint64(rv.Uint()) case reflect.Float32: - tv.SetFloat32(float32(rv.Float())) + tv.SetFloat32(softfloat.Float32(math.Float32bits(float32(rv.Float())))) case reflect.Float64: - tv.SetFloat64(rv.Float()) + tv.SetFloat64(softfloat.Float64(math.Float64bits(rv.Float()))) case reflect.Array: tv.V = alloc.NewNative(rv) case reflect.Slice: @@ -478,11 +481,11 @@ func go2GnoValueUpdate(alloc *Allocator, rlm *Realm, lvl int, tv *TypedValue, rv } case Float32Kind: if lvl != 0 { - tv.SetFloat32(float32(rv.Float())) + tv.SetFloat32(softfloat.Float32(math.Float32bits(float32(rv.Float())))) } case Float64Kind: if lvl != 0 { - tv.SetFloat64(rv.Float()) + tv.SetFloat64(softfloat.Float64(math.Float64bits(rv.Float()))) } case BigintKind: panic("not yet implemented") @@ -694,9 +697,9 @@ func go2GnoValue2(alloc *Allocator, store Store, rv reflect.Value, recursive boo case reflect.Uint64: tv.SetUint64(rv.Uint()) case reflect.Float32: - tv.SetFloat32(float32(rv.Float())) + tv.SetFloat32(softfloat.Float32(math.Float32bits(float32(rv.Float())))) case reflect.Float64: - tv.SetFloat64(rv.Float()) + tv.SetFloat64(softfloat.Float64(math.Float64bits(rv.Float()))) case reflect.Array: rvl := rv.Len() if rv.Type().Elem().Kind() == reflect.Uint8 { @@ -1099,9 +1102,9 @@ func gno2GoValue(tv *TypedValue, rv reflect.Value) (ret reflect.Value) { case Uint64Type: rv.SetUint(tv.GetUint64()) case Float32Type: - rv.SetFloat(float64(tv.GetFloat32())) + rv.SetFloat(math.Float64frombits(uint64(tv.GetFloat32()))) case Float64Type: - rv.SetFloat(tv.GetFloat64()) + rv.SetFloat(math.Float64frombits(uint64(tv.GetFloat64()))) default: panic(fmt.Sprintf( "unexpected type %s", diff --git a/gnovm/pkg/gnolang/internal/softfloat/runtime_softfloat64_test.go b/gnovm/pkg/gnolang/internal/softfloat/runtime_softfloat64_test.go index c57fe08b0ef..d746df489f8 100644 --- a/gnovm/pkg/gnolang/internal/softfloat/runtime_softfloat64_test.go +++ b/gnovm/pkg/gnolang/internal/softfloat/runtime_softfloat64_test.go @@ -18,11 +18,11 @@ import ( ) // turn uint64 op into float64 op -func fop(f func(x, y uint64) uint64) func(x, y float64) float64 { +func fop(f func(x, y Float64) Float64) func(x, y float64) float64 { return func(x, y float64) float64 { bx := math.Float64bits(x) by := math.Float64bits(y) - return math.Float64frombits(f(bx, by)) + return math.Float64frombits(uint64(f(Float64(bx), Float64(by)))) } } @@ -97,12 +97,12 @@ func trunc32(f float64) float64 { // 64 -sw->32 -hw-> 64 func to32sw(f float64) float64 { - return float64(math.Float32frombits(F64to32(math.Float64bits(f)))) + return float64(math.Float32frombits(uint32(F64to32(Float64(math.Float64bits(f)))))) } // 64 -hw->32 -sw-> 64 func to64sw(f float64) float64 { - return math.Float64frombits(F32to64(math.Float32bits(float32(f)))) + return math.Float64frombits(uint64(F32to64(Float32(math.Float32bits(float32(f)))))) } // float64 -hw-> int64 -hw-> float64 @@ -117,7 +117,7 @@ func hwint32(f float64) float64 { // float64 -sw-> int64 -hw-> float64 func toint64sw(f float64) float64 { - i, ok := F64toint(math.Float64bits(f)) + i, ok := F64toint(Float64(math.Float64bits(f))) if !ok { // There's no right answer for out of range. // Match the hardware to pass the test. @@ -128,7 +128,7 @@ func toint64sw(f float64) float64 { // float64 -hw-> int64 -sw-> float64 func fromint64sw(f float64) float64 { - return math.Float64frombits(Fintto64(int64(f))) + return math.Float64frombits(uint64(Fintto64(int64(f)))) } var nerr int @@ -187,7 +187,7 @@ func hwcmp(f, g float64) (cmp int, isnan bool) { func testcmp(t *testing.T, f, g float64) { hcmp, hisnan := hwcmp(f, g) - scmp, sisnan := Fcmp64(math.Float64bits(f), math.Float64bits(g)) + scmp, sisnan := Fcmp64(Float64(math.Float64bits(f)), Float64(math.Float64bits(g))) if int32(hcmp) != scmp || hisnan != sisnan { err(t, "cmp(%g, %g) = sw %v, %v, hw %v, %v\n", f, g, scmp, sisnan, hcmp, hisnan) } diff --git a/gnovm/pkg/gnolang/internal/softfloat/softfloat.go b/gnovm/pkg/gnolang/internal/softfloat/softfloat.go index 96ac86f6053..4b1fb7ad20c 100644 --- a/gnovm/pkg/gnolang/internal/softfloat/softfloat.go +++ b/gnovm/pkg/gnolang/internal/softfloat/softfloat.go @@ -15,28 +15,72 @@ // ge f >= g package softfloat +const ( + mask = 0x7FF + shift = 64 - 11 - 1 + bias = 1023 +) + +type Float64 uint64 +type Float32 uint32 + +func Trunc(x Float64) Float64 { + _, _, _, isInf, IsNaN := funpack64(uint64(x)) + if x == 0 || isInf || IsNaN { + return x + } + + d, _ := Modf(x) + return d +} +func Modf(u Float64) (int Float64, frac Float64) { + cmp, _ := Fcmp64(u, 1) + + if cmp < 0 { + cmp, _ := Fcmp64(u, 0) + switch { + case cmp < 0: + int, frac = Modf(Fneg64(u)) + return Fneg64(int), Fneg64(frac) + case cmp == 0: + return u, u // Return -0, -0 when f == -0 + } + return 0, u + } + + e := uint(u>>shift)&mask - bias + + // Keep the top 12+e bits, the integer part; clear the rest. + if e < 64-12 { + u &^= 1<<(64-12-e) - 1 + } + + frac = Fsub64(u, int) + return +} + // This file mostly exports the functions from runtime_softfloat64.go //go:generate sh copy.sh -func Fadd64(f, g uint64) uint64 { return fadd64(f, g) } -func Fsub64(f, g uint64) uint64 { return fsub64(f, g) } -func Fmul64(f, g uint64) uint64 { return fmul64(f, g) } -func Fdiv64(f, g uint64) uint64 { return fdiv64(f, g) } -func Fneg64(f uint64) uint64 { return fneg64(f) } -func Feq64(f, g uint64) bool { return feq64(f, g) } -func Fgt64(f, g uint64) bool { return fgt64(f, g) } -func Fge64(f, g uint64) bool { return fge64(f, g) } - -func Fadd32(f, g uint32) uint32 { return fadd32(f, g) } -func Fsub32(f, g uint32) uint32 { return fadd32(f, Fneg32(g)) } -func Fmul32(f, g uint32) uint32 { return fmul32(f, g) } -func Fdiv32(f, g uint32) uint32 { return fdiv32(f, g) } -func Feq32(f, g uint32) bool { return feq32(f, g) } -func Fgt32(f, g uint32) bool { return fgt32(f, g) } -func Fge32(f, g uint32) bool { return fge32(f, g) } - -func Fcmp64(f, g uint64) (cmp int32, isnan bool) { return fcmp64(f, g) } +func Fadd64(f, g Float64) Float64 { return Float64(fadd64(uint64(f), uint64(g))) } +func Fsub64(f, g Float64) Float64 { return Float64(fsub64(uint64(f), uint64(g))) } +func Fmul64(f, g Float64) Float64 { return Float64(fmul64(uint64(f), uint64(g))) } +func Fdiv64(f, g Float64) Float64 { return Float64(fdiv64(uint64(f), uint64(g))) } +func Fneg64(f Float64) Float64 { return Float64(fneg64(uint64(f))) } +func Feq64(f, g Float64) bool { return feq64(uint64(f), uint64(g)) } +func Fgt64(f, g Float64) bool { return fgt64(uint64(f), uint64(g)) } +func Fge64(f, g Float64) bool { return fge64(uint64(f), uint64(g)) } + +func Fadd32(f, g Float32) Float32 { return Float32(fadd32(uint32(f), uint32(g))) } +func Fsub32(f, g Float32) Float32 { return Float32(fadd32(uint32(f), uint32(Fneg32(uint32(g))))) } +func Fmul32(f, g Float32) Float32 { return Float32(fmul32(uint32(f), uint32(g))) } +func Fdiv32(f, g Float32) Float32 { return Float32(fdiv32(uint32(f), uint32(g))) } +func Feq32(f, g Float32) bool { return feq32(uint32(f), uint32(g)) } +func Fgt32(f, g Float32) bool { return fgt32(uint32(f), uint32(g)) } +func Fge32(f, g Float32) bool { return fge32(uint32(f), uint32(g)) } + +func Fcmp64(f, g Float64) (cmp int32, isnan bool) { return fcmp64(uint64(f), uint64(g)) } func Fneg32(f uint32) uint32 { // Not defined in runtime - this is a copy similar to fneg64. @@ -45,19 +89,21 @@ func Fneg32(f uint32) uint32 { // Conversions -func Fintto64(val int64) (f uint64) { return fintto64(val) } -func Fintto32(val int64) (f uint32) { return fintto32(val) } - -func F32to64(f uint32) uint64 { return f32to64(f) } -func F32toint32(x uint32) int32 { return f32toint32(x) } -func F32toint64(x uint32) int64 { return f32toint64(x) } -func F32touint64(x uint32) uint64 { return f32touint64(x) } -func F64to32(f uint64) uint32 { return f64to32(f) } -func F64toint(f uint64) (val int64, ok bool) { return f64toint(f) } -func F64toint32(x uint64) int32 { return f64toint32(x) } -func F64toint64(x uint64) int64 { return f64toint64(x) } -func F64touint64(x uint64) uint64 { return f64touint64(x) } -func Fint32to32(x int32) uint32 { return fint32to32(x) } -func Fint32to64(x int32) uint64 { return fint32to64(x) } -func Fint64to32(x int64) uint32 { return fint64to32(x) } -func Fint64to64(x int64) uint64 { return fint64to64(x) } +func Fintto64(val int64) Float64 { return Float64(fintto64(val)) } +func Fintto32(val int64) Float32 { return Float32(fintto32(val)) } + +func F32to64(f Float32) Float64 { return Float64(f32to64(uint32(f))) } +func F32toint32(x Float32) int32 { return f32toint32(uint32(x)) } +func F32toint64(x Float32) int64 { return f32toint64(uint32(x)) } +func F32touint64(x Float32) uint64 { return f32touint64(uint32(x)) } +func F64to32(f Float64) Float32 { return Float32(f64to32(uint64(f))) } +func F64toint(f Float64) (val int64, ok bool) { return f64toint(uint64(f)) } +func F64toint32(x Float64) int32 { return f64toint32(uint64(x)) } +func F64toint64(x Float64) int64 { return f64toint64(uint64(x)) } +func F64touint64(x Float64) uint64 { return f64touint64(uint64(x)) } +func Fint32to32(x int32) Float32 { return Float32(fint32to32(x)) } +func Fint32to64(x int32) Float64 { return Float64(fint32to64(x)) } +func Fint64to32(x int64) Float32 { return Float32(fint64to32(x)) } +func Fint64to64(x int64) Float64 { return Float64(fint64to64(x)) } +func Fuint64to32(x uint64) Float32 { return Float32(fuint64to32(x)) } +func Fuint64to64(x uint64) Float64 { return Float64(fuint64to64(x)) } diff --git a/gnovm/pkg/gnolang/op_binary.go b/gnovm/pkg/gnolang/op_binary.go index a541a7da8b5..69c011316f8 100644 --- a/gnovm/pkg/gnolang/op_binary.go +++ b/gnovm/pkg/gnolang/op_binary.go @@ -5,6 +5,7 @@ import ( "math/big" "github.com/cockroachdb/apd/v3" + "github.com/gnolang/gno/gnovm/pkg/gnolang/internal/softfloat" ) // ---------------------------------------- @@ -390,9 +391,11 @@ func isEql(store Store, lv, rv *TypedValue) bool { case Uint64Kind: return (lv.GetUint64() == rv.GetUint64()) case Float32Kind: - return (lv.GetFloat32() == rv.GetFloat32()) // XXX determinism? + cmp, nan := softfloat.Fcmp64(softfloat.Float64(lv.GetFloat32()), softfloat.Float64(rv.GetFloat32())) + return cmp == 0 && !nan case Float64Kind: - return (lv.GetFloat64() == rv.GetFloat64()) // XXX determinism? + cmp, nan := softfloat.Fcmp64(lv.GetFloat64(), rv.GetFloat64()) + return cmp == 0 && !nan case BigintKind: lb := lv.V.(BigintValue).V rb := rv.V.(BigintValue).V @@ -531,9 +534,11 @@ func isLss(lv, rv *TypedValue) bool { case Uint64Kind: return (lv.GetUint64() < rv.GetUint64()) case Float32Kind: - return (lv.GetFloat32() < rv.GetFloat32()) // XXX determinism? + cmp, nan := softfloat.Fcmp64(softfloat.Float64(lv.GetFloat32()), softfloat.Float64(rv.GetFloat32())) + return (cmp < 0) && !nan case Float64Kind: - return (lv.GetFloat64() < rv.GetFloat64()) // XXX determinism? + cmp, nan := softfloat.Fcmp64(lv.GetFloat64(), rv.GetFloat64()) + return (cmp < 0) && !nan case BigintKind: lb := lv.V.(BigintValue).V rb := rv.V.(BigintValue).V @@ -575,9 +580,11 @@ func isLeq(lv, rv *TypedValue) bool { case Uint64Kind: return (lv.GetUint64() <= rv.GetUint64()) case Float32Kind: - return (lv.GetFloat32() <= rv.GetFloat32()) // XXX determinism? + cmp, nan := softfloat.Fcmp64(softfloat.Float64(lv.GetFloat32()), softfloat.Float64(rv.GetFloat32())) + return (cmp <= 0) && !nan case Float64Kind: - return (lv.GetFloat64() <= rv.GetFloat64()) // XXX determinism? + cmp, nan := softfloat.Fcmp64(lv.GetFloat64(), rv.GetFloat64()) + return (cmp <= 0) && !nan case BigintKind: lb := lv.V.(BigintValue).V rb := rv.V.(BigintValue).V @@ -619,9 +626,11 @@ func isGtr(lv, rv *TypedValue) bool { case Uint64Kind: return (lv.GetUint64() > rv.GetUint64()) case Float32Kind: - return (lv.GetFloat32() > rv.GetFloat32()) // XXX determinism? + cmp, nan := softfloat.Fcmp64(softfloat.Float64(lv.GetFloat32()), softfloat.Float64(rv.GetFloat32())) + return (cmp > 0) && !nan case Float64Kind: - return (lv.GetFloat64() > rv.GetFloat64()) // XXX determinism? + cmp, nan := softfloat.Fcmp64(lv.GetFloat64(), rv.GetFloat64()) + return (cmp > 0) && !nan case BigintKind: lb := lv.V.(BigintValue).V rb := rv.V.(BigintValue).V @@ -663,9 +672,11 @@ func isGeq(lv, rv *TypedValue) bool { case Uint64Kind: return (lv.GetUint64() >= rv.GetUint64()) case Float32Kind: - return (lv.GetFloat32() >= rv.GetFloat32()) // XXX determinism? + cmp, nan := softfloat.Fcmp64(softfloat.Float64(lv.GetFloat32()), softfloat.Float64(rv.GetFloat32())) + return (cmp >= 0) && !nan case Float64Kind: - return (lv.GetFloat64() >= rv.GetFloat64()) // XXX determinism? + cmp, nan := softfloat.Fcmp64(lv.GetFloat64(), rv.GetFloat64()) + return (cmp >= 0) && !nan case BigintKind: lb := lv.V.(BigintValue).V rb := rv.V.(BigintValue).V @@ -713,10 +724,10 @@ func addAssign(alloc *Allocator, lv, rv *TypedValue) { lv.SetUint64(lv.GetUint64() + rv.GetUint64()) case Float32Type: // NOTE: gno doesn't fuse *+. - lv.SetFloat32(lv.GetFloat32() + rv.GetFloat32()) // XXX determinism? + lv.SetFloat32(softfloat.Fadd32(lv.GetFloat32(), rv.GetFloat32())) case Float64Type: // NOTE: gno doesn't fuse *+. - lv.SetFloat64(lv.GetFloat64() + rv.GetFloat64()) // XXX determinism? + lv.SetFloat64(softfloat.Fadd64(lv.GetFloat64(), rv.GetFloat64())) case BigintType, UntypedBigintType: lb := lv.GetBigInt() lb = big.NewInt(0).Add(lb, rv.GetBigInt()) @@ -769,10 +780,10 @@ func subAssign(lv, rv *TypedValue) { lv.SetUint64(lv.GetUint64() - rv.GetUint64()) case Float32Type: // NOTE: gno doesn't fuse *+. - lv.SetFloat32(lv.GetFloat32() - rv.GetFloat32()) // XXX determinism? + lv.SetFloat32(softfloat.Fsub32(lv.GetFloat32(), rv.GetFloat32())) case Float64Type: // NOTE: gno doesn't fuse *+. - lv.SetFloat64(lv.GetFloat64() - rv.GetFloat64()) // XXX determinism? + lv.SetFloat64(softfloat.Fsub64(lv.GetFloat64(), rv.GetFloat64())) case BigintType, UntypedBigintType: lb := lv.GetBigInt() lb = big.NewInt(0).Sub(lb, rv.GetBigInt()) @@ -825,10 +836,10 @@ func mulAssign(lv, rv *TypedValue) { lv.SetUint64(lv.GetUint64() * rv.GetUint64()) case Float32Type: // NOTE: gno doesn't fuse *+. - lv.SetFloat32(lv.GetFloat32() * rv.GetFloat32()) // XXX determinism? + lv.SetFloat32(softfloat.Fmul32(lv.GetFloat32(), rv.GetFloat32())) case Float64Type: // NOTE: gno doesn't fuse *+. - lv.SetFloat64(lv.GetFloat64() * rv.GetFloat64()) // XXX determinism? + lv.SetFloat64(softfloat.Fmul64(lv.GetFloat64(), rv.GetFloat64())) case BigintType, UntypedBigintType: lb := lv.GetBigInt() lb = big.NewInt(0).Mul(lb, rv.GetBigInt()) @@ -916,18 +927,16 @@ func quoAssign(lv, rv *TypedValue) *Exception { lv.SetUint64(lv.GetUint64() / rv.GetUint64()) case Float32Type: // NOTE: gno doesn't fuse *+. - if rv.GetFloat32() == 0 { + if cmp, nan := softfloat.Fcmp64(softfloat.F32to64(rv.GetFloat32()), softfloat.Fint32to64(0)); cmp == 0 && !nan { return expt } - lv.SetFloat32(lv.GetFloat32() / rv.GetFloat32()) - // XXX FOR DETERMINISM, PANIC IF NAN. + lv.SetFloat32(softfloat.Fdiv32(lv.GetFloat32(), rv.GetFloat32())) case Float64Type: // NOTE: gno doesn't fuse *+. - if rv.GetFloat64() == 0 { + if cmp, nan := softfloat.Fcmp64(rv.GetFloat64(), 0); cmp == 0 && !nan { return expt } - lv.SetFloat64(lv.GetFloat64() / rv.GetFloat64()) - // XXX FOR DETERMINISM, PANIC IF NAN. + lv.SetFloat64(softfloat.Fdiv64(lv.GetFloat64(), rv.GetFloat64())) case BigintType, UntypedBigintType: if rv.GetBigInt().Sign() == 0 { return expt diff --git a/gnovm/pkg/gnolang/values.go b/gnovm/pkg/gnolang/values.go index 8e27bcbcbdb..1a8badf3444 100644 --- a/gnovm/pkg/gnolang/values.go +++ b/gnovm/pkg/gnolang/values.go @@ -3,7 +3,6 @@ package gnolang import ( "encoding/binary" "fmt" - "math" "math/big" "reflect" "strconv" @@ -12,6 +11,7 @@ import ( "github.com/cockroachdb/apd/v3" + "github.com/gnolang/gno/gnovm/pkg/gnolang/internal/softfloat" "github.com/gnolang/gno/tm2/pkg/crypto" ) @@ -1121,15 +1121,15 @@ func (tv *TypedValue) PrimitiveBytes() (data []byte) { return data case Float32Type: data = make([]byte, 4) - u32 := math.Float32bits(tv.GetFloat32()) + u32 := tv.GetFloat32() binary.LittleEndian.PutUint32( - data, u32) + data, uint32(u32)) return data case Float64Type: data = make([]byte, 8) - u64 := math.Float64bits(tv.GetFloat64()) + u64 := tv.GetFloat64() binary.LittleEndian.PutUint64( - data, u64) + data, uint64(u64)) return data case BigintType: return tv.V.(BigintValue).V.Bytes() @@ -1450,7 +1450,7 @@ func (tv *TypedValue) GetUint64() uint64 { return *(*uint64)(unsafe.Pointer(&tv.N)) } -func (tv *TypedValue) SetFloat32(n float32) { +func (tv *TypedValue) SetFloat32(n softfloat.Float32) { if debug { if tv.T.Kind() != Float32Kind || isNative(tv.T) { panic(fmt.Sprintf( @@ -1458,10 +1458,10 @@ func (tv *TypedValue) SetFloat32(n float32) { tv.T.String())) } } - *(*float32)(unsafe.Pointer(&tv.N)) = n + *(*softfloat.Float32)(unsafe.Pointer(&tv.N)) = n } -func (tv *TypedValue) GetFloat32() float32 { +func (tv *TypedValue) GetFloat32() softfloat.Float32 { if debug { if tv.T != nil && tv.T.Kind() != Float32Kind { panic(fmt.Sprintf( @@ -1469,10 +1469,10 @@ func (tv *TypedValue) GetFloat32() float32 { tv.T.String())) } } - return *(*float32)(unsafe.Pointer(&tv.N)) + return *(*softfloat.Float32)(unsafe.Pointer(&tv.N)) } -func (tv *TypedValue) SetFloat64(n float64) { +func (tv *TypedValue) SetFloat64(n softfloat.Float64) { if debug { if tv.T.Kind() != Float64Kind || isNative(tv.T) { panic(fmt.Sprintf( @@ -1480,10 +1480,10 @@ func (tv *TypedValue) SetFloat64(n float64) { tv.T.String())) } } - *(*float64)(unsafe.Pointer(&tv.N)) = n + *(*softfloat.Float64)(unsafe.Pointer(&tv.N)) = n } -func (tv *TypedValue) GetFloat64() float64 { +func (tv *TypedValue) GetFloat64() softfloat.Float64 { if debug { if tv.T != nil && tv.T.Kind() != Float64Kind { panic(fmt.Sprintf( @@ -1491,7 +1491,7 @@ func (tv *TypedValue) GetFloat64() float64 { tv.T.String())) } } - return *(*float64)(unsafe.Pointer(&tv.N)) + return *(*softfloat.Float64)(unsafe.Pointer(&tv.N)) } func (tv *TypedValue) GetBigInt() *big.Int { diff --git a/gnovm/pkg/gnolang/values_conversions.go b/gnovm/pkg/gnolang/values_conversions.go index df93144b4e7..998286f2293 100644 --- a/gnovm/pkg/gnolang/values_conversions.go +++ b/gnovm/pkg/gnolang/values_conversions.go @@ -8,6 +8,7 @@ import ( "strconv" "github.com/cockroachdb/apd/v3" + "github.com/gnolang/gno/gnovm/pkg/gnolang/internal/softfloat" ) // t cannot be nil or untyped or DataByteType. @@ -163,11 +164,11 @@ GNO_CASE: tv.T = t tv.SetUint64(x) case Float32Kind: - x := float32(tv.GetInt()) // XXX determinism? + x := softfloat.Fintto32(int64(tv.GetInt())) tv.T = t tv.SetFloat32(x) case Float64Kind: - x := float64(tv.GetInt()) // XXX determinism? + x := softfloat.Fintto64(int64(tv.GetInt())) tv.T = t tv.SetFloat64(x) case StringKind: @@ -233,11 +234,11 @@ GNO_CASE: tv.T = t tv.SetUint64(x) case Float32Kind: - x := float32(tv.GetInt8()) // XXX determinism? + x := softfloat.Fint32to32(int32(tv.GetInt8())) tv.T = t tv.SetFloat32(x) case Float64Kind: - x := float64(tv.GetInt8()) // XXX determinism? + x := softfloat.Fint32to64(int32(tv.GetInt8())) tv.T = t tv.SetFloat64(x) case StringKind: @@ -304,11 +305,11 @@ GNO_CASE: tv.T = t tv.SetUint64(x) case Float32Kind: - x := float32(tv.GetInt16()) // XXX determinism? + x := softfloat.Fint32to32(int32(tv.GetInt16())) tv.T = t tv.SetFloat32(x) case Float64Kind: - x := float64(tv.GetInt16()) // XXX determinism? + x := softfloat.Fint32to64(int32(tv.GetInt16())) tv.T = t tv.SetFloat64(x) case StringKind: @@ -379,11 +380,11 @@ GNO_CASE: tv.T = t tv.SetUint64(x) case Float32Kind: - x := float32(tv.GetInt32()) // XXX determinism? + x := softfloat.Fint32to32(tv.GetInt32()) tv.T = t tv.SetFloat32(x) case Float64Kind: - x := float64(tv.GetInt32()) // XXX determinism? + x := softfloat.Fint32to64(tv.GetInt32()) tv.T = t tv.SetFloat64(x) case StringKind: @@ -456,11 +457,11 @@ GNO_CASE: tv.T = t tv.SetUint64(x) case Float32Kind: - x := float32(tv.GetInt64()) // XXX determinism? + x := softfloat.Fint64to32(tv.GetInt64()) tv.T = t tv.SetFloat32(x) case Float64Kind: - x := float64(tv.GetInt64()) // XXX determinism? + x := softfloat.Fint64to64(tv.GetInt64()) tv.T = t tv.SetFloat64(x) case StringKind: @@ -533,11 +534,11 @@ GNO_CASE: tv.T = t tv.SetUint64(x) case Float32Kind: - x := float32(tv.GetUint()) // XXX determinism? + x := softfloat.Fuint64to32(uint64(tv.GetUint())) tv.T = t tv.SetFloat32(x) case Float64Kind: - x := float64(tv.GetUint()) // XXX determinism? + x := softfloat.Fuint64to64(uint64(tv.GetUint())) tv.T = t tv.SetFloat64(x) case StringKind: @@ -602,11 +603,11 @@ GNO_CASE: tv.T = t tv.SetUint64(x) case Float32Kind: - x := float32(tv.GetUint8()) // XXX determinism? + x := softfloat.Fuint64to32(uint64(tv.GetUint8())) tv.T = t tv.SetFloat32(x) case Float64Kind: - x := float64(tv.GetUint8()) // XXX determinism? + x := softfloat.Fuint64to64(uint64(tv.GetUint8())) tv.T = t tv.SetFloat64(x) case StringKind: @@ -673,11 +674,11 @@ GNO_CASE: tv.T = t tv.SetUint64(x) case Float32Kind: - x := float32(tv.GetUint16()) // XXX determinism? + x := softfloat.Fuint64to32(uint64(tv.GetUint16())) tv.T = t tv.SetFloat32(x) case Float64Kind: - x := float64(tv.GetUint16()) // XXX determinism? + x := softfloat.Fuint64to64(uint64(tv.GetUint16())) tv.T = t tv.SetFloat64(x) case StringKind: @@ -746,11 +747,11 @@ GNO_CASE: tv.T = t tv.SetUint64(x) case Float32Kind: - x := float32(tv.GetUint32()) // XXX determinism? + x := softfloat.Fuint64to32(uint64(tv.GetUint32())) tv.T = t tv.SetFloat32(x) case Float64Kind: - x := float64(tv.GetUint32()) // XXX determinism? + x := softfloat.Fuint64to64(uint64(tv.GetUint32())) tv.T = t tv.SetFloat64(x) case StringKind: @@ -825,11 +826,11 @@ GNO_CASE: tv.T = t tv.SetUint64(x) case Float32Kind: - x := float32(tv.GetUint64()) // XXX determinism? + x := softfloat.Fuint64to32(tv.GetUint64()) tv.T = t tv.SetFloat32(x) case Float64Kind: - x := float64(tv.GetUint64()) // XXX determinism? + x := softfloat.Fuint64to64(tv.GetUint64()) tv.T = t tv.SetFloat64(x) case StringKind: @@ -857,7 +858,7 @@ GNO_CASE: return int64(trunc) >= math.MinInt && int64(trunc) <= math.MaxInt }) - x := int(tv.GetFloat32()) // XXX determinism? + x := int(softfloat.F32toint64(tv.GetFloat32())) tv.T = t tv.SetInt(x) case Int8Kind: @@ -872,7 +873,7 @@ GNO_CASE: return int64(trunc) >= math.MinInt8 && int64(trunc) <= math.MaxInt8 }) - x := int8(tv.GetFloat32()) // XXX determinism? + x := int8(softfloat.F32toint32(tv.GetFloat32())) tv.T = t tv.SetInt8(x) case Int16Kind: @@ -887,7 +888,7 @@ GNO_CASE: return int64(trunc) >= math.MinInt16 && int64(trunc) <= math.MaxInt16 }) - x := int16(tv.GetFloat32()) // XXX determinism? + x := int16(softfloat.F32toint32(tv.GetFloat32())) tv.T = t tv.SetInt16(x) case Int32Kind: @@ -902,7 +903,7 @@ GNO_CASE: return int64(trunc) >= math.MinInt32 && int64(trunc) <= math.MaxInt32 }) - x := int32(tv.GetFloat32()) // XXX determinism? + x := int32(softfloat.F32toint32(tv.GetFloat32())) tv.T = t tv.SetInt32(x) case Int64Kind: @@ -913,7 +914,7 @@ GNO_CASE: return val == trunc }) - x := int64(tv.GetFloat32()) // XXX determinism? + x := int64(softfloat.F32toint64(tv.GetFloat32())) tv.T = t tv.SetInt64(x) case UintKind: @@ -928,7 +929,7 @@ GNO_CASE: return trunc >= 0 && trunc <= math.MaxUint }) - x := uint(tv.GetFloat32()) // XXX determinism? + x := uint(softfloat.F32touint64(tv.GetFloat32())) tv.T = t tv.SetUint(x) case Uint8Kind: @@ -943,7 +944,7 @@ GNO_CASE: return int64(trunc) >= 0 && int64(trunc) <= math.MaxUint8 }) - x := uint8(tv.GetFloat32()) // XXX determinism? + x := uint8(softfloat.F32touint64(tv.GetFloat32())) tv.T = t tv.SetUint8(x) case Uint16Kind: @@ -958,7 +959,7 @@ GNO_CASE: return int64(trunc) >= 0 && int64(trunc) <= math.MaxUint16 }) - x := uint16(tv.GetFloat32()) // XXX determinism? + x := uint16(softfloat.F32touint64(tv.GetFloat32())) tv.T = t tv.SetUint16(x) case Uint32Kind: @@ -973,7 +974,7 @@ GNO_CASE: return int64(trunc) >= 0 && int64(trunc) <= math.MaxUint32 }) - x := uint32(tv.GetFloat32()) // XXX determinism? + x := uint32(softfloat.F32touint64(tv.GetFloat32())) tv.T = t tv.SetUint32(x) case Uint64Kind: @@ -988,15 +989,15 @@ GNO_CASE: return trunc >= 0 && trunc <= math.MaxUint }) - x := uint64(tv.GetFloat32()) // XXX determinism? + x := uint64(softfloat.F32touint64(tv.GetFloat32())) tv.T = t tv.SetUint64(x) case Float32Kind: - x := tv.GetFloat32() // XXX determinism? + x := tv.GetFloat32() // ??? tv.T = t tv.SetFloat32(x) case Float64Kind: - x := float64(tv.GetFloat32()) // XXX determinism? + x := softfloat.F32to64(tv.GetFloat32()) // ??? tv.T = t tv.SetFloat64(x) default: @@ -1009,159 +1010,169 @@ GNO_CASE: case IntKind: validate(Float64Kind, IntKind, func() bool { val := tv.GetFloat64() - trunc := math.Trunc(val) + trunc := softfloat.Trunc(val) - if val != trunc { + if cmp, _ := softfloat.Fcmp64(val, trunc); cmp != 0 { return false } - return int64(trunc) >= math.MinInt && int64(trunc) <= math.MaxInt + truncInt64 := softfloat.F64toint64(val) + return truncInt64 >= math.MinInt && truncInt64 <= math.MaxInt }) - x := int(tv.GetFloat64()) // XXX determinism? + x := int(softfloat.F64toint64(tv.GetFloat64())) tv.T = t tv.SetInt(x) case Int8Kind: validate(Float64Kind, Int8Kind, func() bool { val := tv.GetFloat64() - trunc := math.Trunc(val) + trunc := softfloat.Trunc(val) - if val != trunc { + if cmp, _ := softfloat.Fcmp64(val, trunc); cmp != 0 { return false } - return int64(trunc) >= math.MinInt8 && int64(trunc) <= math.MaxInt8 + truncInt64 := softfloat.F64toint64(val) + return truncInt64 >= math.MinInt8 && truncInt64 <= math.MaxInt8 }) - x := int8(tv.GetFloat64()) // XXX determinism? + x := int8(softfloat.F64toint32(tv.GetFloat64())) tv.T = t tv.SetInt8(x) case Int16Kind: validate(Float64Kind, Int16Kind, func() bool { val := tv.GetFloat64() - trunc := math.Trunc(val) + trunc := softfloat.Trunc(val) - if val != trunc { + if cmp, _ := softfloat.Fcmp64(val, trunc); cmp != 0 { return false } - return int64(trunc) >= math.MinInt16 && int64(trunc) <= math.MaxInt16 + truncInt64 := softfloat.F64toint64(val) + return truncInt64 >= math.MinInt16 && truncInt64 <= math.MaxInt16 }) - x := int16(tv.GetFloat64()) // XXX determinism? + x := int16(softfloat.F64toint32(tv.GetFloat64())) tv.T = t tv.SetInt16(x) case Int32Kind: validate(Float64Kind, Int32Kind, func() bool { val := tv.GetFloat64() - trunc := math.Trunc(val) + trunc := softfloat.Trunc(val) - if val != trunc { + if cmp, _ := softfloat.Fcmp64(val, trunc); cmp != 0 { return false } - return int64(trunc) >= math.MinInt32 && int64(trunc) <= math.MaxInt32 + truncInt64 := softfloat.F64toint64(val) + return truncInt64 >= math.MinInt32 && truncInt64 <= math.MaxInt32 }) - x := int32(tv.GetFloat64()) // XXX determinism? + x := softfloat.F64toint32(tv.GetFloat64()) tv.T = t tv.SetInt32(x) case Int64Kind: validate(Float64Kind, Int64Kind, func() bool { val := tv.GetFloat64() - trunc := math.Trunc(val) + trunc := softfloat.Trunc(val) - return val == trunc + cmp, _ := softfloat.Fcmp64(val, trunc) + return cmp == 0 }) - x := int64(tv.GetFloat64()) // XXX determinism? + x := softfloat.F64toint64(tv.GetFloat64()) tv.T = t tv.SetInt64(x) case UintKind: validate(Float64Kind, UintKind, func() bool { val := tv.GetFloat64() - trunc := math.Trunc(val) + trunc := softfloat.Trunc(val) - if val != trunc { + if cmp, _ := softfloat.Fcmp64(val, trunc); cmp != 0 { return false } return trunc >= 0 && trunc <= math.MaxUint }) - x := uint(tv.GetFloat64()) // XXX determinism? + x := uint(softfloat.F64touint64(tv.GetFloat64())) tv.T = t tv.SetUint(x) case Uint8Kind: validate(Float64Kind, Uint8Kind, func() bool { val := tv.GetFloat64() - trunc := math.Trunc(val) + trunc := softfloat.Trunc(val) - if val != trunc { + if cmp, _ := softfloat.Fcmp64(val, trunc); cmp != 0 { return false } - return int64(trunc) >= 0 && int64(trunc) <= math.MaxUint8 + truncInt64 := softfloat.F64toint64(val) + return truncInt64 >= 0 && truncInt64 <= math.MaxUint8 }) - x := uint8(tv.GetFloat64()) // XXX determinism? + x := uint8(softfloat.F64touint64(tv.GetFloat64())) tv.T = t tv.SetUint8(x) case Uint16Kind: validate(Float64Kind, Uint16Kind, func() bool { val := tv.GetFloat64() - trunc := math.Trunc(val) + trunc := softfloat.Trunc(val) - if val != trunc { + if cmp, _ := softfloat.Fcmp64(val, trunc); cmp != 0 { return false } - return int64(trunc) >= 0 && int64(trunc) <= math.MaxUint16 + truncInt64 := softfloat.F64toint64(val) + return truncInt64 >= 0 && truncInt64 <= math.MaxUint16 }) - x := uint16(tv.GetFloat64()) // XXX determinism? + x := uint16(softfloat.F64touint64(tv.GetFloat64())) tv.T = t tv.SetUint16(x) case Uint32Kind: validate(Float64Kind, Uint32Kind, func() bool { val := tv.GetFloat64() - trunc := math.Trunc(val) + trunc := softfloat.Trunc(val) - if val != trunc { + if cmp, _ := softfloat.Fcmp64(val, trunc); cmp != 0 { return false } - return int64(trunc) >= 0 && int64(trunc) <= math.MaxUint32 + truncInt64 := softfloat.F64toint64(val) + return truncInt64 >= 0 && truncInt64 <= math.MaxUint32 }) - x := uint32(tv.GetFloat64()) // XXX determinism? + x := uint32(softfloat.F64touint64(tv.GetFloat64())) tv.T = t tv.SetUint32(x) case Uint64Kind: validate(Float64Kind, Uint64Kind, func() bool { val := tv.GetFloat64() - trunc := math.Trunc(val) + trunc := softfloat.Trunc(val) - if val != trunc { + if cmp, _ := softfloat.Fcmp64(val, trunc); cmp != 0 { return false } - return trunc >= 0 && trunc <= math.MaxUint64 + truncUint64 := softfloat.F64touint64(val) + return truncUint64 >= 0 && truncUint64 <= math.MaxUint64 }) - x := uint64(tv.GetFloat64()) // XXX determinism? + x := softfloat.F64touint64(tv.GetFloat64()) tv.T = t tv.SetUint64(x) case Float32Kind: validate(Float64Kind, Float32Kind, func() bool { - return tv.GetFloat64() <= math.MaxFloat32 + cmp, _ := softfloat.Fcmp64(tv.GetFloat64(), softfloat.Float64(math.Float64bits(float64(math.MaxFloat32)))) + return cmp <= 0 }) - x := float32(tv.GetFloat64()) // XXX determinism? + x := softfloat.F64to32(tv.GetFloat64()) tv.T = t tv.SetFloat32(x) case Float64Kind: - x := tv.GetFloat64() // XXX determinism? + x := tv.GetFloat64() // ??? tv.T = t tv.SetFloat64(x) default: @@ -1481,7 +1492,7 @@ func ConvertUntypedBigintTo(dst *TypedValue, bv BigintValue, t Type) { if f32 == 0 && (acc == big.Below || acc == big.Above) { panic("bigint underflows float32 (too close to zero)") } - dst.SetFloat32(f32) + dst.SetFloat32(softfloat.Float32(math.Float32bits(f32))) return // done case Float64Kind: dst.T = t @@ -1495,7 +1506,7 @@ func ConvertUntypedBigintTo(dst *TypedValue, bv BigintValue, t Type) { if f64 == 0 && (acc == big.Below || acc == big.Above) { panic("bigint underflows float64 (too close to zero)") } - dst.SetFloat64(f64) + dst.SetFloat64(softfloat.Float64(math.Float64bits(f64))) return // done case BigdecKind: dst.T = t @@ -1610,7 +1621,7 @@ func ConvertUntypedBigdecTo(dst *TypedValue, bv BigdecValue, t Type) { dst.T = Float64Type dst.V = nil f, _ := bd.Float64() - dst.SetFloat64(f) + dst.SetFloat64(softfloat.Float64(math.Float64bits(f))) return case IntKind, Int8Kind, Int16Kind, Int32Kind, Int64Kind: fallthrough @@ -1636,7 +1647,7 @@ func ConvertUntypedBigdecTo(dst *TypedValue, bv BigdecValue, t Type) { if math.IsInf(float64(f32), 0) { panic("cannot convert untyped bigdec to float32 -- too close to +-Inf") } - dst.SetFloat32(f32) + dst.SetFloat32(softfloat.Float32(math.Float32bits(f32))) return case Float64Kind: dst.T = t @@ -1648,7 +1659,7 @@ func ConvertUntypedBigdecTo(dst *TypedValue, bv BigdecValue, t Type) { if math.IsInf(f64, 0) { panic("cannot convert untyped bigdec to float64 -- too close to +-Inf") } - dst.SetFloat64(f64) + dst.SetFloat64(softfloat.Float64(math.Float64bits(f64))) return default: panic(fmt.Sprintf( diff --git a/gnovm/pkg/gnolang/values_conversions_test.go b/gnovm/pkg/gnolang/values_conversions_test.go index 5436347733f..efd26dcf831 100644 --- a/gnovm/pkg/gnolang/values_conversions_test.go +++ b/gnovm/pkg/gnolang/values_conversions_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/cockroachdb/apd/v3" + "github.com/gnolang/gno/gnovm/pkg/gnolang/internal/softfloat" "github.com/stretchr/testify/require" ) @@ -23,5 +24,5 @@ func TestConvertUntypedBigdecToFloat(t *testing.T) { ConvertUntypedBigdecTo(dst, bd, typ) - require.Equal(t, float64(0), dst.GetFloat64()) + require.Equal(t, softfloat.Fintto64(0), dst.GetFloat64()) } diff --git a/gnovm/tests/files/float8.gno b/gnovm/tests/files/float8.gno new file mode 100644 index 00000000000..eafae9fbca5 --- /dev/null +++ b/gnovm/tests/files/float8.gno @@ -0,0 +1,77 @@ +package main + +import "math" + +func main() { + var i8 int8 = 127 + var i16 int16 = 32767 + var i32 int32 = 2147483647 + var i64 int64 = 9223372036854775807 + var i int = 9223372036854775807 + var u8 uint8 = 255 + var u16 uint16 = 65535 + var u32 uint32 = 4294967295 + var u64 uint64 = 18446744073709551615 + var f32 float32 = 0x1p127 * (1 + (1 - 0x1p-23)) + var f64 float64 = math.MaxFloat64 + println(f32 / 2) + println(f64 / 2) + println((f32 - 1) + 1) + println((f64 - 1) + 1) + println((f32 / 2) * 2) + println((f64 / 2) * 2) + println(f32 - 1) + println(f64 - 1) + println(float32(i8)) + println(float64(i8)) + println(float32(i16)) + println(float64(i16)) + println(float32(i32)) + println(float64(i32)) + println(float32(i64)) + println(float64(i64)) + println(float32(i)) + println(float64(i)) + println(float32(u8)) + println(float64(u8)) + println(float32(u16)) + println(float64(u16)) + println(float32(u32)) + println(float64(u32)) + println(float32(u64)) + println(float64(u64)) + println(float32(f32)) + // println(float64(f32)) + // println(float32(f64)) + println(float64(f64)) +} + +// Output: +// 1.7014117e+38 +// 8.988465674311579e+307 +// 3.4028235e+38 +// 1.7976931348623157e+308 +// 3.4028235e+38 +// 1.7976931348623157e+308 +// 3.4028235e+38 +// 1.7976931348623157e+308 +// 127 +// 127 +// 32767 +// 32767 +// 2.1474836e+09 +// 2.147483647e+09 +// 9.223372e+18 +// 9.223372036854776e+18 +// 9.223372e+18 +// 9.223372036854776e+18 +// 255 +// 255 +// 65535 +// 65535 +// 4.2949673e+09 +// 4.294967295e+09 +// 1.8446744e+19 +// 1.8446744073709552e+19 +// 3.4028235e+38 +// 1.7976931348623157e+308 From 853a9d3e1a5dcd711ac9265ebed02cc8b9974d22 Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Thu, 28 Nov 2024 22:00:42 +0100 Subject: [PATCH 4/6] feat: small refactoring --- gno.land/pkg/sdk/vm/convert.go | 6 +- gnovm/pkg/gnolang/gonative.go | 4 +- gnovm/pkg/gnolang/op_binary.go | 12 +-- gnovm/pkg/gnolang/op_inc_dec.go | 9 +- gnovm/pkg/gnolang/op_unary.go | 5 +- .../gnolang/{internal => }/softfloat/copy.sh | 2 +- .../softfloat/runtime_softfloat64.go | 0 .../softfloat/runtime_softfloat64_test.go | 4 +- .../{internal => }/softfloat/softfloat.go | 40 +++++--- gnovm/pkg/gnolang/values.go | 2 +- gnovm/pkg/gnolang/values_conversions.go | 94 ++++++++++--------- gnovm/pkg/gnolang/values_conversions_test.go | 2 +- gnovm/tests/files/float8.gno | 8 +- 13 files changed, 110 insertions(+), 78 deletions(-) rename gnovm/pkg/gnolang/{internal => }/softfloat/copy.sh (98%) rename gnovm/pkg/gnolang/{internal => }/softfloat/runtime_softfloat64.go (100%) rename gnovm/pkg/gnolang/{internal => }/softfloat/runtime_softfloat64_test.go (98%) rename gnovm/pkg/gnolang/{internal => }/softfloat/softfloat.go (85%) diff --git a/gno.land/pkg/sdk/vm/convert.go b/gno.land/pkg/sdk/vm/convert.go index cafb6cad67f..b6f3a3de7c1 100644 --- a/gno.land/pkg/sdk/vm/convert.go +++ b/gno.land/pkg/sdk/vm/convert.go @@ -3,11 +3,13 @@ package vm import ( "encoding/base64" "fmt" + "math" "strconv" "strings" "github.com/cockroachdb/apd/v3" gno "github.com/gnolang/gno/gnovm/pkg/gnolang" + "github.com/gnolang/gno/gnovm/pkg/gnolang/softfloat" ) func assertNoPlusPrefix(s string) { @@ -143,11 +145,11 @@ func convertArgToGno(arg string, argT gno.Type) (tv gno.TypedValue) { return case gno.Float32Type: value := convertFloat(arg, 32) - tv.SetFloat32(float32(value)) + tv.SetFloat32(softfloat.Float32(math.Float32bits(float32(value)))) return case gno.Float64Type: value := convertFloat(arg, 64) - tv.SetFloat64(value) + tv.SetFloat64(softfloat.Float64(math.Float64bits(value))) return default: panic(fmt.Sprintf("unexpected primitive type %s", bt.String())) diff --git a/gnovm/pkg/gnolang/gonative.go b/gnovm/pkg/gnolang/gonative.go index a553acffa1c..73f5f3728cb 100644 --- a/gnovm/pkg/gnolang/gonative.go +++ b/gnovm/pkg/gnolang/gonative.go @@ -5,7 +5,7 @@ import ( "math" "reflect" - "github.com/gnolang/gno/gnovm/pkg/gnolang/internal/softfloat" + "github.com/gnolang/gno/gnovm/pkg/gnolang/softfloat" ) // NOTE @@ -1052,7 +1052,7 @@ func gno2GoValue(tv *TypedValue, rv reflect.Value) (ret reflect.Value) { case Uint64Type: rv.SetUint(tv.GetUint64()) case Float32Type: - rv.SetFloat(math.Float64frombits(uint64(tv.GetFloat32()))) + rv.SetFloat(math.Float64frombits(uint64(softfloat.F32to64(tv.GetFloat32())))) case Float64Type: rv.SetFloat(math.Float64frombits(uint64(tv.GetFloat64()))) default: diff --git a/gnovm/pkg/gnolang/op_binary.go b/gnovm/pkg/gnolang/op_binary.go index 8a4ad84bc39..2529207cd2a 100644 --- a/gnovm/pkg/gnolang/op_binary.go +++ b/gnovm/pkg/gnolang/op_binary.go @@ -6,7 +6,7 @@ import ( "math/big" "github.com/cockroachdb/apd/v3" - "github.com/gnolang/gno/gnovm/pkg/gnolang/internal/softfloat" + "github.com/gnolang/gno/gnovm/pkg/gnolang/softfloat" ) // ---------------------------------------- @@ -392,7 +392,7 @@ func isEql(store Store, lv, rv *TypedValue) bool { case Uint64Kind: return (lv.GetUint64() == rv.GetUint64()) case Float32Kind: - cmp, nan := softfloat.Fcmp64(softfloat.Float64(lv.GetFloat32()), softfloat.Float64(rv.GetFloat32())) + cmp, nan := softfloat.Fcmp64(softfloat.F32to64(lv.GetFloat32()), softfloat.F32to64(rv.GetFloat32())) return cmp == 0 && !nan case Float64Kind: cmp, nan := softfloat.Fcmp64(lv.GetFloat64(), rv.GetFloat64()) @@ -535,7 +535,7 @@ func isLss(lv, rv *TypedValue) bool { case Uint64Kind: return (lv.GetUint64() < rv.GetUint64()) case Float32Kind: - cmp, nan := softfloat.Fcmp64(softfloat.Float64(lv.GetFloat32()), softfloat.Float64(rv.GetFloat32())) + cmp, nan := softfloat.Fcmp64(softfloat.F32to64(lv.GetFloat32()), softfloat.F32to64(rv.GetFloat32())) return (cmp < 0) && !nan case Float64Kind: cmp, nan := softfloat.Fcmp64(lv.GetFloat64(), rv.GetFloat64()) @@ -581,7 +581,7 @@ func isLeq(lv, rv *TypedValue) bool { case Uint64Kind: return (lv.GetUint64() <= rv.GetUint64()) case Float32Kind: - cmp, nan := softfloat.Fcmp64(softfloat.Float64(lv.GetFloat32()), softfloat.Float64(rv.GetFloat32())) + cmp, nan := softfloat.Fcmp64(softfloat.F32to64(lv.GetFloat32()), softfloat.F32to64(rv.GetFloat32())) return (cmp <= 0) && !nan case Float64Kind: cmp, nan := softfloat.Fcmp64(lv.GetFloat64(), rv.GetFloat64()) @@ -627,7 +627,7 @@ func isGtr(lv, rv *TypedValue) bool { case Uint64Kind: return (lv.GetUint64() > rv.GetUint64()) case Float32Kind: - cmp, nan := softfloat.Fcmp64(softfloat.Float64(lv.GetFloat32()), softfloat.Float64(rv.GetFloat32())) + cmp, nan := softfloat.Fcmp64(softfloat.F32to64(lv.GetFloat32()), softfloat.F32to64(rv.GetFloat32())) return (cmp > 0) && !nan case Float64Kind: cmp, nan := softfloat.Fcmp64(lv.GetFloat64(), rv.GetFloat64()) @@ -673,7 +673,7 @@ func isGeq(lv, rv *TypedValue) bool { case Uint64Kind: return (lv.GetUint64() >= rv.GetUint64()) case Float32Kind: - cmp, nan := softfloat.Fcmp64(softfloat.Float64(lv.GetFloat32()), softfloat.Float64(rv.GetFloat32())) + cmp, nan := softfloat.Fcmp64(softfloat.F32to64(lv.GetFloat32()), softfloat.F32to64(rv.GetFloat32())) return (cmp >= 0) && !nan case Float64Kind: cmp, nan := softfloat.Fcmp64(lv.GetFloat64(), rv.GetFloat64()) diff --git a/gnovm/pkg/gnolang/op_inc_dec.go b/gnovm/pkg/gnolang/op_inc_dec.go index 7a8a885bcf0..ca1cab55a09 100644 --- a/gnovm/pkg/gnolang/op_inc_dec.go +++ b/gnovm/pkg/gnolang/op_inc_dec.go @@ -5,6 +5,7 @@ import ( "math/big" "github.com/cockroachdb/apd/v3" + "github.com/gnolang/gno/gnovm/pkg/gnolang/softfloat" ) func (m *Machine) doOpInc() { @@ -54,9 +55,9 @@ func (m *Machine) doOpInc() { case Uint64Type: lv.SetUint64(lv.GetUint64() + 1) case Float32Type: - lv.SetFloat32(lv.GetFloat32() + 1) + lv.SetFloat32(softfloat.Fadd32(lv.GetFloat32(), softfloat.Fint32to32(1))) case Float64Type: - lv.SetFloat64(lv.GetFloat64() + 1) + lv.SetFloat64(softfloat.Fadd64(lv.GetFloat64(), softfloat.Fintto64(1))) case BigintType, UntypedBigintType: lb := lv.GetBigInt() lb = big.NewInt(0).Add(lb, big.NewInt(1)) @@ -124,9 +125,9 @@ func (m *Machine) doOpDec() { case Uint64Type: lv.SetUint64(lv.GetUint64() - 1) case Float32Type: - lv.SetFloat32(lv.GetFloat32() - 1) + lv.SetFloat32(softfloat.Fsub32(lv.GetFloat32(), softfloat.Fint32to32(1))) case Float64Type: - lv.SetFloat64(lv.GetFloat64() - 1) + lv.SetFloat64(softfloat.Fsub64(lv.GetFloat64(), softfloat.Fintto64(1))) case BigintType, UntypedBigintType: lb := lv.GetBigInt() lb = big.NewInt(0).Sub(lb, big.NewInt(1)) diff --git a/gnovm/pkg/gnolang/op_unary.go b/gnovm/pkg/gnolang/op_unary.go index 9c330c7f8f1..e0ea3861084 100644 --- a/gnovm/pkg/gnolang/op_unary.go +++ b/gnovm/pkg/gnolang/op_unary.go @@ -5,6 +5,7 @@ import ( "math/big" "github.com/cockroachdb/apd/v3" + "github.com/gnolang/gno/gnovm/pkg/gnolang/softfloat" ) func (m *Machine) doOpUpos() { @@ -46,9 +47,9 @@ func (m *Machine) doOpUneg() { case Uint64Type: xv.SetUint64(-xv.GetUint64()) case Float32Type: - xv.SetFloat32(-xv.GetFloat32()) + xv.SetFloat32(softfloat.Fneg32(xv.GetFloat32())) case Float64Type: - xv.SetFloat64(-xv.GetFloat64()) + xv.SetFloat64(softfloat.Fneg64(xv.GetFloat64())) case UntypedBigintType, BigintType: bv := xv.V.(BigintValue) xv.V = BigintValue{V: new(big.Int).Neg(bv.V)} diff --git a/gnovm/pkg/gnolang/internal/softfloat/copy.sh b/gnovm/pkg/gnolang/softfloat/copy.sh similarity index 98% rename from gnovm/pkg/gnolang/internal/softfloat/copy.sh rename to gnovm/pkg/gnolang/softfloat/copy.sh index 89566cbb6fc..8107002ee9f 100644 --- a/gnovm/pkg/gnolang/internal/softfloat/copy.sh +++ b/gnovm/pkg/gnolang/softfloat/copy.sh @@ -26,7 +26,7 @@ cat > runtime_softfloat64_test.go << EOF EOF cat "$GOROOT/src/runtime/softfloat64_test.go" >> ./runtime_softfloat64_test.go sed -i 's/^package runtime_test$/package softfloat_test/ -s#^\t\. "runtime"$#\t. "github.com/gnolang/gno/gnovm/pkg/gnolang/internal/softfloat"# +s#^\t\. "runtime"$#\t. "github.com/gnolang/gno/gnovm/pkg/gnolang/softfloat"# s/GOARCH/runtime.GOARCH/g 16a\ "runtime"' runtime_softfloat64_test.go diff --git a/gnovm/pkg/gnolang/internal/softfloat/runtime_softfloat64.go b/gnovm/pkg/gnolang/softfloat/runtime_softfloat64.go similarity index 100% rename from gnovm/pkg/gnolang/internal/softfloat/runtime_softfloat64.go rename to gnovm/pkg/gnolang/softfloat/runtime_softfloat64.go diff --git a/gnovm/pkg/gnolang/internal/softfloat/runtime_softfloat64_test.go b/gnovm/pkg/gnolang/softfloat/runtime_softfloat64_test.go similarity index 98% rename from gnovm/pkg/gnolang/internal/softfloat/runtime_softfloat64_test.go rename to gnovm/pkg/gnolang/softfloat/runtime_softfloat64_test.go index d746df489f8..eb0a36269f9 100644 --- a/gnovm/pkg/gnolang/internal/softfloat/runtime_softfloat64_test.go +++ b/gnovm/pkg/gnolang/softfloat/runtime_softfloat64_test.go @@ -10,11 +10,11 @@ package softfloat_test import ( + . "github.com/gnolang/gno/gnovm/pkg/gnolang/softfloat" "math" "math/rand" - . "github.com/gnolang/gno/gnovm/pkg/gnolang/internal/softfloat" + "runtime" "testing" - "runtime" ) // turn uint64 op into float64 op diff --git a/gnovm/pkg/gnolang/internal/softfloat/softfloat.go b/gnovm/pkg/gnolang/softfloat/softfloat.go similarity index 85% rename from gnovm/pkg/gnolang/internal/softfloat/softfloat.go rename to gnovm/pkg/gnolang/softfloat/softfloat.go index 4b1fb7ad20c..4c092c0a609 100644 --- a/gnovm/pkg/gnolang/internal/softfloat/softfloat.go +++ b/gnovm/pkg/gnolang/softfloat/softfloat.go @@ -15,33 +15,49 @@ // ge f >= g package softfloat +import ( + "fmt" + "math" +) + const ( mask = 0x7FF shift = 64 - 11 - 1 bias = 1023 ) -type Float64 uint64 -type Float32 uint32 +type ( + Float64 uint64 + Float32 uint32 +) + +func (f Float64) String() string { + return fmt.Sprintf("%v", math.Float64frombits(uint64(f))) +} + +func (f Float32) String() string { + return fmt.Sprintf("%v", math.Float32frombits(uint32(f))) +} func Trunc(x Float64) Float64 { - _, _, _, isInf, IsNaN := funpack64(uint64(x)) - if x == 0 || isInf || IsNaN { + cmp, _ := Fcmp64(x, Fintto64(0)) + if _, _, _, isInf, IsNaN := funpack64(uint64(x)); cmp == 0 || isInf || IsNaN { return x } d, _ := Modf(x) return d } -func Modf(u Float64) (int Float64, frac Float64) { - cmp, _ := Fcmp64(u, 1) + +func Modf(u Float64) (it Float64, frac Float64) { + cmp, _ := Fcmp64(u, Fintto64(1)) if cmp < 0 { - cmp, _ := Fcmp64(u, 0) + cmp, _ := Fcmp64(u, Fintto64(0)) switch { case cmp < 0: - int, frac = Modf(Fneg64(u)) - return Fneg64(int), Fneg64(frac) + it, frac = Modf(Fneg64(u)) + return Fneg64(it), Fneg64(frac) case cmp == 0: return u, u // Return -0, -0 when f == -0 } @@ -55,7 +71,7 @@ func Modf(u Float64) (int Float64, frac Float64) { u &^= 1<<(64-12-e) - 1 } - frac = Fsub64(u, int) + frac = Fsub64(u, it) return } @@ -73,7 +89,7 @@ func Fgt64(f, g Float64) bool { return fgt64(uint64(f), uint64(g)) } func Fge64(f, g Float64) bool { return fge64(uint64(f), uint64(g)) } func Fadd32(f, g Float32) Float32 { return Float32(fadd32(uint32(f), uint32(g))) } -func Fsub32(f, g Float32) Float32 { return Float32(fadd32(uint32(f), uint32(Fneg32(uint32(g))))) } +func Fsub32(f, g Float32) Float32 { return Float32(fadd32(uint32(f), uint32(Fneg32(g)))) } func Fmul32(f, g Float32) Float32 { return Float32(fmul32(uint32(f), uint32(g))) } func Fdiv32(f, g Float32) Float32 { return Float32(fdiv32(uint32(f), uint32(g))) } func Feq32(f, g Float32) bool { return feq32(uint32(f), uint32(g)) } @@ -82,7 +98,7 @@ func Fge32(f, g Float32) bool { return fge32(uint32(f), uint32(g)) } func Fcmp64(f, g Float64) (cmp int32, isnan bool) { return fcmp64(uint64(f), uint64(g)) } -func Fneg32(f uint32) uint32 { +func Fneg32(f Float32) Float32 { // Not defined in runtime - this is a copy similar to fneg64. return f ^ (1 << (mantbits32 + expbits32)) } diff --git a/gnovm/pkg/gnolang/values.go b/gnovm/pkg/gnolang/values.go index 1a8badf3444..95fa59216b3 100644 --- a/gnovm/pkg/gnolang/values.go +++ b/gnovm/pkg/gnolang/values.go @@ -11,7 +11,7 @@ import ( "github.com/cockroachdb/apd/v3" - "github.com/gnolang/gno/gnovm/pkg/gnolang/internal/softfloat" + "github.com/gnolang/gno/gnovm/pkg/gnolang/softfloat" "github.com/gnolang/gno/tm2/pkg/crypto" ) diff --git a/gnovm/pkg/gnolang/values_conversions.go b/gnovm/pkg/gnolang/values_conversions.go index e830db8c931..eac90aeed14 100644 --- a/gnovm/pkg/gnolang/values_conversions.go +++ b/gnovm/pkg/gnolang/values_conversions.go @@ -8,7 +8,7 @@ import ( "strconv" "github.com/cockroachdb/apd/v3" - "github.com/gnolang/gno/gnovm/pkg/gnolang/internal/softfloat" + "github.com/gnolang/gno/gnovm/pkg/gnolang/softfloat" ) // t cannot be nil or untyped or DataByteType. @@ -848,14 +848,15 @@ GNO_CASE: switch k { case IntKind: validate(Float32Kind, IntKind, func() bool { - val := float64(tv.GetFloat32()) - trunc := math.Trunc(val) + val := softfloat.F32to64(tv.GetFloat32()) + trunc := softfloat.Trunc(val) - if val != trunc { + if cmp, _ := softfloat.Fcmp64(val, trunc); cmp != 0 { return false } - return int64(trunc) >= math.MinInt && int64(trunc) <= math.MaxInt + truncInt64 := softfloat.F64toint64(trunc) + return truncInt64 >= math.MinInt && truncInt64 <= math.MaxInt }) x := int(softfloat.F32toint64(tv.GetFloat32())) @@ -863,14 +864,15 @@ GNO_CASE: tv.SetInt(x) case Int8Kind: validate(Float32Kind, Int8Kind, func() bool { - val := float64(tv.GetFloat32()) - trunc := math.Trunc(val) + val := softfloat.F32to64(tv.GetFloat32()) + trunc := softfloat.Trunc(val) - if val != trunc { + if cmp, _ := softfloat.Fcmp64(val, trunc); cmp != 0 { return false } - return int64(trunc) >= math.MinInt8 && int64(trunc) <= math.MaxInt8 + truncInt64 := softfloat.F64toint64(trunc) + return truncInt64 >= math.MinInt8 && truncInt64 <= math.MaxInt8 }) x := int8(softfloat.F32toint32(tv.GetFloat32())) @@ -878,14 +880,15 @@ GNO_CASE: tv.SetInt8(x) case Int16Kind: validate(Float32Kind, Int16Kind, func() bool { - val := float64(tv.GetFloat32()) - trunc := math.Trunc(val) + val := softfloat.F32to64(tv.GetFloat32()) + trunc := softfloat.Trunc(val) - if val != trunc { + if cmp, _ := softfloat.Fcmp64(val, trunc); cmp != 0 { return false } - return int64(trunc) >= math.MinInt16 && int64(trunc) <= math.MaxInt16 + truncInt64 := softfloat.F64toint64(trunc) + return truncInt64 >= math.MinInt16 && truncInt64 <= math.MaxInt16 }) x := int16(softfloat.F32toint32(tv.GetFloat32())) @@ -893,40 +896,43 @@ GNO_CASE: tv.SetInt16(x) case Int32Kind: validate(Float32Kind, Int32Kind, func() bool { - val := float64(tv.GetFloat32()) - trunc := math.Trunc(val) + val := softfloat.F32to64(tv.GetFloat32()) + trunc := softfloat.Trunc(val) - if val != trunc { + if cmp, _ := softfloat.Fcmp64(val, trunc); cmp != 0 { return false } - return int64(trunc) >= math.MinInt32 && int64(trunc) <= math.MaxInt32 + truncInt64 := softfloat.F64toint64(trunc) + return truncInt64 >= math.MinInt32 && truncInt64 <= math.MaxInt32 }) - x := int32(softfloat.F32toint32(tv.GetFloat32())) + x := softfloat.F32toint32(tv.GetFloat32()) tv.T = t tv.SetInt32(x) case Int64Kind: validate(Float32Kind, Int64Kind, func() bool { - val := float64(tv.GetFloat32()) - trunc := math.Trunc(val) + val := softfloat.F32to64(tv.GetFloat32()) + trunc := softfloat.Trunc(val) - return val == trunc + cmp, _ := softfloat.Fcmp64(val, trunc) + return cmp == 0 }) - x := int64(softfloat.F32toint64(tv.GetFloat32())) + x := softfloat.F32toint64(tv.GetFloat32()) tv.T = t tv.SetInt64(x) case UintKind: validate(Float32Kind, UintKind, func() bool { - val := float64(tv.GetFloat32()) - trunc := math.Trunc(val) + val := softfloat.F32to64(tv.GetFloat32()) + trunc := softfloat.Trunc(val) - if val != trunc { + if cmp, _ := softfloat.Fcmp64(val, trunc); cmp != 0 { return false } - return trunc >= 0 && trunc <= math.MaxUint + truncUint64 := softfloat.F64touint64(trunc) + return truncUint64 >= 0 && truncUint64 <= math.MaxUint }) x := uint(softfloat.F32touint64(tv.GetFloat32())) @@ -934,14 +940,15 @@ GNO_CASE: tv.SetUint(x) case Uint8Kind: validate(Float32Kind, Uint8Kind, func() bool { - val := float64(tv.GetFloat32()) - trunc := math.Trunc(val) + val := softfloat.F32to64(tv.GetFloat32()) + trunc := softfloat.Trunc(val) - if val != trunc { + if cmp, _ := softfloat.Fcmp64(val, trunc); cmp != 0 { return false } - return int64(trunc) >= 0 && int64(trunc) <= math.MaxUint8 + truncUint64 := softfloat.F64touint64(trunc) + return truncUint64 >= 0 && truncUint64 <= math.MaxUint8 }) x := uint8(softfloat.F32touint64(tv.GetFloat32())) @@ -949,14 +956,15 @@ GNO_CASE: tv.SetUint8(x) case Uint16Kind: validate(Float32Kind, Uint16Kind, func() bool { - val := float64(tv.GetFloat32()) - trunc := math.Trunc(val) + val := softfloat.F32to64(tv.GetFloat32()) + trunc := softfloat.Trunc(val) - if val != trunc { + if cmp, _ := softfloat.Fcmp64(val, trunc); cmp != 0 { return false } - return int64(trunc) >= 0 && int64(trunc) <= math.MaxUint16 + truncUint64 := softfloat.F64touint64(trunc) + return truncUint64 >= 0 && truncUint64 <= math.MaxUint16 }) x := uint16(softfloat.F32touint64(tv.GetFloat32())) @@ -964,14 +972,15 @@ GNO_CASE: tv.SetUint16(x) case Uint32Kind: validate(Float32Kind, Uint32Kind, func() bool { - val := float64(tv.GetFloat32()) - trunc := math.Trunc(val) + val := softfloat.F32to64(tv.GetFloat32()) + trunc := softfloat.Trunc(val) - if val != trunc { + if cmp, _ := softfloat.Fcmp64(val, trunc); cmp != 0 { return false } - return int64(trunc) >= 0 && int64(trunc) <= math.MaxUint32 + truncUint64 := softfloat.F64touint64(trunc) + return truncUint64 >= 0 && truncUint64 <= math.MaxUint32 }) x := uint32(softfloat.F32touint64(tv.GetFloat32())) @@ -979,17 +988,18 @@ GNO_CASE: tv.SetUint32(x) case Uint64Kind: validate(Float32Kind, Uint64Kind, func() bool { - val := float64(tv.GetFloat32()) - trunc := math.Trunc(val) + val := softfloat.F32to64(tv.GetFloat32()) + trunc := softfloat.Trunc(val) if val != trunc { return false } - return trunc >= 0 && trunc <= math.MaxUint + truncUint64 := softfloat.F64touint64(trunc) + return truncUint64 >= 0 && truncUint64 <= math.MaxUint }) - x := uint64(softfloat.F32touint64(tv.GetFloat32())) + x := softfloat.F32touint64(tv.GetFloat32()) tv.T = t tv.SetUint64(x) case Float32Kind: diff --git a/gnovm/pkg/gnolang/values_conversions_test.go b/gnovm/pkg/gnolang/values_conversions_test.go index 9d82936c09a..67f14658354 100644 --- a/gnovm/pkg/gnolang/values_conversions_test.go +++ b/gnovm/pkg/gnolang/values_conversions_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/cockroachdb/apd/v3" - "github.com/gnolang/gno/gnovm/pkg/gnolang/internal/softfloat" + "github.com/gnolang/gno/gnovm/pkg/gnolang/softfloat" "github.com/stretchr/testify/require" ) diff --git a/gnovm/tests/files/float8.gno b/gnovm/tests/files/float8.gno index eafae9fbca5..e1fce9bb3d7 100644 --- a/gnovm/tests/files/float8.gno +++ b/gnovm/tests/files/float8.gno @@ -12,7 +12,7 @@ func main() { var u16 uint16 = 65535 var u32 uint32 = 4294967295 var u64 uint64 = 18446744073709551615 - var f32 float32 = 0x1p127 * (1 + (1 - 0x1p-23)) + var f32 float32 = math.MaxFloat32 var f64 float64 = math.MaxFloat64 println(f32 / 2) println(f64 / 2) @@ -41,8 +41,8 @@ func main() { println(float32(u64)) println(float64(u64)) println(float32(f32)) - // println(float64(f32)) - // println(float32(f64)) + println(float64(f32)) + println(float32(f64)) println(float64(f64)) } @@ -74,4 +74,6 @@ func main() { // 1.8446744e+19 // 1.8446744073709552e+19 // 3.4028235e+38 +// 3.4028234663852886e+38 +// +Inf // 1.7976931348623157e+308 From 8b46e0176f07d7c730758e75c8c89c897ba85955 Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Sun, 1 Dec 2024 22:44:38 +0100 Subject: [PATCH 5/6] feat: refactor again --- gno.land/pkg/sdk/vm/convert.go | 6 +- gnovm/pkg/gnolang/gonative.go | 19 +- .../gnolang/{ => internal}/softfloat/copy.sh | 4 +- .../softfloat/runtime_softfloat64.go | 0 .../softfloat/runtime_softfloat64_test.go | 18 +- .../gnolang/internal/softfloat/softfloat.go | 70 ++++ gnovm/pkg/gnolang/op_binary.go | 51 ++- gnovm/pkg/gnolang/op_inc_dec.go | 9 +- gnovm/pkg/gnolang/op_unary.go | 5 +- gnovm/pkg/gnolang/softfloat.go | 338 ++++++++++++++++++ gnovm/pkg/gnolang/softfloat/softfloat.go | 125 ------- gnovm/pkg/gnolang/values.go | 17 +- gnovm/pkg/gnolang/values_conversions.go | 244 ++++++------- gnovm/pkg/gnolang/values_conversions_test.go | 3 +- 14 files changed, 575 insertions(+), 334 deletions(-) rename gnovm/pkg/gnolang/{ => internal}/softfloat/copy.sh (94%) rename gnovm/pkg/gnolang/{ => internal}/softfloat/runtime_softfloat64.go (100%) rename gnovm/pkg/gnolang/{ => internal}/softfloat/runtime_softfloat64_test.go (87%) create mode 100644 gnovm/pkg/gnolang/internal/softfloat/softfloat.go create mode 100644 gnovm/pkg/gnolang/softfloat.go delete mode 100644 gnovm/pkg/gnolang/softfloat/softfloat.go diff --git a/gno.land/pkg/sdk/vm/convert.go b/gno.land/pkg/sdk/vm/convert.go index b6f3a3de7c1..89fc3e0973d 100644 --- a/gno.land/pkg/sdk/vm/convert.go +++ b/gno.land/pkg/sdk/vm/convert.go @@ -3,13 +3,11 @@ package vm import ( "encoding/base64" "fmt" - "math" "strconv" "strings" "github.com/cockroachdb/apd/v3" gno "github.com/gnolang/gno/gnovm/pkg/gnolang" - "github.com/gnolang/gno/gnovm/pkg/gnolang/softfloat" ) func assertNoPlusPrefix(s string) { @@ -145,11 +143,11 @@ func convertArgToGno(arg string, argT gno.Type) (tv gno.TypedValue) { return case gno.Float32Type: value := convertFloat(arg, 32) - tv.SetFloat32(softfloat.Float32(math.Float32bits(float32(value)))) + tv.SetFloat32(gno.ConvertToSoftFloat32(value)) return case gno.Float64Type: value := convertFloat(arg, 64) - tv.SetFloat64(softfloat.Float64(math.Float64bits(value))) + tv.SetFloat64(gno.ConvertToSoftFloat64(value)) return default: panic(fmt.Sprintf("unexpected primitive type %s", bt.String())) diff --git a/gnovm/pkg/gnolang/gonative.go b/gnovm/pkg/gnolang/gonative.go index 73f5f3728cb..e77c7f99474 100644 --- a/gnovm/pkg/gnolang/gonative.go +++ b/gnovm/pkg/gnolang/gonative.go @@ -2,10 +2,7 @@ package gnolang import ( "fmt" - "math" "reflect" - - "github.com/gnolang/gno/gnovm/pkg/gnolang/softfloat" ) // NOTE @@ -332,9 +329,9 @@ func go2GnoValue(alloc *Allocator, rv reflect.Value) (tv TypedValue) { case reflect.Uint64: tv.SetUint64(rv.Uint()) case reflect.Float32: - tv.SetFloat32(softfloat.Float32(math.Float32bits(float32(rv.Float())))) + tv.SetFloat32(ConvertToSoftFloat32(rv.Float())) case reflect.Float64: - tv.SetFloat64(softfloat.Float64(math.Float64bits(rv.Float()))) + tv.SetFloat64(ConvertToSoftFloat64(rv.Float())) case reflect.Array: tv.V = alloc.NewNative(rv) case reflect.Slice: @@ -431,11 +428,11 @@ func go2GnoValueUpdate(alloc *Allocator, rlm *Realm, lvl int, tv *TypedValue, rv } case Float32Kind: if lvl != 0 { - tv.SetFloat32(softfloat.Float32(math.Float32bits(float32(rv.Float())))) + tv.SetFloat32(ConvertToSoftFloat32(rv.Float())) } case Float64Kind: if lvl != 0 { - tv.SetFloat64(softfloat.Float64(math.Float64bits(rv.Float()))) + tv.SetFloat64(ConvertToSoftFloat64(rv.Float())) } case BigintKind: panic("not yet implemented") @@ -647,9 +644,9 @@ func go2GnoValue2(alloc *Allocator, store Store, rv reflect.Value, recursive boo case reflect.Uint64: tv.SetUint64(rv.Uint()) case reflect.Float32: - tv.SetFloat32(softfloat.Float32(math.Float32bits(float32(rv.Float())))) + tv.SetFloat32(ConvertToSoftFloat32(rv.Float())) case reflect.Float64: - tv.SetFloat64(softfloat.Float64(math.Float64bits(rv.Float()))) + tv.SetFloat64(ConvertToSoftFloat64(rv.Float())) case reflect.Array: rvl := rv.Len() if rv.Type().Elem().Kind() == reflect.Uint8 { @@ -1052,9 +1049,9 @@ func gno2GoValue(tv *TypedValue, rv reflect.Value) (ret reflect.Value) { case Uint64Type: rv.SetUint(tv.GetUint64()) case Float32Type: - rv.SetFloat(math.Float64frombits(uint64(softfloat.F32to64(tv.GetFloat32())))) + rv.SetFloat(tv.GetFloat32().Float64()) case Float64Type: - rv.SetFloat(math.Float64frombits(uint64(tv.GetFloat64()))) + rv.SetFloat(tv.GetFloat64().Float64()) default: panic(fmt.Sprintf( "unexpected type %s", diff --git a/gnovm/pkg/gnolang/softfloat/copy.sh b/gnovm/pkg/gnolang/internal/softfloat/copy.sh similarity index 94% rename from gnovm/pkg/gnolang/softfloat/copy.sh rename to gnovm/pkg/gnolang/internal/softfloat/copy.sh index 8107002ee9f..6d2a8f80462 100644 --- a/gnovm/pkg/gnolang/softfloat/copy.sh +++ b/gnovm/pkg/gnolang/internal/softfloat/copy.sh @@ -26,7 +26,7 @@ cat > runtime_softfloat64_test.go << EOF EOF cat "$GOROOT/src/runtime/softfloat64_test.go" >> ./runtime_softfloat64_test.go sed -i 's/^package runtime_test$/package softfloat_test/ -s#^\t\. "runtime"$#\t. "github.com/gnolang/gno/gnovm/pkg/gnolang/softfloat"# +s#^\t\. "runtime"$#\t. "github.com/gnolang/gno/gnovm/pkg/gnolang/internal/softfloat"# s/GOARCH/runtime.GOARCH/g 16a\ - "runtime"' runtime_softfloat64_test.go + "runtime"' runtime_softfloat64_test.go \ No newline at end of file diff --git a/gnovm/pkg/gnolang/softfloat/runtime_softfloat64.go b/gnovm/pkg/gnolang/internal/softfloat/runtime_softfloat64.go similarity index 100% rename from gnovm/pkg/gnolang/softfloat/runtime_softfloat64.go rename to gnovm/pkg/gnolang/internal/softfloat/runtime_softfloat64.go diff --git a/gnovm/pkg/gnolang/softfloat/runtime_softfloat64_test.go b/gnovm/pkg/gnolang/internal/softfloat/runtime_softfloat64_test.go similarity index 87% rename from gnovm/pkg/gnolang/softfloat/runtime_softfloat64_test.go rename to gnovm/pkg/gnolang/internal/softfloat/runtime_softfloat64_test.go index eb0a36269f9..c57fe08b0ef 100644 --- a/gnovm/pkg/gnolang/softfloat/runtime_softfloat64_test.go +++ b/gnovm/pkg/gnolang/internal/softfloat/runtime_softfloat64_test.go @@ -10,19 +10,19 @@ package softfloat_test import ( - . "github.com/gnolang/gno/gnovm/pkg/gnolang/softfloat" "math" "math/rand" - "runtime" + . "github.com/gnolang/gno/gnovm/pkg/gnolang/internal/softfloat" "testing" + "runtime" ) // turn uint64 op into float64 op -func fop(f func(x, y Float64) Float64) func(x, y float64) float64 { +func fop(f func(x, y uint64) uint64) func(x, y float64) float64 { return func(x, y float64) float64 { bx := math.Float64bits(x) by := math.Float64bits(y) - return math.Float64frombits(uint64(f(Float64(bx), Float64(by)))) + return math.Float64frombits(f(bx, by)) } } @@ -97,12 +97,12 @@ func trunc32(f float64) float64 { // 64 -sw->32 -hw-> 64 func to32sw(f float64) float64 { - return float64(math.Float32frombits(uint32(F64to32(Float64(math.Float64bits(f)))))) + return float64(math.Float32frombits(F64to32(math.Float64bits(f)))) } // 64 -hw->32 -sw-> 64 func to64sw(f float64) float64 { - return math.Float64frombits(uint64(F32to64(Float32(math.Float32bits(float32(f)))))) + return math.Float64frombits(F32to64(math.Float32bits(float32(f)))) } // float64 -hw-> int64 -hw-> float64 @@ -117,7 +117,7 @@ func hwint32(f float64) float64 { // float64 -sw-> int64 -hw-> float64 func toint64sw(f float64) float64 { - i, ok := F64toint(Float64(math.Float64bits(f))) + i, ok := F64toint(math.Float64bits(f)) if !ok { // There's no right answer for out of range. // Match the hardware to pass the test. @@ -128,7 +128,7 @@ func toint64sw(f float64) float64 { // float64 -hw-> int64 -sw-> float64 func fromint64sw(f float64) float64 { - return math.Float64frombits(uint64(Fintto64(int64(f)))) + return math.Float64frombits(Fintto64(int64(f))) } var nerr int @@ -187,7 +187,7 @@ func hwcmp(f, g float64) (cmp int, isnan bool) { func testcmp(t *testing.T, f, g float64) { hcmp, hisnan := hwcmp(f, g) - scmp, sisnan := Fcmp64(Float64(math.Float64bits(f)), Float64(math.Float64bits(g))) + scmp, sisnan := Fcmp64(math.Float64bits(f), math.Float64bits(g)) if int32(hcmp) != scmp || hisnan != sisnan { err(t, "cmp(%g, %g) = sw %v, %v, hw %v, %v\n", f, g, scmp, sisnan, hcmp, hisnan) } diff --git a/gnovm/pkg/gnolang/internal/softfloat/softfloat.go b/gnovm/pkg/gnolang/internal/softfloat/softfloat.go new file mode 100644 index 00000000000..29d22f66d45 --- /dev/null +++ b/gnovm/pkg/gnolang/internal/softfloat/softfloat.go @@ -0,0 +1,70 @@ +// Package softfloat is a copy of the Go runtime's softfloat64.go file. +// It is a pure software floating point implementation. It can be used to +// perform determinstic, hardware-independent floating point computations. +// +// This package uses shortnames to refer to its different operations. Here is a +// quick reference: +// +// add f + g +// sub f - g +// mul f * g +// div f / g +// neg (- f) +// eq f == g +// gt f > g +// ge f >= g +package softfloat + +// This file mostly exports the functions from runtime_softfloat64.go + +//go:generate sh copy.sh + +func Fadd64(f, g uint64) uint64 { return fadd64(f, g) } +func Fsub64(f, g uint64) uint64 { return fsub64(f, g) } +func Fmul64(f, g uint64) uint64 { return fmul64(f, g) } +func Fdiv64(f, g uint64) uint64 { return fdiv64(f, g) } +func Fneg64(f uint64) uint64 { return fneg64(f) } +func Feq64(f, g uint64) bool { return feq64(f, g) } +func Fgt64(f, g uint64) bool { return fgt64(f, g) } +func Fge64(f, g uint64) bool { return fge64(f, g) } + +func Fadd32(f, g uint32) uint32 { return fadd32(f, g) } +func Fsub32(f, g uint32) uint32 { return fadd32(f, Fneg32(g)) } +func Fmul32(f, g uint32) uint32 { return fmul32(f, g) } +func Fdiv32(f, g uint32) uint32 { return fdiv32(f, g) } +func Feq32(f, g uint32) bool { return feq32(f, g) } +func Fgt32(f, g uint32) bool { return fgt32(f, g) } +func Fge32(f, g uint32) bool { return fge32(f, g) } + +func Fcmp64(f, g uint64) (cmp int32, isnan bool) { return fcmp64(f, g) } + +func Fneg32(f uint32) uint32 { + // Not defined in runtime - this is a copy similar to fneg64. + return f ^ (1 << (mantbits32 + expbits32)) +} + +// Conversions + +func Fintto64(val int64) (f uint64) { return fintto64(val) } +func Fintto32(val int64) (f uint32) { return fintto32(val) } + +func F32to64(f uint32) uint64 { return f32to64(f) } +func F32toint32(x uint32) int32 { return f32toint32(x) } +func F32toint64(x uint32) int64 { return f32toint64(x) } +func F32touint64(x uint32) uint64 { return f32touint64(x) } +func F64to32(f uint64) uint32 { return f64to32(f) } +func F64toint(f uint64) (val int64, ok bool) { return f64toint(f) } +func F64toint32(x uint64) int32 { return f64toint32(x) } +func F64toint64(x uint64) int64 { return f64toint64(x) } +func F64touint64(x uint64) uint64 { return f64touint64(x) } +func Fint32to32(x int32) uint32 { return fint32to32(x) } +func Fint32to64(x int32) uint64 { return fint32to64(x) } +func Fint64to32(x int64) uint32 { return fint64to32(x) } +func Fint64to64(x int64) uint64 { return fint64to64(x) } +func Fuint64to32(x uint64) uint32 { return fuint64to32(x) } +func Fuint64to64(x uint64) uint64 { return fuint64to64(x) } + +// unpack64 unpacks the float64 f into sign, exp, mantissa, isInf, isNaN. + +func Funpack32(f uint32) (sign, mant uint32, exp int, inf, nan bool) { return funpack32(f) } +func Funpack64(f uint64) (sign, mant uint64, exp int, inf, nan bool) { return funpack64(f) } diff --git a/gnovm/pkg/gnolang/op_binary.go b/gnovm/pkg/gnolang/op_binary.go index 2529207cd2a..e99a5505058 100644 --- a/gnovm/pkg/gnolang/op_binary.go +++ b/gnovm/pkg/gnolang/op_binary.go @@ -6,7 +6,6 @@ import ( "math/big" "github.com/cockroachdb/apd/v3" - "github.com/gnolang/gno/gnovm/pkg/gnolang/softfloat" ) // ---------------------------------------- @@ -392,11 +391,9 @@ func isEql(store Store, lv, rv *TypedValue) bool { case Uint64Kind: return (lv.GetUint64() == rv.GetUint64()) case Float32Kind: - cmp, nan := softfloat.Fcmp64(softfloat.F32to64(lv.GetFloat32()), softfloat.F32to64(rv.GetFloat32())) - return cmp == 0 && !nan + return lv.GetFloat32().Eq(rv.GetFloat32()) case Float64Kind: - cmp, nan := softfloat.Fcmp64(lv.GetFloat64(), rv.GetFloat64()) - return cmp == 0 && !nan + return lv.GetFloat64().Eq(rv.GetFloat64()) case BigintKind: lb := lv.V.(BigintValue).V rb := rv.V.(BigintValue).V @@ -535,11 +532,9 @@ func isLss(lv, rv *TypedValue) bool { case Uint64Kind: return (lv.GetUint64() < rv.GetUint64()) case Float32Kind: - cmp, nan := softfloat.Fcmp64(softfloat.F32to64(lv.GetFloat32()), softfloat.F32to64(rv.GetFloat32())) - return (cmp < 0) && !nan + return lv.GetFloat32().Lt(rv.GetFloat32()) case Float64Kind: - cmp, nan := softfloat.Fcmp64(lv.GetFloat64(), rv.GetFloat64()) - return (cmp < 0) && !nan + return lv.GetFloat64().Lt(rv.GetFloat64()) case BigintKind: lb := lv.V.(BigintValue).V rb := rv.V.(BigintValue).V @@ -581,11 +576,9 @@ func isLeq(lv, rv *TypedValue) bool { case Uint64Kind: return (lv.GetUint64() <= rv.GetUint64()) case Float32Kind: - cmp, nan := softfloat.Fcmp64(softfloat.F32to64(lv.GetFloat32()), softfloat.F32to64(rv.GetFloat32())) - return (cmp <= 0) && !nan + return lv.GetFloat32().Le(rv.GetFloat32()) case Float64Kind: - cmp, nan := softfloat.Fcmp64(lv.GetFloat64(), rv.GetFloat64()) - return (cmp <= 0) && !nan + return lv.GetFloat64().Le(rv.GetFloat64()) case BigintKind: lb := lv.V.(BigintValue).V rb := rv.V.(BigintValue).V @@ -627,11 +620,9 @@ func isGtr(lv, rv *TypedValue) bool { case Uint64Kind: return (lv.GetUint64() > rv.GetUint64()) case Float32Kind: - cmp, nan := softfloat.Fcmp64(softfloat.F32to64(lv.GetFloat32()), softfloat.F32to64(rv.GetFloat32())) - return (cmp > 0) && !nan + return lv.GetFloat32().Gt(rv.GetFloat32()) case Float64Kind: - cmp, nan := softfloat.Fcmp64(lv.GetFloat64(), rv.GetFloat64()) - return (cmp > 0) && !nan + return lv.GetFloat64().Gt(rv.GetFloat64()) case BigintKind: lb := lv.V.(BigintValue).V rb := rv.V.(BigintValue).V @@ -673,11 +664,9 @@ func isGeq(lv, rv *TypedValue) bool { case Uint64Kind: return (lv.GetUint64() >= rv.GetUint64()) case Float32Kind: - cmp, nan := softfloat.Fcmp64(softfloat.F32to64(lv.GetFloat32()), softfloat.F32to64(rv.GetFloat32())) - return (cmp >= 0) && !nan + return lv.GetFloat32().Ge(rv.GetFloat32()) case Float64Kind: - cmp, nan := softfloat.Fcmp64(lv.GetFloat64(), rv.GetFloat64()) - return (cmp >= 0) && !nan + return lv.GetFloat64().Ge(rv.GetFloat64()) case BigintKind: lb := lv.V.(BigintValue).V rb := rv.V.(BigintValue).V @@ -725,10 +714,10 @@ func addAssign(alloc *Allocator, lv, rv *TypedValue) { lv.SetUint64(lv.GetUint64() + rv.GetUint64()) case Float32Type: // NOTE: gno doesn't fuse *+. - lv.SetFloat32(softfloat.Fadd32(lv.GetFloat32(), rv.GetFloat32())) + lv.SetFloat32(lv.GetFloat32().Add(rv.GetFloat32())) case Float64Type: // NOTE: gno doesn't fuse *+. - lv.SetFloat64(softfloat.Fadd64(lv.GetFloat64(), rv.GetFloat64())) + lv.SetFloat64(lv.GetFloat64().Add(rv.GetFloat64())) case BigintType, UntypedBigintType: lb := lv.GetBigInt() lb = big.NewInt(0).Add(lb, rv.GetBigInt()) @@ -781,10 +770,10 @@ func subAssign(lv, rv *TypedValue) { lv.SetUint64(lv.GetUint64() - rv.GetUint64()) case Float32Type: // NOTE: gno doesn't fuse *+. - lv.SetFloat32(softfloat.Fsub32(lv.GetFloat32(), rv.GetFloat32())) + lv.SetFloat32(lv.GetFloat32().Sub(rv.GetFloat32())) case Float64Type: // NOTE: gno doesn't fuse *+. - lv.SetFloat64(softfloat.Fsub64(lv.GetFloat64(), rv.GetFloat64())) + lv.SetFloat64(lv.GetFloat64().Sub(rv.GetFloat64())) case BigintType, UntypedBigintType: lb := lv.GetBigInt() lb = big.NewInt(0).Sub(lb, rv.GetBigInt()) @@ -837,10 +826,10 @@ func mulAssign(lv, rv *TypedValue) { lv.SetUint64(lv.GetUint64() * rv.GetUint64()) case Float32Type: // NOTE: gno doesn't fuse *+. - lv.SetFloat32(softfloat.Fmul32(lv.GetFloat32(), rv.GetFloat32())) + lv.SetFloat32(lv.GetFloat32().Mul(rv.GetFloat32())) case Float64Type: // NOTE: gno doesn't fuse *+. - lv.SetFloat64(softfloat.Fmul64(lv.GetFloat64(), rv.GetFloat64())) + lv.SetFloat64(lv.GetFloat64().Mul(rv.GetFloat64())) case BigintType, UntypedBigintType: lb := lv.GetBigInt() lb = big.NewInt(0).Mul(lb, rv.GetBigInt()) @@ -928,16 +917,16 @@ func quoAssign(lv, rv *TypedValue) *Exception { lv.SetUint64(lv.GetUint64() / rv.GetUint64()) case Float32Type: // NOTE: gno doesn't fuse *+. - if cmp, nan := softfloat.Fcmp64(softfloat.F32to64(rv.GetFloat32()), softfloat.Fint32to64(0)); cmp == 0 && !nan { + if rv.GetFloat32().Eq(0) { return expt } - lv.SetFloat32(softfloat.Fdiv32(lv.GetFloat32(), rv.GetFloat32())) + lv.SetFloat32(lv.GetFloat32().Div(rv.GetFloat32())) case Float64Type: // NOTE: gno doesn't fuse *+. - if cmp, nan := softfloat.Fcmp64(rv.GetFloat64(), 0); cmp == 0 && !nan { + if rv.GetFloat64().Eq(0) { return expt } - lv.SetFloat64(softfloat.Fdiv64(lv.GetFloat64(), rv.GetFloat64())) + lv.SetFloat64(lv.GetFloat64().Div(rv.GetFloat64())) case BigintType, UntypedBigintType: if rv.GetBigInt().Sign() == 0 { return expt diff --git a/gnovm/pkg/gnolang/op_inc_dec.go b/gnovm/pkg/gnolang/op_inc_dec.go index ca1cab55a09..98583a57976 100644 --- a/gnovm/pkg/gnolang/op_inc_dec.go +++ b/gnovm/pkg/gnolang/op_inc_dec.go @@ -5,7 +5,6 @@ import ( "math/big" "github.com/cockroachdb/apd/v3" - "github.com/gnolang/gno/gnovm/pkg/gnolang/softfloat" ) func (m *Machine) doOpInc() { @@ -55,9 +54,9 @@ func (m *Machine) doOpInc() { case Uint64Type: lv.SetUint64(lv.GetUint64() + 1) case Float32Type: - lv.SetFloat32(softfloat.Fadd32(lv.GetFloat32(), softfloat.Fint32to32(1))) + lv.SetFloat32(lv.GetFloat32().Add(1)) case Float64Type: - lv.SetFloat64(softfloat.Fadd64(lv.GetFloat64(), softfloat.Fintto64(1))) + lv.SetFloat64(lv.GetFloat64().Add(1)) case BigintType, UntypedBigintType: lb := lv.GetBigInt() lb = big.NewInt(0).Add(lb, big.NewInt(1)) @@ -125,9 +124,9 @@ func (m *Machine) doOpDec() { case Uint64Type: lv.SetUint64(lv.GetUint64() - 1) case Float32Type: - lv.SetFloat32(softfloat.Fsub32(lv.GetFloat32(), softfloat.Fint32to32(1))) + lv.SetFloat32(lv.GetFloat32().Sub(1)) case Float64Type: - lv.SetFloat64(softfloat.Fsub64(lv.GetFloat64(), softfloat.Fintto64(1))) + lv.SetFloat64(lv.GetFloat64().Sub(1)) case BigintType, UntypedBigintType: lb := lv.GetBigInt() lb = big.NewInt(0).Sub(lb, big.NewInt(1)) diff --git a/gnovm/pkg/gnolang/op_unary.go b/gnovm/pkg/gnolang/op_unary.go index e0ea3861084..99ecb98d792 100644 --- a/gnovm/pkg/gnolang/op_unary.go +++ b/gnovm/pkg/gnolang/op_unary.go @@ -5,7 +5,6 @@ import ( "math/big" "github.com/cockroachdb/apd/v3" - "github.com/gnolang/gno/gnovm/pkg/gnolang/softfloat" ) func (m *Machine) doOpUpos() { @@ -47,9 +46,9 @@ func (m *Machine) doOpUneg() { case Uint64Type: xv.SetUint64(-xv.GetUint64()) case Float32Type: - xv.SetFloat32(softfloat.Fneg32(xv.GetFloat32())) + xv.SetFloat32(xv.GetFloat32().Neg()) case Float64Type: - xv.SetFloat64(softfloat.Fneg64(xv.GetFloat64())) + xv.SetFloat64(xv.GetFloat64().Neg()) case UntypedBigintType, BigintType: bv := xv.V.(BigintValue) xv.V = BigintValue{V: new(big.Int).Neg(bv.V)} diff --git a/gnovm/pkg/gnolang/softfloat.go b/gnovm/pkg/gnolang/softfloat.go new file mode 100644 index 00000000000..61991b0a005 --- /dev/null +++ b/gnovm/pkg/gnolang/softfloat.go @@ -0,0 +1,338 @@ +package gnolang + +import ( + "fmt" + "math" + + "github.com/gnolang/gno/gnovm/pkg/gnolang/internal/softfloat" +) + +const ( + mask = 0x7FF + shift = 64 - 11 - 1 + bias = 1023 +) + +type ( + SoftFloat64 uint64 + SoftFloat32 uint32 +) + +func Trunc(x SoftFloat64) SoftFloat64 { + cmp, _ := softfloat.Fcmp64(uint64(x), softfloat.Fintto64(0)) + if _, _, _, isInf, IsNaN := softfloat.Funpack64(uint64(x)); cmp == 0 || isInf || IsNaN { + return x + } + + d, _ := Modf(x) + return d +} + +func Modf(u SoftFloat64) (it SoftFloat64, frac SoftFloat64) { + if u.Le(1) { + switch { + case u.Lt(0): + it, frac = Modf(u.Neg()) + return -it, -frac + case u.Eq(0): + return u, u // Return -0, -0 when f == -0 + } + return 0, u + } + + it = u + e := uint(it>>shift)&mask - bias + + // Keep the top 12+e bits, the integer part; clear the rest. + if e < 64-12 { + it &^= 1<<(64-12-e) - 1 + } + + frac = u.Sub(it) + return +} + +func ConvertToSoftFloat64(n any) SoftFloat64 { + switch n := n.(type) { + case SoftFloat64: + return n + case SoftFloat32: + return SoftFloat64(softfloat.F32to64(uint32(n))) + case int: + return SoftFloat64(softfloat.Fintto64(int64(n))) + case int32: + return SoftFloat64(softfloat.Fint32to64(n)) + case int8: + return SoftFloat64(softfloat.Fint32to64(int32(n))) + case int16: + return SoftFloat64(softfloat.Fint32to64(int32(n))) + case int64: + return SoftFloat64(softfloat.Fint64to64(n)) + case uint: + return SoftFloat64(softfloat.Fuint64to64(uint64(n))) + case uint16: + return SoftFloat64(softfloat.Fuint64to64(uint64(n))) + case uint32: + return SoftFloat64(softfloat.Fuint64to64(uint64(n))) + case uint8: + return SoftFloat64(softfloat.Fuint64to64(uint64(n))) + case uint64: + return SoftFloat64(softfloat.Fuint64to64(n)) + case float32: + return SoftFloat32(math.Float32bits(n)).SoftFloat64() + case float64: + return SoftFloat64(math.Float64bits(n)) + default: + panic(fmt.Sprintf("unsupported type: %T", n)) + } +} + +func ConvertToSoftFloat32(n any) SoftFloat32 { + switch n := n.(type) { + case SoftFloat64: + return SoftFloat32(softfloat.F64to32(uint64(n))) + case SoftFloat32: + return n + case int: + return SoftFloat32(softfloat.Fintto32(int64(n))) + case int32: + return SoftFloat32(softfloat.Fint32to32(n)) + case int8: + return SoftFloat32(softfloat.Fint32to32(int32(n))) + case int16: + return SoftFloat32(softfloat.Fint32to32(int32(n))) + case int64: + return SoftFloat32(softfloat.Fint64to32(n)) + case uint: + return SoftFloat32(softfloat.Fuint64to32(uint64(n))) + case uint16: + return SoftFloat32(softfloat.Fuint64to32(uint64(n))) + case uint32: + return SoftFloat32(softfloat.Fuint64to32(uint64(n))) + case uint8: + return SoftFloat32(softfloat.Fuint64to32(uint64(n))) + case uint64: + return SoftFloat32(softfloat.Fuint64to32(n)) + case float32: + return SoftFloat32(math.Float32bits(n)) + case float64: + return SoftFloat64(math.Float64bits(n)).SoftFloat32() + default: + panic(fmt.Sprintf("unsupported type: %T", n)) + } +} + +// SoftFloat64 + +func (f SoftFloat64) String() string { + return fmt.Sprintf("%v", math.Float64frombits(uint64(f))) +} + +func (f SoftFloat64) Float64() float64 { + return math.Float64frombits(uint64(f)) +} + +func (f SoftFloat64) Float32() float32 { + return float32(math.Float64frombits(uint64(f))) +} + +func (f SoftFloat64) SoftFloat32() SoftFloat32 { + return SoftFloat32(softfloat.F64to32(uint64(f))) +} + +func (f SoftFloat64) Int() int { + n, _ := softfloat.F64toint(uint64(f)) + return int(n) +} + +func (f SoftFloat64) Int64() int64 { + return softfloat.F64toint64(uint64(f)) +} + +func (f SoftFloat64) Int32() int32 { + return softfloat.F64toint32(uint64(f)) +} + +func (f SoftFloat64) Int16() int16 { + return int16(softfloat.F64toint32(uint64(f))) +} + +func (f SoftFloat64) Int8() int8 { + return int8(softfloat.F64toint32(uint64(f))) +} + +func (f SoftFloat64) Uint() uint { + return uint(softfloat.F64touint64(uint64(f))) +} + +func (f SoftFloat64) Uint64() uint64 { + return softfloat.F64touint64(uint64(f)) +} + +func (f SoftFloat64) Uint32() uint32 { + return uint32(softfloat.F64touint64(uint64(f))) +} + +func (f SoftFloat64) Uint16() uint16 { + return uint16(softfloat.F64touint64(uint64(f))) +} + +func (f SoftFloat64) Uint8() uint8 { + return uint8(softfloat.F64touint64(uint64(f))) +} + +func (f SoftFloat64) Add(g any) SoftFloat64 { + return SoftFloat64(softfloat.Fadd64(uint64(f), uint64(ConvertToSoftFloat64(g)))) +} + +func (f SoftFloat64) Sub(g any) SoftFloat64 { + return SoftFloat64(softfloat.Fsub64(uint64(f), uint64(ConvertToSoftFloat64(g)))) +} + +func (f SoftFloat64) Mul(g any) SoftFloat64 { + return SoftFloat64(softfloat.Fmul64(uint64(f), uint64(ConvertToSoftFloat64(g)))) +} + +func (f SoftFloat64) Div(g any) SoftFloat64 { + return SoftFloat64(softfloat.Fdiv64(uint64(f), uint64(ConvertToSoftFloat64(g)))) +} + +func (f SoftFloat64) Neg() SoftFloat64 { + return SoftFloat64(softfloat.Fneg64(uint64(f))) +} + +func (f SoftFloat64) Trunc() SoftFloat64 { + return Trunc(f) +} + +// == +func (f SoftFloat64) Eq(g any) bool { + return softfloat.Feq64(uint64(f), uint64(ConvertToSoftFloat64(g))) +} + +// > +func (f SoftFloat64) Gt(g any) bool { + return softfloat.Fgt64(uint64(f), uint64(ConvertToSoftFloat64(g))) +} + +// >= +func (f SoftFloat64) Ge(g any) bool { + return softfloat.Fge64(uint64(f), uint64(ConvertToSoftFloat64(g))) +} + +// < +func (f SoftFloat64) Lt(g any) bool { + return !softfloat.Fge64(uint64(f), uint64(ConvertToSoftFloat64(g))) +} + +// <= +func (f SoftFloat64) Le(g any) bool { + return !softfloat.Fgt64(uint64(f), uint64(ConvertToSoftFloat64(g))) +} + +// SoftFloat32 + +func (f SoftFloat32) Float32() float32 { + return math.Float32frombits(uint32(f)) +} + +func (f SoftFloat32) Float64() float64 { + return math.Float64frombits(softfloat.F32to64(uint32(f))) +} + +func (f SoftFloat32) SoftFloat64() SoftFloat64 { + return SoftFloat64(softfloat.F32to64(uint32(f))) +} + +func (f SoftFloat32) Int() int { + return int(softfloat.F32toint64(uint32(f))) +} + +func (f SoftFloat32) Int64() int64 { + return softfloat.F32toint64(uint32(f)) +} + +func (f SoftFloat32) Int32() int32 { + return softfloat.F32toint32(uint32(f)) +} + +func (f SoftFloat32) Int16() int16 { + return int16(softfloat.F32toint32(uint32(f))) +} + +func (f SoftFloat32) Int8() int8 { + return int8(softfloat.F32toint32(uint32(f))) +} + +func (f SoftFloat32) Uint() uint { + return uint(softfloat.F32touint64(uint32(f))) +} + +func (f SoftFloat32) Uint64() uint64 { + return softfloat.F32touint64(uint32(f)) +} + +func (f SoftFloat32) Uint32() uint32 { + return uint32(softfloat.F32touint64(uint32(f))) +} + +func (f SoftFloat32) Uint16() uint16 { + return uint16(softfloat.F32touint64(uint32(f))) +} + +func (f SoftFloat32) Uint8() uint8 { + return uint8(softfloat.F32touint64(uint32(f))) +} + +func (f SoftFloat32) Add(g any) SoftFloat32 { + return SoftFloat32(softfloat.Fadd32(uint32(f), uint32(ConvertToSoftFloat32(g)))) +} + +func (f SoftFloat32) Sub(g any) SoftFloat32 { + return SoftFloat32(softfloat.Fsub32(uint32(f), uint32(ConvertToSoftFloat32(g)))) +} + +func (f SoftFloat32) Mul(g any) SoftFloat32 { + return SoftFloat32(softfloat.Fmul32(uint32(f), uint32(ConvertToSoftFloat32(g)))) +} + +func (f SoftFloat32) Div(g any) SoftFloat32 { + return SoftFloat32(softfloat.Fdiv32(uint32(f), uint32(ConvertToSoftFloat32(g)))) +} + +func (f SoftFloat32) Neg() SoftFloat32 { + return SoftFloat32(softfloat.Fneg32(uint32(f))) +} + +func (f SoftFloat32) Trunc() SoftFloat32 { + return SoftFloat32(softfloat.F64to32(uint64(Trunc(SoftFloat64(softfloat.F32to64(uint32(f))))))) +} + +// == +func (f SoftFloat32) Eq(g any) bool { + return softfloat.Feq32(uint32(f), uint32(ConvertToSoftFloat32(g))) +} + +// > +func (f SoftFloat32) Gt(g any) bool { + return softfloat.Fgt32(uint32(f), uint32(ConvertToSoftFloat32(g))) +} + +// >= +func (f SoftFloat32) Ge(g any) bool { + return softfloat.Fge32(uint32(f), uint32(ConvertToSoftFloat32(g))) +} + +// < +func (f SoftFloat32) Lt(g any) bool { + return !softfloat.Fge32(uint32(f), uint32(ConvertToSoftFloat32(g))) +} + +// <= +func (f SoftFloat32) Le(g any) bool { + return !softfloat.Fgt32(uint32(f), uint32(ConvertToSoftFloat32(g))) +} + +func (f SoftFloat32) String() string { + return fmt.Sprintf("%v", math.Float32frombits(uint32(f))) +} diff --git a/gnovm/pkg/gnolang/softfloat/softfloat.go b/gnovm/pkg/gnolang/softfloat/softfloat.go deleted file mode 100644 index 4c092c0a609..00000000000 --- a/gnovm/pkg/gnolang/softfloat/softfloat.go +++ /dev/null @@ -1,125 +0,0 @@ -// Package softfloat is a copy of the Go runtime's softfloat64.go file. -// It is a pure software floating point implementation. It can be used to -// perform determinstic, hardware-independent floating point computations. -// -// This package uses shortnames to refer to its different operations. Here is a -// quick reference: -// -// add f + g -// sub f - g -// mul f * g -// div f / g -// neg (- f) -// eq f == g -// gt f > g -// ge f >= g -package softfloat - -import ( - "fmt" - "math" -) - -const ( - mask = 0x7FF - shift = 64 - 11 - 1 - bias = 1023 -) - -type ( - Float64 uint64 - Float32 uint32 -) - -func (f Float64) String() string { - return fmt.Sprintf("%v", math.Float64frombits(uint64(f))) -} - -func (f Float32) String() string { - return fmt.Sprintf("%v", math.Float32frombits(uint32(f))) -} - -func Trunc(x Float64) Float64 { - cmp, _ := Fcmp64(x, Fintto64(0)) - if _, _, _, isInf, IsNaN := funpack64(uint64(x)); cmp == 0 || isInf || IsNaN { - return x - } - - d, _ := Modf(x) - return d -} - -func Modf(u Float64) (it Float64, frac Float64) { - cmp, _ := Fcmp64(u, Fintto64(1)) - - if cmp < 0 { - cmp, _ := Fcmp64(u, Fintto64(0)) - switch { - case cmp < 0: - it, frac = Modf(Fneg64(u)) - return Fneg64(it), Fneg64(frac) - case cmp == 0: - return u, u // Return -0, -0 when f == -0 - } - return 0, u - } - - e := uint(u>>shift)&mask - bias - - // Keep the top 12+e bits, the integer part; clear the rest. - if e < 64-12 { - u &^= 1<<(64-12-e) - 1 - } - - frac = Fsub64(u, it) - return -} - -// This file mostly exports the functions from runtime_softfloat64.go - -//go:generate sh copy.sh - -func Fadd64(f, g Float64) Float64 { return Float64(fadd64(uint64(f), uint64(g))) } -func Fsub64(f, g Float64) Float64 { return Float64(fsub64(uint64(f), uint64(g))) } -func Fmul64(f, g Float64) Float64 { return Float64(fmul64(uint64(f), uint64(g))) } -func Fdiv64(f, g Float64) Float64 { return Float64(fdiv64(uint64(f), uint64(g))) } -func Fneg64(f Float64) Float64 { return Float64(fneg64(uint64(f))) } -func Feq64(f, g Float64) bool { return feq64(uint64(f), uint64(g)) } -func Fgt64(f, g Float64) bool { return fgt64(uint64(f), uint64(g)) } -func Fge64(f, g Float64) bool { return fge64(uint64(f), uint64(g)) } - -func Fadd32(f, g Float32) Float32 { return Float32(fadd32(uint32(f), uint32(g))) } -func Fsub32(f, g Float32) Float32 { return Float32(fadd32(uint32(f), uint32(Fneg32(g)))) } -func Fmul32(f, g Float32) Float32 { return Float32(fmul32(uint32(f), uint32(g))) } -func Fdiv32(f, g Float32) Float32 { return Float32(fdiv32(uint32(f), uint32(g))) } -func Feq32(f, g Float32) bool { return feq32(uint32(f), uint32(g)) } -func Fgt32(f, g Float32) bool { return fgt32(uint32(f), uint32(g)) } -func Fge32(f, g Float32) bool { return fge32(uint32(f), uint32(g)) } - -func Fcmp64(f, g Float64) (cmp int32, isnan bool) { return fcmp64(uint64(f), uint64(g)) } - -func Fneg32(f Float32) Float32 { - // Not defined in runtime - this is a copy similar to fneg64. - return f ^ (1 << (mantbits32 + expbits32)) -} - -// Conversions - -func Fintto64(val int64) Float64 { return Float64(fintto64(val)) } -func Fintto32(val int64) Float32 { return Float32(fintto32(val)) } - -func F32to64(f Float32) Float64 { return Float64(f32to64(uint32(f))) } -func F32toint32(x Float32) int32 { return f32toint32(uint32(x)) } -func F32toint64(x Float32) int64 { return f32toint64(uint32(x)) } -func F32touint64(x Float32) uint64 { return f32touint64(uint32(x)) } -func F64to32(f Float64) Float32 { return Float32(f64to32(uint64(f))) } -func F64toint(f Float64) (val int64, ok bool) { return f64toint(uint64(f)) } -func F64toint32(x Float64) int32 { return f64toint32(uint64(x)) } -func F64toint64(x Float64) int64 { return f64toint64(uint64(x)) } -func F64touint64(x Float64) uint64 { return f64touint64(uint64(x)) } -func Fint32to32(x int32) Float32 { return Float32(fint32to32(x)) } -func Fint32to64(x int32) Float64 { return Float64(fint32to64(x)) } -func Fint64to32(x int64) Float32 { return Float32(fint64to32(x)) } -func Fint64to64(x int64) Float64 { return Float64(fint64to64(x)) } -func Fuint64to32(x uint64) Float32 { return Float32(fuint64to32(x)) } -func Fuint64to64(x uint64) Float64 { return Float64(fuint64to64(x)) } diff --git a/gnovm/pkg/gnolang/values.go b/gnovm/pkg/gnolang/values.go index 95fa59216b3..4fb6733e627 100644 --- a/gnovm/pkg/gnolang/values.go +++ b/gnovm/pkg/gnolang/values.go @@ -11,7 +11,6 @@ import ( "github.com/cockroachdb/apd/v3" - "github.com/gnolang/gno/gnovm/pkg/gnolang/softfloat" "github.com/gnolang/gno/tm2/pkg/crypto" ) @@ -1450,7 +1449,7 @@ func (tv *TypedValue) GetUint64() uint64 { return *(*uint64)(unsafe.Pointer(&tv.N)) } -func (tv *TypedValue) SetFloat32(n softfloat.Float32) { +func (tv *TypedValue) SetFloat32(n SoftFloat32) { if debug { if tv.T.Kind() != Float32Kind || isNative(tv.T) { panic(fmt.Sprintf( @@ -1458,10 +1457,10 @@ func (tv *TypedValue) SetFloat32(n softfloat.Float32) { tv.T.String())) } } - *(*softfloat.Float32)(unsafe.Pointer(&tv.N)) = n + *(*SoftFloat32)(unsafe.Pointer(&tv.N)) = n } -func (tv *TypedValue) GetFloat32() softfloat.Float32 { +func (tv *TypedValue) GetFloat32() SoftFloat32 { if debug { if tv.T != nil && tv.T.Kind() != Float32Kind { panic(fmt.Sprintf( @@ -1469,10 +1468,10 @@ func (tv *TypedValue) GetFloat32() softfloat.Float32 { tv.T.String())) } } - return *(*softfloat.Float32)(unsafe.Pointer(&tv.N)) + return *(*SoftFloat32)(unsafe.Pointer(&tv.N)) } -func (tv *TypedValue) SetFloat64(n softfloat.Float64) { +func (tv *TypedValue) SetFloat64(n SoftFloat64) { if debug { if tv.T.Kind() != Float64Kind || isNative(tv.T) { panic(fmt.Sprintf( @@ -1480,10 +1479,10 @@ func (tv *TypedValue) SetFloat64(n softfloat.Float64) { tv.T.String())) } } - *(*softfloat.Float64)(unsafe.Pointer(&tv.N)) = n + *(*SoftFloat64)(unsafe.Pointer(&tv.N)) = n } -func (tv *TypedValue) GetFloat64() softfloat.Float64 { +func (tv *TypedValue) GetFloat64() SoftFloat64 { if debug { if tv.T != nil && tv.T.Kind() != Float64Kind { panic(fmt.Sprintf( @@ -1491,7 +1490,7 @@ func (tv *TypedValue) GetFloat64() softfloat.Float64 { tv.T.String())) } } - return *(*softfloat.Float64)(unsafe.Pointer(&tv.N)) + return *(*SoftFloat64)(unsafe.Pointer(&tv.N)) } func (tv *TypedValue) GetBigInt() *big.Int { diff --git a/gnovm/pkg/gnolang/values_conversions.go b/gnovm/pkg/gnolang/values_conversions.go index eac90aeed14..09963185b58 100644 --- a/gnovm/pkg/gnolang/values_conversions.go +++ b/gnovm/pkg/gnolang/values_conversions.go @@ -8,7 +8,6 @@ import ( "strconv" "github.com/cockroachdb/apd/v3" - "github.com/gnolang/gno/gnovm/pkg/gnolang/softfloat" ) // t cannot be nil or untyped or DataByteType. @@ -164,11 +163,11 @@ GNO_CASE: tv.T = t tv.SetUint64(x) case Float32Kind: - x := softfloat.Fintto32(int64(tv.GetInt())) + x := ConvertToSoftFloat32(tv.GetInt()) tv.T = t tv.SetFloat32(x) case Float64Kind: - x := softfloat.Fintto64(int64(tv.GetInt())) + x := ConvertToSoftFloat64(tv.GetInt()) tv.T = t tv.SetFloat64(x) case StringKind: @@ -234,11 +233,11 @@ GNO_CASE: tv.T = t tv.SetUint64(x) case Float32Kind: - x := softfloat.Fint32to32(int32(tv.GetInt8())) + x := ConvertToSoftFloat32(tv.GetInt8()) tv.T = t tv.SetFloat32(x) case Float64Kind: - x := softfloat.Fint32to64(int32(tv.GetInt8())) + x := ConvertToSoftFloat64(tv.GetInt8()) tv.T = t tv.SetFloat64(x) case StringKind: @@ -305,11 +304,11 @@ GNO_CASE: tv.T = t tv.SetUint64(x) case Float32Kind: - x := softfloat.Fint32to32(int32(tv.GetInt16())) + x := ConvertToSoftFloat32(tv.GetInt16()) tv.T = t tv.SetFloat32(x) case Float64Kind: - x := softfloat.Fint32to64(int32(tv.GetInt16())) + x := ConvertToSoftFloat64(tv.GetInt16()) tv.T = t tv.SetFloat64(x) case StringKind: @@ -380,11 +379,11 @@ GNO_CASE: tv.T = t tv.SetUint64(x) case Float32Kind: - x := softfloat.Fint32to32(tv.GetInt32()) + x := ConvertToSoftFloat32(tv.GetInt32()) tv.T = t tv.SetFloat32(x) case Float64Kind: - x := softfloat.Fint32to64(tv.GetInt32()) + x := ConvertToSoftFloat64(tv.GetInt32()) tv.T = t tv.SetFloat64(x) case StringKind: @@ -457,11 +456,11 @@ GNO_CASE: tv.T = t tv.SetUint64(x) case Float32Kind: - x := softfloat.Fint64to32(tv.GetInt64()) + x := ConvertToSoftFloat32(tv.GetInt64()) tv.T = t tv.SetFloat32(x) case Float64Kind: - x := softfloat.Fint64to64(tv.GetInt64()) + x := ConvertToSoftFloat64(tv.GetInt64()) tv.T = t tv.SetFloat64(x) case StringKind: @@ -534,11 +533,11 @@ GNO_CASE: tv.T = t tv.SetUint64(x) case Float32Kind: - x := softfloat.Fuint64to32(uint64(tv.GetUint())) + x := ConvertToSoftFloat32(tv.GetUint()) tv.T = t tv.SetFloat32(x) case Float64Kind: - x := softfloat.Fuint64to64(uint64(tv.GetUint())) + x := ConvertToSoftFloat64(tv.GetUint()) tv.T = t tv.SetFloat64(x) case StringKind: @@ -603,11 +602,11 @@ GNO_CASE: tv.T = t tv.SetUint64(x) case Float32Kind: - x := softfloat.Fuint64to32(uint64(tv.GetUint8())) + x := ConvertToSoftFloat32(tv.GetUint8()) tv.T = t tv.SetFloat32(x) case Float64Kind: - x := softfloat.Fuint64to64(uint64(tv.GetUint8())) + x := ConvertToSoftFloat64(tv.GetUint8()) tv.T = t tv.SetFloat64(x) case StringKind: @@ -674,11 +673,11 @@ GNO_CASE: tv.T = t tv.SetUint64(x) case Float32Kind: - x := softfloat.Fuint64to32(uint64(tv.GetUint16())) + x := ConvertToSoftFloat32(tv.GetUint16()) tv.T = t tv.SetFloat32(x) case Float64Kind: - x := softfloat.Fuint64to64(uint64(tv.GetUint16())) + x := ConvertToSoftFloat64(tv.GetUint16()) tv.T = t tv.SetFloat64(x) case StringKind: @@ -747,11 +746,11 @@ GNO_CASE: tv.T = t tv.SetUint64(x) case Float32Kind: - x := softfloat.Fuint64to32(uint64(tv.GetUint32())) + x := ConvertToSoftFloat32(tv.GetUint32()) tv.T = t tv.SetFloat32(x) case Float64Kind: - x := softfloat.Fuint64to64(uint64(tv.GetUint32())) + x := ConvertToSoftFloat64(tv.GetUint32()) tv.T = t tv.SetFloat64(x) case StringKind: @@ -826,11 +825,11 @@ GNO_CASE: tv.T = t tv.SetUint64(x) case Float32Kind: - x := softfloat.Fuint64to32(tv.GetUint64()) + x := ConvertToSoftFloat32(tv.GetUint64()) tv.T = t tv.SetFloat32(x) case Float64Kind: - x := softfloat.Fuint64to64(tv.GetUint64()) + x := ConvertToSoftFloat64(tv.GetUint64()) tv.T = t tv.SetFloat64(x) case StringKind: @@ -848,158 +847,147 @@ GNO_CASE: switch k { case IntKind: validate(Float32Kind, IntKind, func() bool { - val := softfloat.F32to64(tv.GetFloat32()) - trunc := softfloat.Trunc(val) + trunc := tv.GetFloat32().Trunc() - if cmp, _ := softfloat.Fcmp64(val, trunc); cmp != 0 { + if !trunc.Eq(tv.GetFloat32()) { return false } - truncInt64 := softfloat.F64toint64(trunc) + truncInt64 := trunc.Int64() return truncInt64 >= math.MinInt && truncInt64 <= math.MaxInt }) - x := int(softfloat.F32toint64(tv.GetFloat32())) + x := tv.GetFloat32().Int() tv.T = t tv.SetInt(x) case Int8Kind: validate(Float32Kind, Int8Kind, func() bool { - val := softfloat.F32to64(tv.GetFloat32()) - trunc := softfloat.Trunc(val) + trunc := tv.GetFloat32().Trunc() - if cmp, _ := softfloat.Fcmp64(val, trunc); cmp != 0 { + if !trunc.Eq(tv.GetFloat32()) { return false } - truncInt64 := softfloat.F64toint64(trunc) + truncInt64 := trunc.Int64() return truncInt64 >= math.MinInt8 && truncInt64 <= math.MaxInt8 }) - x := int8(softfloat.F32toint32(tv.GetFloat32())) + x := tv.GetFloat32().Int8() tv.T = t tv.SetInt8(x) case Int16Kind: validate(Float32Kind, Int16Kind, func() bool { - val := softfloat.F32to64(tv.GetFloat32()) - trunc := softfloat.Trunc(val) + trunc := tv.GetFloat32().Trunc() - if cmp, _ := softfloat.Fcmp64(val, trunc); cmp != 0 { + if !trunc.Eq(tv.GetFloat32()) { return false } - truncInt64 := softfloat.F64toint64(trunc) + truncInt64 := trunc.Int64() return truncInt64 >= math.MinInt16 && truncInt64 <= math.MaxInt16 }) - x := int16(softfloat.F32toint32(tv.GetFloat32())) + x := tv.GetFloat32().Int16() tv.T = t tv.SetInt16(x) case Int32Kind: validate(Float32Kind, Int32Kind, func() bool { - val := softfloat.F32to64(tv.GetFloat32()) - trunc := softfloat.Trunc(val) + trunc := tv.GetFloat32().Trunc() - if cmp, _ := softfloat.Fcmp64(val, trunc); cmp != 0 { + if !trunc.Eq(tv.GetFloat32()) { return false } - truncInt64 := softfloat.F64toint64(trunc) + truncInt64 := trunc.Int64() return truncInt64 >= math.MinInt32 && truncInt64 <= math.MaxInt32 }) - x := softfloat.F32toint32(tv.GetFloat32()) + x := tv.GetFloat32().Int32() tv.T = t tv.SetInt32(x) case Int64Kind: validate(Float32Kind, Int64Kind, func() bool { - val := softfloat.F32to64(tv.GetFloat32()) - trunc := softfloat.Trunc(val) + trunc := tv.GetFloat32().Trunc() - cmp, _ := softfloat.Fcmp64(val, trunc) - return cmp == 0 + return tv.GetFloat32().Eq(trunc) }) - x := softfloat.F32toint64(tv.GetFloat32()) + x := tv.GetFloat32().Int64() tv.T = t tv.SetInt64(x) case UintKind: validate(Float32Kind, UintKind, func() bool { - val := softfloat.F32to64(tv.GetFloat32()) - trunc := softfloat.Trunc(val) + trunc := tv.GetFloat32().Trunc() - if cmp, _ := softfloat.Fcmp64(val, trunc); cmp != 0 { + if !trunc.Eq(tv.GetFloat32()) { return false } - truncUint64 := softfloat.F64touint64(trunc) + truncUint64 := trunc.Uint64() return truncUint64 >= 0 && truncUint64 <= math.MaxUint }) - x := uint(softfloat.F32touint64(tv.GetFloat32())) + x := tv.GetFloat32().Uint() tv.T = t tv.SetUint(x) case Uint8Kind: validate(Float32Kind, Uint8Kind, func() bool { - val := softfloat.F32to64(tv.GetFloat32()) - trunc := softfloat.Trunc(val) + trunc := tv.GetFloat32().Trunc() - if cmp, _ := softfloat.Fcmp64(val, trunc); cmp != 0 { + if !trunc.Eq(tv.GetFloat32()) { return false } - truncUint64 := softfloat.F64touint64(trunc) + truncUint64 := trunc.Uint64() return truncUint64 >= 0 && truncUint64 <= math.MaxUint8 }) - x := uint8(softfloat.F32touint64(tv.GetFloat32())) + x := tv.GetFloat32().Uint8() tv.T = t tv.SetUint8(x) case Uint16Kind: validate(Float32Kind, Uint16Kind, func() bool { - val := softfloat.F32to64(tv.GetFloat32()) - trunc := softfloat.Trunc(val) + trunc := tv.GetFloat32().Trunc() - if cmp, _ := softfloat.Fcmp64(val, trunc); cmp != 0 { + if !trunc.Eq(tv.GetFloat32()) { return false } - truncUint64 := softfloat.F64touint64(trunc) + truncUint64 := trunc.Uint64() return truncUint64 >= 0 && truncUint64 <= math.MaxUint16 }) - x := uint16(softfloat.F32touint64(tv.GetFloat32())) + x := tv.GetFloat32().Uint16() tv.T = t tv.SetUint16(x) case Uint32Kind: validate(Float32Kind, Uint32Kind, func() bool { - val := softfloat.F32to64(tv.GetFloat32()) - trunc := softfloat.Trunc(val) + trunc := tv.GetFloat32().Trunc() - if cmp, _ := softfloat.Fcmp64(val, trunc); cmp != 0 { + if !trunc.Eq(tv.GetFloat32()) { return false } - truncUint64 := softfloat.F64touint64(trunc) + truncUint64 := trunc.Uint64() return truncUint64 >= 0 && truncUint64 <= math.MaxUint32 }) - x := uint32(softfloat.F32touint64(tv.GetFloat32())) + x := tv.GetFloat32().Uint32() tv.T = t tv.SetUint32(x) case Uint64Kind: validate(Float32Kind, Uint64Kind, func() bool { - val := softfloat.F32to64(tv.GetFloat32()) - trunc := softfloat.Trunc(val) + trunc := tv.GetFloat32().Trunc() - if val != trunc { + if !trunc.Eq(tv.GetFloat32()) { return false } - truncUint64 := softfloat.F64touint64(trunc) + truncUint64 := trunc.Uint64() return truncUint64 >= 0 && truncUint64 <= math.MaxUint }) - x := softfloat.F32touint64(tv.GetFloat32()) + x := tv.GetFloat32().Uint64() tv.T = t tv.SetUint64(x) case Float32Kind: @@ -1007,7 +995,7 @@ GNO_CASE: tv.T = t tv.SetFloat32(x) case Float64Kind: - x := softfloat.F32to64(tv.GetFloat32()) // ??? + x := tv.GetFloat32().SoftFloat64() /// ??? tv.T = t tv.SetFloat64(x) default: @@ -1019,166 +1007,156 @@ GNO_CASE: switch k { case IntKind: validate(Float64Kind, IntKind, func() bool { - val := tv.GetFloat64() - trunc := softfloat.Trunc(val) + trunc := tv.GetFloat64().Trunc() - if cmp, _ := softfloat.Fcmp64(val, trunc); cmp != 0 { + if !trunc.Eq(tv.GetFloat64()) { return false } - truncInt64 := softfloat.F64toint64(val) + truncInt64 := trunc.Int64() return truncInt64 >= math.MinInt && truncInt64 <= math.MaxInt }) - x := int(softfloat.F64toint64(tv.GetFloat64())) + x := tv.GetFloat64().Int() tv.T = t tv.SetInt(x) case Int8Kind: validate(Float64Kind, Int8Kind, func() bool { - val := tv.GetFloat64() - trunc := softfloat.Trunc(val) + trunc := tv.GetFloat64().Trunc() - if cmp, _ := softfloat.Fcmp64(val, trunc); cmp != 0 { + if !trunc.Eq(tv.GetFloat64()) { return false } - truncInt64 := softfloat.F64toint64(val) + truncInt64 := trunc.Int64() return truncInt64 >= math.MinInt8 && truncInt64 <= math.MaxInt8 }) - x := int8(softfloat.F64toint32(tv.GetFloat64())) + x := tv.GetFloat64().Int8() tv.T = t tv.SetInt8(x) case Int16Kind: validate(Float64Kind, Int16Kind, func() bool { - val := tv.GetFloat64() - trunc := softfloat.Trunc(val) + trunc := tv.GetFloat64().Trunc() - if cmp, _ := softfloat.Fcmp64(val, trunc); cmp != 0 { + if !trunc.Eq(tv.GetFloat64()) { return false } - truncInt64 := softfloat.F64toint64(val) + truncInt64 := trunc.Int64() return truncInt64 >= math.MinInt16 && truncInt64 <= math.MaxInt16 }) - x := int16(softfloat.F64toint32(tv.GetFloat64())) + x := tv.GetFloat64().Int16() tv.T = t tv.SetInt16(x) case Int32Kind: validate(Float64Kind, Int32Kind, func() bool { - val := tv.GetFloat64() - trunc := softfloat.Trunc(val) + trunc := tv.GetFloat64().Trunc() - if cmp, _ := softfloat.Fcmp64(val, trunc); cmp != 0 { + if !trunc.Eq(tv.GetFloat64()) { return false } - truncInt64 := softfloat.F64toint64(val) + truncInt64 := trunc.Int64() return truncInt64 >= math.MinInt32 && truncInt64 <= math.MaxInt32 }) - x := softfloat.F64toint32(tv.GetFloat64()) + x := tv.GetFloat64().Int32() tv.T = t tv.SetInt32(x) case Int64Kind: validate(Float64Kind, Int64Kind, func() bool { - val := tv.GetFloat64() - trunc := softfloat.Trunc(val) + trunc := tv.GetFloat64().Trunc() - cmp, _ := softfloat.Fcmp64(val, trunc) - return cmp == 0 + return tv.GetFloat64().Eq(trunc) }) - x := softfloat.F64toint64(tv.GetFloat64()) + x := tv.GetFloat64().Int64() tv.T = t tv.SetInt64(x) case UintKind: validate(Float64Kind, UintKind, func() bool { - val := tv.GetFloat64() - trunc := softfloat.Trunc(val) + trunc := tv.GetFloat64().Trunc() - if cmp, _ := softfloat.Fcmp64(val, trunc); cmp != 0 { + if !trunc.Eq(tv.GetFloat64()) { return false } - return trunc >= 0 && trunc <= math.MaxUint + truncUint64 := trunc.Uint64() + + return truncUint64 >= 0 && truncUint64 <= math.MaxUint }) - x := uint(softfloat.F64touint64(tv.GetFloat64())) + x := tv.GetFloat64().Uint() tv.T = t tv.SetUint(x) case Uint8Kind: validate(Float64Kind, Uint8Kind, func() bool { - val := tv.GetFloat64() - trunc := softfloat.Trunc(val) + trunc := tv.GetFloat64().Trunc() - if cmp, _ := softfloat.Fcmp64(val, trunc); cmp != 0 { + if !trunc.Eq(tv.GetFloat64()) { return false } - truncInt64 := softfloat.F64toint64(val) - return truncInt64 >= 0 && truncInt64 <= math.MaxUint8 + truncUint64 := trunc.Uint64() + return truncUint64 >= 0 && truncUint64 <= math.MaxUint8 }) - x := uint8(softfloat.F64touint64(tv.GetFloat64())) + x := tv.GetFloat64().Uint8() tv.T = t tv.SetUint8(x) case Uint16Kind: validate(Float64Kind, Uint16Kind, func() bool { - val := tv.GetFloat64() - trunc := softfloat.Trunc(val) + trunc := tv.GetFloat64().Trunc() - if cmp, _ := softfloat.Fcmp64(val, trunc); cmp != 0 { + if !trunc.Eq(tv.GetFloat64()) { return false } - truncInt64 := softfloat.F64toint64(val) - return truncInt64 >= 0 && truncInt64 <= math.MaxUint16 + truncUint64 := trunc.Uint64() + return truncUint64 >= 0 && truncUint64 <= math.MaxUint16 }) - x := uint16(softfloat.F64touint64(tv.GetFloat64())) + x := tv.GetFloat64().Uint16() tv.T = t tv.SetUint16(x) case Uint32Kind: validate(Float64Kind, Uint32Kind, func() bool { - val := tv.GetFloat64() - trunc := softfloat.Trunc(val) + trunc := tv.GetFloat64().Trunc() - if cmp, _ := softfloat.Fcmp64(val, trunc); cmp != 0 { + if !trunc.Eq(tv.GetFloat64()) { return false } - truncInt64 := softfloat.F64toint64(val) - return truncInt64 >= 0 && truncInt64 <= math.MaxUint32 + truncUint64 := trunc.Uint64() + return truncUint64 >= 0 && truncUint64 <= math.MaxUint32 }) - x := uint32(softfloat.F64touint64(tv.GetFloat64())) + x := tv.GetFloat64().Uint32() tv.T = t tv.SetUint32(x) case Uint64Kind: validate(Float64Kind, Uint64Kind, func() bool { - val := tv.GetFloat64() - trunc := softfloat.Trunc(val) + trunc := tv.GetFloat64().Trunc() - if cmp, _ := softfloat.Fcmp64(val, trunc); cmp != 0 { + if !trunc.Eq(tv.GetFloat64()) { return false } - truncUint64 := softfloat.F64touint64(val) + truncUint64 := trunc.Uint64() return truncUint64 >= 0 && truncUint64 <= math.MaxUint64 }) - x := softfloat.F64touint64(tv.GetFloat64()) + x := tv.GetFloat64().Uint64() tv.T = t tv.SetUint64(x) case Float32Kind: validate(Float64Kind, Float32Kind, func() bool { - cmp, _ := softfloat.Fcmp64(tv.GetFloat64(), softfloat.Float64(math.Float64bits(float64(math.MaxFloat32)))) - return cmp <= 0 + return tv.GetFloat64().Le(SoftFloat64(math.Float64bits(float64(math.MaxFloat32)))) }) - x := softfloat.F64to32(tv.GetFloat64()) + x := tv.GetFloat64().SoftFloat32() tv.T = t tv.SetFloat32(x) case Float64Kind: @@ -1502,7 +1480,7 @@ func ConvertUntypedBigintTo(dst *TypedValue, bv BigintValue, t Type) { if f32 == 0 && (acc == big.Below || acc == big.Above) { panic("bigint underflows float32 (too close to zero)") } - dst.SetFloat32(softfloat.Float32(math.Float32bits(f32))) + dst.SetFloat32(SoftFloat32(math.Float32bits(f32))) return // done case Float64Kind: dst.T = t @@ -1516,7 +1494,7 @@ func ConvertUntypedBigintTo(dst *TypedValue, bv BigintValue, t Type) { if f64 == 0 && (acc == big.Below || acc == big.Above) { panic("bigint underflows float64 (too close to zero)") } - dst.SetFloat64(softfloat.Float64(math.Float64bits(f64))) + dst.SetFloat64(SoftFloat64(math.Float64bits(f64))) return // done case BigdecKind: dst.T = t @@ -1631,7 +1609,7 @@ func ConvertUntypedBigdecTo(dst *TypedValue, bv BigdecValue, t Type) { dst.T = Float64Type dst.V = nil f, _ := bd.Float64() - dst.SetFloat64(softfloat.Float64(math.Float64bits(f))) + dst.SetFloat64(SoftFloat64(math.Float64bits(f))) return case IntKind, Int8Kind, Int16Kind, Int32Kind, Int64Kind: fallthrough @@ -1657,7 +1635,7 @@ func ConvertUntypedBigdecTo(dst *TypedValue, bv BigdecValue, t Type) { if math.IsInf(float64(f32), 0) { panic("cannot convert untyped bigdec to float32 -- too close to +-Inf") } - dst.SetFloat32(softfloat.Float32(math.Float32bits(f32))) + dst.SetFloat32(SoftFloat32(math.Float32bits(f32))) return case Float64Kind: dst.T = t @@ -1669,7 +1647,7 @@ func ConvertUntypedBigdecTo(dst *TypedValue, bv BigdecValue, t Type) { if math.IsInf(f64, 0) { panic("cannot convert untyped bigdec to float64 -- too close to +-Inf") } - dst.SetFloat64(softfloat.Float64(math.Float64bits(f64))) + dst.SetFloat64(SoftFloat64(math.Float64bits(f64))) return default: panic(fmt.Sprintf( diff --git a/gnovm/pkg/gnolang/values_conversions_test.go b/gnovm/pkg/gnolang/values_conversions_test.go index 67f14658354..2899fabda3d 100644 --- a/gnovm/pkg/gnolang/values_conversions_test.go +++ b/gnovm/pkg/gnolang/values_conversions_test.go @@ -6,7 +6,6 @@ import ( "testing" "github.com/cockroachdb/apd/v3" - "github.com/gnolang/gno/gnovm/pkg/gnolang/softfloat" "github.com/stretchr/testify/require" ) @@ -25,7 +24,7 @@ func TestConvertUntypedBigdecToFloat(t *testing.T) { ConvertUntypedBigdecTo(dst, bd, typ) - require.Equal(t, softfloat.Fintto64(0), dst.GetFloat64()) + require.Equal(t, ConvertToSoftFloat64(0), dst.GetFloat64()) } func TestBitShiftingOverflow(t *testing.T) { From 004d5d253e0c594dad1d81e5d46bced8d5db5818 Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Tue, 3 Dec 2024 22:16:28 +0100 Subject: [PATCH 6/6] fix: test --- gnovm/pkg/gnolang/internal/softfloat/softfloat.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gnovm/pkg/gnolang/internal/softfloat/softfloat.go b/gnovm/pkg/gnolang/internal/softfloat/softfloat.go index 4aafbba60a0..73f7fa79be0 100644 --- a/gnovm/pkg/gnolang/internal/softfloat/softfloat.go +++ b/gnovm/pkg/gnolang/internal/softfloat/softfloat.go @@ -36,12 +36,12 @@ func Feq32(f, g uint32) bool { return feq32(f, g) } func Fgt32(f, g uint32) bool { return fgt32(f, g) } func Fge32(f, g uint32) bool { return fge32(f, g) } func Flt32(f, g uint32) bool { - cmp, nan := fcmp64(f32to64(f), f32to64(f)) + cmp, nan := fcmp64(f32to64(f), f32to64(g)) return cmp <= -1 && !nan } func Fle32(f, g uint32) bool { - cmp, nan := fcmp64(f32to64(f), f32to64(f)) + cmp, nan := fcmp64(f32to64(f), f32to64(g)) return cmp <= 0 && !nan }