forked from woodsbury/decimal128
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdecimal.go
More file actions
284 lines (225 loc) · 6.08 KB
/
decimal.go
File metadata and controls
284 lines (225 loc) · 6.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
// Package decimal128 provides a 128-bit decimal floating point type.
package decimal128
const (
exponentBias = 6176
maxBiasedExponent = 12287
maxUnbiasedExponent = maxBiasedExponent - exponentBias
minBiasedExponent = 0
minUnbiasedExponent = minBiasedExponent - exponentBias
maxDigits = 35
)
// Decimal represents a 128-bit decimal floating point value. The zero value
// for Decimal is the number +0.0.
type Decimal struct {
lo, hi uint64
}
// Abs returns a new Decimal set to the absolute value of d.
func Abs(d Decimal) Decimal {
return Decimal{d.lo, d.hi & 0x7fff_ffff_ffff_ffff}
}
// Frexp breaks a finite, non-zero d into a fraction and an integral power of
// ten. The absolute value of the fraction will be in the interval [0.1, 1).
//
// If d is ±Inf, NaN, or zero the value is returned unchanged and the returned
// power of ten is zero.
func Frexp(d Decimal) (Decimal, int) {
if d.isSpecial() || d.IsZero() {
return d, 0
}
sig, exp := d.decompose()
rexp := int(exp) - exponentBias + sig.log10() + 1
exp -= int16(rexp)
return compose(d.Signbit(), sig, exp), rexp
}
// Inf returns a new Decimal set to positive infinity if sign >= 0, or negative
// infinity if sign < 0.
func Inf(sign int) Decimal {
return inf(sign < 0)
}
// Ldexp is the inverse of [Frexp], returning frac × 10**exp.
func Ldexp(frac Decimal, exp int) Decimal {
if frac.isSpecial() || frac.IsZero() {
return frac
}
if exp < minUnbiasedExponent {
return zero(frac.Signbit())
}
if exp > maxUnbiasedExponent+39 {
return inf(frac.Signbit())
}
neg := frac.Signbit()
fsig, fexp := frac.decompose()
fexp += int16(exp)
sig, exp16 := DefaultRoundingMode.reduce128(neg, fsig, fexp, 0)
if exp16 > maxBiasedExponent {
return inf(neg)
}
return compose(neg, sig, exp16)
}
// NaN returns a new Decimal set to the "not-a-number" value.
func NaN() Decimal {
return nan(payloadOpNaN, 0, 0)
}
// New returns a new Decimal with the provided significand and exponent.
func New(sig int64, exp int) Decimal {
if sig == 0 {
return zero(false)
}
neg := false
if sig < 0 {
neg = true
sig *= -1
}
if exp < minUnbiasedExponent+19 {
return zero(neg)
}
if exp > maxUnbiasedExponent+39 {
return inf(neg)
}
sig128, exp16 := DefaultRoundingMode.reduce64(neg, uint64(sig), int16(exp+exponentBias))
if exp16 > maxBiasedExponent {
return inf(neg)
}
return compose(neg, sig128, exp16)
}
func compose(neg bool, sig uint128, exp int16) Decimal {
var hi uint64
if sig[1] > 0x0001_ffff_ffff_ffff {
hi = 0x6000_0000_0000_0000 | uint64(exp)<<47 | sig[1]&0x7fff_ffff_ffff
} else {
hi = uint64(exp)<<49 | sig[1]
}
if neg {
hi |= 0x8000_0000_0000_0000
}
return Decimal{sig[0], hi}
}
func inf(neg bool) Decimal {
if neg {
return Decimal{0, 0xf800_0000_0000_0000}
}
return Decimal{0, 0x7800_0000_0000_0000}
}
func nan(op, lhs, rhs Payload) Decimal {
return Decimal{uint64(op | lhs<<8 | rhs<<16), 0x7c00_0000_0000_0000}
}
func one(neg bool) Decimal {
if neg {
return Decimal{1, 0xb040_0000_0000_0000}
}
return Decimal{1, 0x3040_0000_0000_0000}
}
func zero(neg bool) Decimal {
if neg {
return Decimal{0, 0x8000_0000_0000_0000}
}
return Decimal{}
}
// Canonical returns the result of converting d into its canonical
// representation. Many values have multiple possible ways of being represented
// as a Decimal. Canonical converts each of these into a single representation.
//
// If d is ±Inf or NaN, the canonical representation consists of only the bits
// required to represent the respective special floating point value with all
// other bits set to 0. For NaN values this also removes any payload it may
// have had.
//
// If d is ±0, the canonical representation consists of only the sign bit set
// based on the sign of the value with all other bits set to 0.
//
// If d is finite and non-zero, the canonical representation is calculated as
// the representation with an exponent closest to zero that still accurately
// stores all non-zero digits the value has.
func (d Decimal) Canonical() Decimal {
if d.isSpecial() {
if d.IsNaN() {
return nan(0, 0, 0)
}
return inf(d.Signbit())
}
sig, exp := d.decompose()
if sig[0]|sig[1] == 0 {
return zero(d.Signbit())
}
for exp > exponentBias {
tmp := sig.mul64(10)
if tmp[1] > 0x0002_7fff_ffff_ffff {
break
}
sig = tmp
exp--
}
for exp < exponentBias {
tmp, rem := sig.div10()
if rem != 0 {
break
}
sig = tmp
exp++
}
return compose(d.Signbit(), sig, exp)
}
// IsInf reports whether d is an infinity. If sign > 0, IsInf reports whether
// d is positive infinity. If sign < 0, IsInf reports whether d is negative
// infinity. If sign == 0, IsInf reports whether d is either infinity.
func (d Decimal) IsInf(sign int) bool {
if !d.isInf() {
return false
}
if sign == 0 {
return true
}
if sign > 0 {
return !d.Signbit()
}
return d.Signbit()
}
// IsNaN reports whether d is a "not-a-number" value.
func (d Decimal) IsNaN() bool {
return d.hi&0x7c00_0000_0000_0000 == 0x7c00_0000_0000_0000
}
// Neg returns d with its sign negated.
func (d Decimal) Neg() Decimal {
return Decimal{d.lo, d.hi ^ 0x8000_0000_0000_0000}
}
// Sign returns:
//
// -1 if d < 0
// 0 if d is ±0
// +1 if d > 0
//
// It panics if d is NaN.
func (d Decimal) Sign() int {
if d.IsNaN() {
panic("Decimal(NaN).Sign()")
}
if d.IsZero() {
return 0
}
if d.Signbit() {
return -1
}
return 1
}
// Signbit reports whether d is negative or negative zero.
func (d Decimal) Signbit() bool {
return d.hi&0x8000_0000_0000_0000 == 0x8000_0000_0000_0000
}
func (d Decimal) decompose() (uint128, int16) {
var sig uint128
var exp int16
if d.hi&0x6000_0000_0000_0000 == 0x6000_0000_0000_0000 {
sig = uint128{d.lo, d.hi&0x7fff_ffff_ffff | 0x0002_0000_0000_0000}
exp = int16(d.hi & 0x1fff_8000_0000_0000 >> 47)
} else {
sig = uint128{d.lo, d.hi & 0x0001_ffff_ffff_ffff}
exp = int16(d.hi & 0x7ffe_0000_0000_0000 >> 49)
}
return sig, exp
}
func (d Decimal) isInf() bool {
return d.hi&0x7c00_0000_0000_0000 == 0x7800_0000_0000_0000
}
func (d Decimal) isSpecial() bool {
return d.hi&0x7800_0000_0000_0000 == 0x7800_0000_0000_0000
}