arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
hermez-node/utils/utils.go
utils/utils.goRaw
  1//nolint:gomnd
  2package utils
  3
  4import (
  5	"encoding/binary"
  6	"errors"
  7	"math/big"
  8)
  9
 10var (
 11	// ErrRoundingLoss is used when converted big.Int to Float16 causes rounding loss
 12	ErrRoundingLoss = errors.New("input value causes rounding loss")
 13)
 14
 15// Float16 represents a float in a 16 bit format
 16type Float16 uint16
 17
 18// Bytes return a byte array of length 2 with the Float16 value encoded in LittleEndian
 19func (f16 Float16) Bytes() []byte {
 20	var b [2]byte
 21	binary.LittleEndian.PutUint16(b[:], uint16(f16))
 22	return b[:]
 23}
 24
 25// BigInt converts the Float16 to a *big.Int integer
 26func (fl16 *Float16) BigInt() *big.Int {
 27	fl := int64(*fl16)
 28
 29	m := big.NewInt(fl & 0x3FF)
 30	e := big.NewInt(fl >> 11)
 31	e5 := (fl >> 10) & 0x01
 32
 33	exp := big.NewInt(0).Exp(big.NewInt(10), e, nil)
 34	res := m.Mul(m, exp)
 35
 36	if e5 != 0 && e.Cmp(big.NewInt(0)) != 0 {
 37		res.Add(res, exp.Div(exp, big.NewInt(2)))
 38	}
 39	return res
 40}
 41
 42// floorFix2Float converts a fix to a float, always rounding down
 43func floorFix2Float(_f *big.Int) Float16 {
 44	zero := big.NewInt(0)
 45	ten := big.NewInt(10)
 46	e := int64(0)
 47
 48	m := big.NewInt(0)
 49	m.Set(_f)
 50
 51	if m.Cmp(zero) == 0 {
 52		return 0
 53	}
 54
 55	s := big.NewInt(0).Rsh(m, 10)
 56
 57	for s.Cmp(zero) != 0 {
 58		m.Div(m, ten)
 59		s.Rsh(m, 10)
 60		e++
 61	}
 62
 63	return Float16(m.Int64() | e<<11)
 64}
 65
 66// NewFloat16 encodes a *big.Int integer as a Float16, returning error in case
 67// of loss during the encoding.
 68func NewFloat16(f *big.Int) (Float16, error) {
 69	fl1 := floorFix2Float(f)
 70	fi1 := fl1.BigInt()
 71	fl2 := fl1 | 0x400
 72	fi2 := fl2.BigInt()
 73
 74	m3 := (fl1 & 0x3FF) + 1
 75	e3 := fl1 >> 11
 76
 77	if m3&0x400 == 0 {
 78		m3 = 0x66
 79		e3++
 80	}
 81
 82	fl3 := m3 + e3<<11
 83	fi3 := fl3.BigInt()
 84
 85	res := fl1
 86
 87	d := big.NewInt(0).Abs(fi1.Sub(fi1, f))
 88	d2 := big.NewInt(0).Abs(fi2.Sub(fi2, f))
 89
 90	if d.Cmp(d2) == 1 {
 91		res = fl2
 92		d = d2
 93	}
 94
 95	d3 := big.NewInt(0).Abs(fi3.Sub(fi3, f))
 96
 97	if d.Cmp(d3) == 1 {
 98		res = fl3
 99	}
100
101	// Do rounding check
102	if res.BigInt().Cmp(f) == 0 {
103		return res, nil
104	}
105	return res, ErrRoundingLoss
106}
107
108// NewFloat16Floor encodes a big.Int integer as a Float16, rounding down in
109// case of loss during the encoding.
110func NewFloat16Floor(f *big.Int) Float16 {
111	fl1 := floorFix2Float(f)
112	fl2 := fl1 | 0x400
113	fi2 := fl2.BigInt()
114
115	if fi2.Cmp(f) < 1 {
116		return fl2
117	}
118	return fl1
119}