@@ -2,6 +2,7 @@ package msgp
22
33import (
44 "math"
5+ "math/bits"
56 "strconv"
67)
78
@@ -77,7 +78,7 @@ func (n *Number) Uint() (uint64, bool) {
7778}
7879
7980// Float casts the number to a float64, and
80- // returns whether or not that was the underlying
81+ // returns whether that was the underlying
8182// type (either a float64 or a float32).
8283func (n * Number ) Float () (float64 , bool ) {
8384 switch n .typ {
@@ -208,6 +209,111 @@ func (n *Number) EncodeMsg(w *Writer) error {
208209 }
209210}
210211
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+
211317// Msgsize implements msgp.Sizer
212318func (n * Number ) Msgsize () int {
213319 switch n .typ {
0 commit comments