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