v8/test/mjsunit/number-tostring-subnormal.js
Michael Mclaughlin 348cc6f152 Improve toString(radix) for doubles near zero
Currently, Number.prototype.toString(radix) often fails to produce the
least significant bit for doubles near zero. For example, for the
minimum double, 5e-324, toString(2) produces "0". This means that a
user cannot reliably get the exact binary or hexdecimal value of a
double from JavaScript using toString.

This patch makes a slight amendment to the DoubleToRadixCString
function, so that doubles where the gap to the next double is 5e-324
(i.e. doubles less than 2**-1021), are represented exactly in binary and
other power-of-two bases, and close to exactly otherwise. It results
in Number.prototype.toString producing the correct binary value for all
doubles.

R=jkummerow@chromium.org, mathias@chromium.org, yangguo@chromium.org

Bug: v8:9294
Change-Id: I71506149b7c4c0eac8c38675a1ee15fb4f36f9ef
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1631601
Commit-Queue: Mathias Bynens <mathias@chromium.org>
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: Mathias Bynens <mathias@chromium.org>
Cr-Commit-Position: refs/heads/master@{#61925}
2019-05-29 20:45:02 +00:00

93 lines
4.4 KiB
JavaScript

// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Tests Number.prototype.toString for numbers within or near the subnormal
// range, when the radix argument is 2.
// A JavaScript number is a IEEE 754 binary64 (double-precision floating-point)
// value, so we should be able to provide a binary string value that is an
// exact representation.
const zeros = count => '0'.repeat(count);
const test = (binaryStringValue, double) => {
assertEquals(binaryStringValue, double.toString(2));
};
// 2**-1074
test(`0.${zeros(1073)}1`, Number.MIN_VALUE);
// Bug v8:9294
test(`0.${zeros(1022)}1101100011110111011100000100011001111101110001010111`,
1.8858070859709815e-308);
test(`0.${zeros(1021)}11110111001111000110110111011101110001000000000000001`,
4.297800585227606e-308);
// Normal doubles smaller than 2**-1021 (4.450147717014403e-308). These values
// are not in the subnormal range, but like the subnormals they have a gap of
// Number.MIN_VALUE between themselves and the next double.
test(`0.${zeros(1021)}11100001110000011100111110011010010100100010001001001`,
3.924423154449847e-308);
test(`-0.${zeros(1021)}11001101101111001101100010110110011000000011010111001`,
-3.57641826104544e-308);
test(`-0.${zeros(1021)}11101100100000100110110001000010010001001000101110001`,
-4.1113361447183043e-308);
test(`0.${zeros(1021)}11111001101000111100111001001101101001001011010001101`,
4.339587042263274e-308);
test(`0.${zeros(1021)}10111011101001010010101011100001110000000001011110001`,
3.261909352323954e-308);
test(`0.${zeros(1021)}10001001101101110110000110001011100001100111111111001`,
2.3939766453008923e-308);
test(`-0.${zeros(1021)}11101001000110010001100111110111001001000001100010011`,
-4.052034242003901e-308);
test(`-0.${zeros(1021)}10001111010010000100000110100010101101001000110010101`,
-2.4907311894031355e-308);
test(`-0.${zeros(1021)}10101100001001010011101010001110011010000001111000001`,
-2.9924709724070097e-308);
test(`-0.${zeros(1021)}11111101001111010110001011001001000100110101001010111`,
-4.402165887028534e-308);
// Subnormal doubles: numbers smaller than 2**-1022 (2.2250738585072014e-308).
test(`0.${zeros(1022)}1100111101100011000101110111111000011111110001001011`,
1.802545172319673e-308);
test(`0.${zeros(1022)}1111001000101011101110111111011111111100111101101011`,
2.104874994274149e-308);
test(`-0.${zeros(1022)}1001110011110110110010010010010001111100001111101011`,
-1.3642832344349763e-308);
test(`0.${zeros(1023)}111101010100011111101110111101011000001110011101101`,
1.0659537476238824e-308);
test(`-0.${zeros(1023)}100101011110101100111101101001110011101011110111101`,
-6.51524700064251e-309);
test(`-0.${zeros(1024)}10011100110100110010111101001001111100000101000111`,
-3.407686279964664e-309);
test(`-0.${zeros(1024)}11101001001010000001101111010101011111111011010111`,
-5.06631661953108e-309);
test(`-0.${zeros(1024)}10111100111100001100010100110011001011000011011101`,
-4.105533080656306e-309);
test(`0.${zeros(1025)}1101111100101101111110101111001010101100001100111`,
2.42476131288505e-309);
test(`-0.${zeros(1025)}1001000100011011100101010010101010101011101000111`,
-1.576540281929606e-309);
test(`0.${zeros(1023)}111101100001000000001011101011101010001110011111101`,
1.0693508455891784e-308);
test(`0.${zeros(1024)}11100010101101001101011010110001110101110100010001`,
4.926157093545696e-309);
test(`-0.${zeros(1027)}10010111100001111110000101011001010111100011011`,
-4.1158103328176e-310);
test(`0.${zeros(1030)}11111010010111010110101101000111010011100001`,
8.500372841691e-311);
test(`0.${zeros(1033)}101001010011111001100100001010001101`,
7.01292938871e-312);
test(`0.${zeros(1037)}11101010101101100111000110100111001`, 6.22574126804e-313);
test(`-0.${zeros(1040)}10100001001101011011111001111111`, -5.3451064043e-314);
test(`-0.${zeros(1043)}1001100101100100000001000011111`, -6.35731246e-315);
test(`0.${zeros(1046)}10101110110100011010110001`, 9.05676893e-316);
test(`-0.${zeros(1050)}11001010000110011100011`, -6.5438353e-317);
test(`0.${zeros(1053)}111001010000010001`, 9.269185e-318);
test(`-0.${zeros(1057)}1100001000010101`, -4.90953e-319);
test(`-0.${zeros(1059)}10011001001111`, -9.6906e-320);
test(`0.${zeros(1063)}111101111`, 9.782e-321);
test(`0.${zeros(1067)}10011`, 3.75e-322);
test(`-0.${zeros(1070)}1`, -4e-323);