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}