Commit 348cc6f1 authored by Michael Mclaughlin's avatar Michael Mclaughlin Committed by Commit Bot

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: 's avatarJakob Kummerow <jkummerow@chromium.org>
Reviewed-by: 's avatarMathias Bynens <mathias@chromium.org>
Cr-Commit-Position: refs/heads/master@{#61925}
parent f75c90a6
......@@ -127,6 +127,7 @@ Matthew Sporleder <msporleder@gmail.com>
Maxim Mazurok <maxim@mazurok.com>
Maxim Mossienko <maxim.mossienko@gmail.com>
Michael Lutz <michi@icosahedron.de>
Michael Mclaughlin <m8ch88l@gmail.com>
Michael Smith <mike@w3.org>
Michaël Zasso <mic.besace@gmail.com>
Mike Gilbert <floppymaster@gmail.com>
......
......@@ -1245,7 +1245,7 @@ char* DoubleToRadixCString(double value, int radix) {
double delta = 0.5 * (Double(value).NextDouble() - value);
delta = std::max(Double(0.0).NextDouble(), delta);
DCHECK_GT(delta, 0.0);
if (fraction > delta) {
if (fraction >= delta) {
// Insert decimal point.
buffer[fraction_cursor++] = '.';
do {
......@@ -1280,7 +1280,7 @@ char* DoubleToRadixCString(double value, int radix) {
break;
}
}
} while (fraction > delta);
} while (fraction >= delta);
}
// Compute integer digits. Fill unrepresented digits with zero.
......
// 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);
// Copyright 2016 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.
assertEquals("0", Number.MIN_VALUE.toString(35));
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment