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}