Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 21 additions & 9 deletions lib/encoder.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

var Buffer = require('safe-buffer').Buffer
var bl = require('bl')
var TOLERANCE = 0.1

module.exports = function buildEncode (encodingTypes, forceFloat64, compatibilityMode, disableTimestampEncoding) {
function encode (obj, avoidSlice) {
Expand Down Expand Up @@ -301,22 +300,35 @@ function write64BitInt (buf, offset, num) {
}

function isFloat (n) {
return n !== Math.floor(n)
return n % 1 !== 0
}

function encodeFloat (obj, forceFloat64) {
var buf
var useDoublePrecision = true

// If `fround` is supported, we can check if a float
// is double or single precision by rounding the object
// to single precision and comparing the difference.
// If it's not supported, it's safer to use a 64 bit
// float so we don't lose precision without meaning to.
if (Math.fround) {
useDoublePrecision = Math.fround(obj) !== obj
}

buf = Buffer.allocUnsafe(5)
buf[0] = 0xca
buf.writeFloatBE(obj, 1)
if (forceFloat64) {
useDoublePrecision = true
}

var buf

// FIXME is there a way to check if a
// value fits in a float?
if (forceFloat64 || Math.abs(obj - buf.readFloatBE(1)) > TOLERANCE) {
if (useDoublePrecision) {
buf = Buffer.allocUnsafe(9)
buf[0] = 0xcb
buf.writeDoubleBE(obj, 1)
} else {
buf = Buffer.allocUnsafe(5)
buf[0] = 0xca
buf.writeFloatBE(obj, 1)
}

return buf
Expand Down
74 changes: 63 additions & 11 deletions test/floats.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,67 @@ var bl = require('bl')

test('encoding/decoding 32-bits float numbers', function (t) {
var encoder = msgpack()
var allNum = []
var float32 = [
1.5,
0.15625,
-2.5
]

allNum.push(-222.42)
allNum.push(748364.2)
allNum.push(2.2)
var float64 = [
2 ** 150,
1.337,
2.2
]

allNum.forEach(function (num) {
float64.forEach(function (num) {
t.test('encoding ' + num, function (t) {
var buf = encoder.encode(num)
t.equal(buf.length, 9, 'must have 5 bytes')
t.equal(buf[0], 0xcb, 'must have the proper header')

var dec = buf.readDoubleBE(1)
t.equal(dec, num, 'must decode correctly')
t.end()
})

t.test('decoding ' + num, function (t) {
var buf = Buffer.allocUnsafe(9)
var dec
buf[0] = 0xcb
buf.writeDoubleBE(num, 1)

dec = encoder.decode(buf)
t.equal(dec, num, 'must decode correctly')
t.end()
})

t.test('mirror test ' + num, function (t) {
var dec = encoder.decode(encoder.encode(num))
t.equal(dec, num, 'must decode correctly')
t.end()
})
})

float32.forEach(function (num) {
t.test('encoding ' + num, function (t) {
var buf = encoder.encode(num)
var dec = buf.readFloatBE(1)
t.equal(buf.length, 5, 'must have 5 bytes')
t.equal(buf[0], 0xca, 'must have the proper header')
t.true(Math.abs(dec - num) < 0.1, 'must decode correctly')

var dec = buf.readFloatBE(1)
t.equal(dec, num, 'must decode correctly')
t.end()
})

t.test('forceFloat64 encoding ' + num, function (t) {
var enc = msgpack({ forceFloat64: true })
var buf = enc.encode(num)
var dec = buf.readDoubleBE(1)

t.equal(buf.length, 9, 'must have 9 bytes')
t.equal(buf[0], 0xcb, 'must have the proper header')
t.true(Math.abs(dec - num) < 0.1, 'must decode correctly')

var dec = buf.readDoubleBE(1)
t.equal(dec, num, 'must decode correctly')
t.end()
})

Expand All @@ -38,14 +76,15 @@ test('encoding/decoding 32-bits float numbers', function (t) {
var dec
buf[0] = 0xca
buf.writeFloatBE(num, 1)

dec = encoder.decode(buf)
t.true(Math.abs(dec - num) < 0.1, 'must decode correctly')
t.equal(dec, num, 'must decode correctly')
t.end()
})

t.test('mirror test ' + num, function (t) {
var dec = encoder.decode(encoder.encode(num))
t.true(Math.abs(dec - num) < 0.1, 'must decode correctly')
t.equal(dec, num, 'must decode correctly')
t.end()
})
})
Expand All @@ -65,3 +104,16 @@ test('decoding an incomplete 32-bits float numbers', function (t) {
t.equals(buf.length, origLength, 'must not consume any byte')
t.end()
})

test('decoding an incomplete 64-bits float numbers', function (t) {
var encoder = msgpack()
var buf = Buffer.allocUnsafe(8)
buf[0] = 0xcb
buf = bl().append(buf)
var origLength = buf.length
t.throws(function () {
encoder.decode(buf)
}, encoder.IncompleteBufferError, 'must throw IncompleteBufferError')
t.equals(buf.length, origLength, 'must not consume any byte')
t.end()
})