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}