@@ -2,6 +2,7 @@ package msgp
2
2
3
3
import (
4
4
"math"
5
+ "math/bits"
5
6
"strconv"
6
7
)
7
8
@@ -77,7 +78,7 @@ func (n *Number) Uint() (uint64, bool) {
77
78
}
78
79
79
80
// Float casts the number to a float64, and
80
- // returns whether or not that was the underlying
81
+ // returns whether that was the underlying
81
82
// type (either a float64 or a float32).
82
83
func (n * Number ) Float () (float64 , bool ) {
83
84
switch n .typ {
@@ -208,6 +209,111 @@ func (n *Number) EncodeMsg(w *Writer) error {
208
209
}
209
210
}
210
211
212
+ // CoerceInt attempts to coerce the value of
213
+ // the number into a signed integer and returns
214
+ // whether it was successful.
215
+ // "Success" implies that no precision in the value of
216
+ // the number was lost, which means that the number was an integer or
217
+ // a floating point that mapped exactly to an integer without rounding.
218
+ func (n * Number ) CoerceInt () (int64 , bool ) {
219
+ switch n .typ {
220
+ case InvalidType , IntType :
221
+ // InvalidType just means un-initialized.
222
+ return int64 (n .bits ), true
223
+ case UintType :
224
+ return int64 (n .bits ), n .bits <= math .MaxInt64
225
+ case Float32Type :
226
+ f := math .Float32frombits (uint32 (n .bits ))
227
+ if n .isExactInt () && f <= math .MaxInt64 && f >= math .MinInt64 {
228
+ return int64 (f ), true
229
+ }
230
+ if n .bits == 0 || n .bits == 1 << 31 {
231
+ return 0 , true
232
+ }
233
+ case Float64Type :
234
+ f := math .Float64frombits (n .bits )
235
+ if n .isExactInt () && f <= math .MaxInt64 && f >= math .MinInt64 {
236
+ return int64 (f ), true
237
+ }
238
+ return 0 , n .bits == 0 || n .bits == 1 << 63
239
+ }
240
+ return 0 , false
241
+ }
242
+
243
+ // CoerceUInt attempts to coerce the value of
244
+ // the number into an unsigned integer and returns
245
+ // whether it was successful.
246
+ // "Success" implies that no precision in the value of
247
+ // the number was lost, which means that the number was an integer or
248
+ // a floating point that mapped exactly to an integer without rounding.
249
+ func (n * Number ) CoerceUInt () (uint64 , bool ) {
250
+ switch n .typ {
251
+ case InvalidType , IntType :
252
+ // InvalidType just means un-initialized.
253
+ if int64 (n .bits ) >= 0 {
254
+ return n .bits , true
255
+ }
256
+ case UintType :
257
+ return n .bits , true
258
+ case Float32Type :
259
+ f := math .Float32frombits (uint32 (n .bits ))
260
+ if f >= 0 && f <= math .MaxUint64 && n .isExactInt () {
261
+ return uint64 (f ), true
262
+ }
263
+ if n .bits == 0 || n .bits == 1 << 31 {
264
+ return 0 , true
265
+ }
266
+ case Float64Type :
267
+ f := math .Float64frombits (n .bits )
268
+ if f >= 0 && f <= math .MaxUint64 && n .isExactInt () {
269
+ return uint64 (f ), true
270
+ }
271
+ return 0 , n .bits == 0 || n .bits == 1 << 63
272
+ }
273
+ return 0 , false
274
+ }
275
+
276
+ // isExactInt will return true if the number represents an integer value.
277
+ // NaN, Inf returns false.
278
+ func (n * Number ) isExactInt () bool {
279
+ var eBits int // Exponent bits
280
+ var mBits int // Mantissa bits
281
+
282
+ switch n .typ {
283
+ case InvalidType , IntType , UintType :
284
+ return true
285
+ case Float32Type :
286
+ eBits = 8
287
+ mBits = 23
288
+ case Float64Type :
289
+ eBits = 11
290
+ mBits = 52
291
+ default :
292
+ return false
293
+ }
294
+ // Calculate float parts
295
+ exp := int (n .bits >> mBits ) & ((1 << eBits ) - 1 )
296
+ mant := n .bits & ((1 << mBits ) - 1 )
297
+ if exp == 0 && mant == 0 {
298
+ // Handle zero value.
299
+ return true
300
+ }
301
+
302
+ exp -= (1 << (eBits - 1 )) - 1
303
+ if exp < 0 || exp == 1 << (eBits - 1 ) {
304
+ // Negative exponent is never integer (except zero handled above)
305
+ // Handles NaN (exp all 1s)
306
+ return false
307
+ }
308
+
309
+ if exp >= mBits {
310
+ // If we have more exponent than mantissa bits it is always an integer.
311
+ return true
312
+ }
313
+ // Check if all bits below the exponent are zero.
314
+ return bits .TrailingZeros64 (mant ) >= mBits - exp
315
+ }
316
+
211
317
// Msgsize implements msgp.Sizer
212
318
func (n * Number ) Msgsize () int {
213
319
switch n .typ {
0 commit comments