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