-
Notifications
You must be signed in to change notification settings - Fork 253
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(NODE-4932): remove .0 suffix from double extended json values #553
Changes from 6 commits
578389f
318a53b
114ca90
40757a0
8734edd
4c93bf3
eaaa214
c3c6346
9fabfe5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -38,26 +38,82 @@ describe('BSON Double Precision', function () { | |||||
|
||||||
describe('.toExtendedJSON()', () => { | ||||||
const tests = [ | ||||||
{ input: new Double(0), output: { $numberDouble: '0.0' } }, | ||||||
{ input: new Double(-0), output: { $numberDouble: '-0.0' } }, | ||||||
{ input: new Double(3), output: { $numberDouble: '3.0' } }, | ||||||
{ input: new Double(-3), output: { $numberDouble: '-3.0' } }, | ||||||
{ input: new Double(3.4), output: { $numberDouble: '3.4' } }, | ||||||
{ input: new Double(Number.EPSILON), output: { $numberDouble: '2.220446049250313e-16' } }, | ||||||
{ input: new Double(12345e7), output: { $numberDouble: '123450000000.0' } }, | ||||||
{ input: new Double(12345e-1), output: { $numberDouble: '1234.5' } }, | ||||||
{ input: new Double(-12345e-1), output: { $numberDouble: '-1234.5' } }, | ||||||
{ input: new Double(Infinity), output: { $numberDouble: 'Infinity' } }, | ||||||
{ input: new Double(-Infinity), output: { $numberDouble: '-Infinity' } }, | ||||||
{ input: new Double(NaN), output: { $numberDouble: 'NaN' } } | ||||||
{ input: 0, output: { $numberDouble: '0.0' } }, | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. there's a lot of test cases here now, I think it's worthwhile to add descriptions? The
Suggested change
I'd like this change because it's not clear why each test is necessary. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. added titles |
||||||
{ input: -0, output: { $numberDouble: '-0.0' } }, | ||||||
{ input: '-0.0', output: { $numberDouble: '-0.0' } }, | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What value does this test provide? The constructor of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not true according to runtime, we do support string inputs, should we make that fix here or in another ticket? As for the test I was just ensuring the decimal point (.0) doesn't break things here. |
||||||
{ input: 3, output: { $numberDouble: '3.0' } }, | ||||||
{ input: -3, output: { $numberDouble: '-3.0' } }, | ||||||
{ input: 3.4, output: { $numberDouble: '3.4' } }, | ||||||
{ input: Number.EPSILON, output: { $numberDouble: '2.220446049250313e-16' } }, | ||||||
{ input: 12345e7, output: { $numberDouble: '123450000000.0' } }, | ||||||
{ input: 12345e-1, output: { $numberDouble: '1234.5' } }, | ||||||
{ input: -12345e-1, output: { $numberDouble: '-1234.5' } }, | ||||||
{ input: Infinity, output: { $numberDouble: 'Infinity' } }, | ||||||
{ input: -Infinity, output: { $numberDouble: '-Infinity' } }, | ||||||
{ input: NaN, output: { $numberDouble: 'NaN' } }, | ||||||
{ | ||||||
input: Number.MAX_VALUE, | ||||||
output: { $numberDouble: '1.7976931348623157e+308' } | ||||||
}, | ||||||
{ input: Number.MIN_VALUE, output: { $numberDouble: '5e-324' } }, | ||||||
{ | ||||||
input: -Number.MAX_VALUE, | ||||||
output: { $numberDouble: '-1.7976931348623157e+308' } | ||||||
}, | ||||||
{ input: -Number.MIN_VALUE, output: { $numberDouble: '-5e-324' } }, | ||||||
// Reference: https://docs.oracle.com/cd/E19957-01/806-3568/ncg_math.html | ||||||
{ | ||||||
// min positive normal number | ||||||
input: '2.2250738585072014e-308', | ||||||
output: { $numberDouble: '2.2250738585072014e-308' } | ||||||
}, | ||||||
{ | ||||||
// max subnormal number (NOTE: JS does not output same input string, but numeric values are equal) | ||||||
input: '2.225073858507201e-308', | ||||||
output: { $numberDouble: '2.225073858507201e-308' } | ||||||
}, | ||||||
{ | ||||||
// min positive subnormal number (NOTE: JS does not output same input string, but numeric values are equal) | ||||||
input: '4.9406564584124654e-324', | ||||||
output: { $numberDouble: '5e-324' } | ||||||
}, | ||||||
{ | ||||||
// https://262.ecma-international.org/13.0/#sec-number.prototype.tofixed | ||||||
// Note: calling toString on this integer returns 1000000000000000100, so toFixed is more precise | ||||||
input: '1000000000000000128', | ||||||
output: { $numberDouble: '1000000000000000128.0' } | ||||||
} | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. while this is interesting, I'm not sure this provides any additional value. We already have tests that show that both positive and negative integer numbers serialize to numbers with a decimal. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is an integer that's beyond the precision that an 8 byte float can hold (hence why toString loses the 28), it confirms that we still output a string that preserves it if that was the input. (Basically, asserts that toFixed is being used for ints). |
||||||
]; | ||||||
|
||||||
for (const test of tests) { | ||||||
const input = test.input; | ||||||
const output = test.output; | ||||||
const title = `returns ${inspect(output)} when Double is ${input}`; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. a context block around both tests here would make the output of the tests a lot more understandable There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, with what differentiating information? (Is this still relevant with the added titles?) |
||||||
it(title, () => { | ||||||
expect(output).to.deep.equal(input.toExtendedJSON({ relaxed: false })); | ||||||
const inputAsDouble = new Double(input); | ||||||
expect(inputAsDouble.toExtendedJSON({ relaxed: false })).to.deep.equal(output); | ||||||
if (!Number.isNaN(inputAsDouble.value)) { | ||||||
expect(Number(inputAsDouble.toExtendedJSON({ relaxed: false }).$numberDouble)).to.equal( | ||||||
inputAsDouble.value | ||||||
); | ||||||
} | ||||||
Comment on lines
+167
to
+171
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm confused why the if-block and second assertion are necessary. We should already be checking for numeric equality of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not exactly, the |
||||||
}); | ||||||
|
||||||
it(`input ${typeof input}: ${input} creates the same bytes after stringification`, () => { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. iiuc, the important aspect of this test is that the value of the double is preserved through ejson stringification. we just happen to test this by asserting that the byte representations are equal. Right? If so, I propose we adjust the title to reflect what we're actually testing, and add a comment explaining that we determine that we're comparing bytes to assert that no loss of precision occurs.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Precision is related but more so that the string is acting as a correct data transfer format, we want to know that the same bytes can be reconstructed from the generated string, sometimes the string changes "4.9406564584124654e-324" -> "5e-324" but both represent the same ieee754 double bytes. |
||||||
const ejsonDoubleString = new Double(input).toExtendedJSON().$numberDouble; | ||||||
const bytesFromInput = (() => { | ||||||
const b = Buffer.alloc(8); | ||||||
b.writeDoubleBE(Number(input)); | ||||||
return b.toString('hex'); | ||||||
})(); | ||||||
|
||||||
const bytesFromOutput = (() => { | ||||||
const b = Buffer.alloc(8); | ||||||
b.writeDoubleBE(Number(ejsonDoubleString)); | ||||||
return b.toString('hex'); | ||||||
})(); | ||||||
|
||||||
expect(bytesFromOutput).to.equal(bytesFromInput); | ||||||
}); | ||||||
} | ||||||
}); | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's unclear to me why the changes in this file are necessary. reverting to what's currently in
main
doesn't make any tests fail. could you explain why we're modifying the test runner?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With toFixed(1) reintroduced we don't need to skip the multi-type test anymore, I put that back, but it is senitive to changes in string format as opposed to value equivalence.
The changes here are meant to raise issue in the future if there's a change to the output format that would break round tripping the string into a numeric value for all doubles. Since the if stmt on main filtered only for the
'1.2345678921232E+18'
case we did not need the extra handling for the NaN and -0 cases. The second section which tests round tripping through BSON needs to handle the cases where EJSON automatically converts values to int/long/double based on size and fraction.