Skip to content

Commit 7384d3f

Browse files
LeszekSwirskiV8 LUCI CQ
authored and
V8 LUCI CQ
committed
Reland "[conversions] Use fast_float for string-to-double conversion."
This is a reland of commit ee2a959 Reland with roll to 6.1.4, which resolves build issues (resolved in Chromium by rolling in crrev.com/c/5783312) Original change's description: > [conversions] Use fast_float for string-to-double conversion. > > Import the fast_float library and use it for string to double > conversion, rather than maintaining our own implementation. > > Bug: 355603404 > Change-Id: I7fbd4933739fc8beb470196124010bb2d52a5aa7 > Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/5729413 > Auto-Submit: Leszek Swirski <leszeks@chromium.org> > Reviewed-by: Patrick Thier <pthier@chromium.org> > Commit-Queue: Patrick Thier <pthier@chromium.org> > Cr-Commit-Position: refs/heads/main@{#95534} Bug: 355603404 Change-Id: Ie4e5109217a68b2fa242cb1172654cb7fd3f8b97 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/5790306 Auto-Submit: Leszek Swirski <leszeks@chromium.org> Commit-Queue: Patrick Thier <pthier@chromium.org> Commit-Queue: Leszek Swirski <leszeks@chromium.org> Reviewed-by: Patrick Thier <pthier@chromium.org> Cr-Commit-Position: refs/heads/main@{#95686}
1 parent 43717d0 commit 7384d3f

File tree

9 files changed

+193
-175
lines changed

9 files changed

+193
-175
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@
6868
/third_party/colorama/src
6969
!/third_party/cpu_features
7070
/third_party/cpu_features/src
71+
!/third_party/fast_float
72+
/third_party/fast_float/src
7173
!/third_party/glibc
7274
!/third_party/googletest
7375
/third_party/googletest/src

BUILD.bazel

+20
Original file line numberDiff line numberDiff line change
@@ -3796,6 +3796,25 @@ filegroup(
37963796
}),
37973797
)
37983798

3799+
v8_library(
3800+
name = "lib_fast_float",
3801+
srcs = [
3802+
"third_party/fast_float/src/include/fast_float/ascii_number.h",
3803+
"third_party/fast_float/src/include/fast_float/bigint.h",
3804+
"third_party/fast_float/src/include/fast_float/constexpr_feature_detect.h",
3805+
"third_party/fast_float/src/include/fast_float/decimal_to_binary.h",
3806+
"third_party/fast_float/src/include/fast_float/digit_comparison.h",
3807+
"third_party/fast_float/src/include/fast_float/fast_float.h",
3808+
"third_party/fast_float/src/include/fast_float/fast_table.h",
3809+
"third_party/fast_float/src/include/fast_float/float_common.h",
3810+
"third_party/fast_float/src/include/fast_float/parse_number.h",
3811+
],
3812+
hdrs = [ "third_party/fast_float/src/include/fast_float/fast_float.h" ],
3813+
includes = [
3814+
"third_party/fast_float/src/include",
3815+
],
3816+
)
3817+
37993818
v8_library(
38003819
name = "lib_fp16",
38013820
srcs = ["third_party/fp16/src/include/fp16.h"],
@@ -4299,6 +4318,7 @@ v8_library(
42994318
":noicu/generated_torque_definitions",
43004319
],
43014320
deps = [
4321+
":lib_fast_float",
43024322
":lib_fp16",
43034323
":v8_libbase",
43044324
"//external:absl_btree",

BUILD.gn

+1
Original file line numberDiff line numberDiff line change
@@ -6260,6 +6260,7 @@ v8_source_set("v8_base_without_compiler") {
62606260
":v8_tracing",
62616261
":v8_version",
62626262
"src/inspector:inspector",
6263+
"//third_party/fast_float",
62636264
]
62646265

62656266
public_deps = [

DEPS

+3
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,8 @@ deps = {
256256
Var('chromium_url') + '/chromium/tools/depot_tools.git' + '@' + 'caa77da9568fb7c48c9db679cf9dc0ae20080585',
257257
'third_party/fp16/src':
258258
Var('chromium_url') + '/external/github.com/Maratyszcza/FP16.git' + '@' + '0a92994d729ff76a58f692d3028ca1b64b145d91',
259+
'third_party/fast_float/src':
260+
Var('chromium_url') + '/external/github.com/fastfloat/fast_float.git' + '@' + '3e57d8dcfb0a04b5a8a26b486b54490a2e9b310f',
259261
'third_party/fuchsia-gn-sdk': {
260262
'url': Var('chromium_url') + '/chromium/src/third_party/fuchsia-gn-sdk.git' + '@' + '30fee7b68b3675e351fa47303c3b6ef322941ccd',
261263
'condition': 'checkout_fuchsia',
@@ -524,6 +526,7 @@ include_rules = [
524526
'+unicode',
525527
'+third_party/fdlibm',
526528
'+third_party/ittapi/include',
529+
'+third_party/fast_float/src/include',
527530
'+third_party/fp16/src/include',
528531
'+third_party/v8/codegen',
529532
'+third_party/fuzztest',

src/numbers/conversions.cc

+46-175
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@
2323
#include "src/strings/char-predicates-inl.h"
2424
#include "src/utils/allocation.h"
2525

26+
#define FASTFLOAT_ALLOWS_LEADING_PLUS
27+
28+
#include "third_party/fast_float/src/include/fast_float/fast_float.h"
29+
#include "third_party/fast_float/src/include/fast_float/float_common.h"
30+
2631
#if defined(_STLP_VENDOR_CSTD)
2732
// STLPort doesn't import fpclassify into the std namespace.
2833
#define FPCLASSIFY_NAMESPACE
@@ -689,197 +694,63 @@ double InternalStringToDouble(const Char* current, const Char* end,
689694

690695
// From here we are parsing a StrDecimalLiteral, as per
691696
// https://tc39.es/ecma262/#sec-tonumber-applied-to-the-string-type
692-
693697
const bool allow_trailing_junk = flag == ALLOW_TRAILING_JUNK;
694698

695-
// Maximum number of significant digits in decimal representation.
696-
// The longest possible double in decimal representation is
697-
// (2^53 - 1) * 2 ^ -1074 that is (2 ^ 53 - 1) * 5 ^ 1074 / 10 ^ 1074
698-
// (768 digits). If we parse a number whose first digits are equal to a
699-
// mean of 2 adjacent doubles (that could have up to 769 digits) the result
700-
// must be rounded to the bigger one unless the tail consists of zeros, so
701-
// we don't need to preserve all the digits.
702-
const int kMaxSignificantDigits = 772;
703-
704-
// The longest form of simplified number is: "-<significant digits>'.1eXXX\0".
705-
const int kBufferSize = kMaxSignificantDigits + 10;
706-
char buffer[kBufferSize];
707-
int buffer_pos = 0;
708-
709-
// Exponent will be adjusted if insignificant digits of the integer part
710-
// or insignificant leading zeros of the fractional part are dropped.
711-
int exponent = 0;
712-
int significant_digits = 0;
713-
int insignificant_digits = 0;
714-
bool nonzero_digit_dropped = false;
715-
716-
enum class Sign { kNone, kNegative, kPositive };
717-
718-
Sign sign = Sign::kNone;
719-
720-
if (*current == '+') {
721-
// Ignore leading sign.
722-
++current;
723-
if (current == end) return JunkStringValue();
724-
sign = Sign::kPositive;
725-
} else if (*current == '-') {
726-
++current;
727-
if (current == end) return JunkStringValue();
728-
sign = Sign::kNegative;
729-
}
730-
731-
static const char kInfinityString[] = "Infinity";
732-
if (*current == kInfinityString[0]) {
733-
if (!SubStringEquals(&current, end, kInfinityString)) {
734-
return JunkStringValue();
735-
}
736-
699+
double value;
700+
// fast_float takes a char/char16_t instead of a uint8_t/uint16_t. Cast the
701+
// pointers to match.
702+
using UC = std::conditional_t<std::is_same_v<Char, uint8_t>, char, char16_t>;
703+
static_assert(sizeof(UC) == sizeof(Char));
704+
const UC* current_uc = reinterpret_cast<const UC*>(current);
705+
const UC* end_uc = reinterpret_cast<const UC*>(end);
706+
auto ret = fast_float::from_chars(current_uc, end_uc, value,
707+
static_cast<fast_float::chars_format>(
708+
fast_float::chars_format::general |
709+
fast_float::chars_format::no_infnan));
710+
if (ret.ptr == end_uc) return value;
711+
if (ret.ptr > current_uc) {
712+
current = reinterpret_cast<const Char*>(ret.ptr);
737713
if (!allow_trailing_junk && AdvanceToNonspace(&current, end)) {
738714
return JunkStringValue();
739715
}
740-
741-
DCHECK_EQ(buffer_pos, 0);
742-
return (sign == Sign::kNegative) ? -V8_INFINITY : V8_INFINITY;
743-
}
744-
745-
// Ignore leading zeros in the integer part.
746-
bool leading_zero = false;
747-
if (*current == '0') {
748-
do {
749-
current++;
750-
if (current == end) return SignedZero(sign == Sign::kNegative);
751-
} while (*current == '0');
752-
leading_zero = true;
753-
}
754-
755-
// Copy significant digits of the integer part (if any) to the buffer.
756-
while (*current >= '0' && *current <= '9') {
757-
if (significant_digits < kMaxSignificantDigits) {
758-
DCHECK_LT(buffer_pos, kBufferSize);
759-
buffer[buffer_pos++] = static_cast<char>(*current);
760-
significant_digits++;
761-
// Will later check if it's an octal in the buffer.
762-
} else {
763-
insignificant_digits++; // Move the digit into the exponential part.
764-
nonzero_digit_dropped = nonzero_digit_dropped || *current != '0';
765-
}
766-
++current;
767-
if (current == end) goto parsing_done;
716+
return value;
768717
}
769718

770-
if (*current == '.') {
771-
++current;
772-
if (current == end) {
773-
if (significant_digits == 0 && !leading_zero) {
719+
// Failed to parse any number -- handle ±Infinity before giving up.
720+
DCHECK_EQ(ret.ptr, current_uc);
721+
DCHECK_NE(current, end);
722+
static constexpr char kInfinityString[] = "Infinity";
723+
switch (*current) {
724+
case '+':
725+
// Ignore leading plus sign.
726+
++current;
727+
if (current == end) return JunkStringValue();
728+
if (*current != kInfinityString[0]) return JunkStringValue();
729+
[[fallthrough]];
730+
case kInfinityString[0]:
731+
if (!SubStringEquals(&current, end, kInfinityString)) {
774732
return JunkStringValue();
775-
} else {
776-
goto parsing_done;
777733
}
778-
}
779-
780-
if (significant_digits == 0) {
781-
// octal = false;
782-
// Integer part consists of 0 or is absent. Significant digits start after
783-
// leading zeros (if any).
784-
while (*current == '0') {
785-
++current;
786-
if (current == end) return SignedZero(sign == Sign::kNegative);
787-
exponent--; // Move this 0 into the exponent.
734+
if (!allow_trailing_junk && AdvanceToNonspace(&current, end)) {
735+
return JunkStringValue();
788736
}
789-
}
737+
return V8_INFINITY;
790738

791-
// There is a fractional part. We don't emit a '.', but adjust the exponent
792-
// instead.
793-
while (*current >= '0' && *current <= '9') {
794-
if (significant_digits < kMaxSignificantDigits) {
795-
DCHECK_LT(buffer_pos, kBufferSize);
796-
buffer[buffer_pos++] = static_cast<char>(*current);
797-
significant_digits++;
798-
exponent--;
799-
} else {
800-
// Ignore insignificant digits in the fractional part.
801-
nonzero_digit_dropped = nonzero_digit_dropped || *current != '0';
802-
}
739+
case '-':
803740
++current;
804-
if (current == end) goto parsing_done;
805-
}
806-
}
807-
808-
if (!leading_zero && exponent == 0 && significant_digits == 0) {
809-
// If leading_zeros is true then the string contains zeros.
810-
// If exponent < 0 then string was [+-]\.0*...
811-
// If significant_digits != 0 the string is not equal to 0.
812-
// Otherwise there are no digits in the string.
813-
return JunkStringValue();
814-
}
815-
816-
// Parse exponential part.
817-
if (*current == 'e' || *current == 'E') {
818-
++current;
819-
if (current == end) {
820-
if (allow_trailing_junk) {
821-
goto parsing_done;
822-
} else {
741+
if (current == end) return JunkStringValue();
742+
if (*current != kInfinityString[0]) return JunkStringValue();
743+
if (!SubStringEquals(&current, end, kInfinityString)) {
823744
return JunkStringValue();
824745
}
825-
}
826-
char exponent_sign = '+';
827-
if (*current == '+' || *current == '-') {
828-
exponent_sign = static_cast<char>(*current);
829-
++current;
830-
if (current == end) {
831-
if (allow_trailing_junk) {
832-
goto parsing_done;
833-
} else {
834-
return JunkStringValue();
835-
}
836-
}
837-
}
838-
839-
if (current == end || *current < '0' || *current > '9') {
840-
if (allow_trailing_junk) {
841-
goto parsing_done;
842-
} else {
746+
if (!allow_trailing_junk && AdvanceToNonspace(&current, end)) {
843747
return JunkStringValue();
844748
}
845-
}
846-
847-
const int max_exponent = INT_MAX / 2;
848-
DCHECK(-max_exponent / 2 <= exponent && exponent <= max_exponent / 2);
849-
int num = 0;
850-
do {
851-
// Check overflow.
852-
int digit = *current - '0';
853-
if (num >= max_exponent / 10 &&
854-
!(num == max_exponent / 10 && digit <= max_exponent % 10)) {
855-
num = max_exponent;
856-
} else {
857-
num = num * 10 + digit;
858-
}
859-
++current;
860-
} while (current != end && *current >= '0' && *current <= '9');
861-
862-
exponent += (exponent_sign == '-' ? -num : num);
863-
}
864-
865-
if (!allow_trailing_junk && AdvanceToNonspace(&current, end)) {
866-
return JunkStringValue();
867-
}
749+
return -V8_INFINITY;
868750

869-
parsing_done:
870-
exponent += insignificant_digits;
871-
872-
if (nonzero_digit_dropped) {
873-
buffer[buffer_pos++] = '1';
874-
exponent--;
751+
default:
752+
return JunkStringValue();
875753
}
876-
877-
SLOW_DCHECK(buffer_pos < kBufferSize);
878-
buffer[buffer_pos] = '\0';
879-
880-
double converted =
881-
Strtod(base::Vector<const char>(buffer, buffer_pos), exponent);
882-
return (sign == Sign::kNegative) ? -converted : converted;
883754
}
884755

885756
double StringToDouble(const char* str, ConversionFlag flags,
@@ -898,8 +769,8 @@ double StringToDouble(base::Vector<const uint8_t> str, ConversionFlag flags,
898769

899770
double StringToDouble(base::Vector<const base::uc16> str, ConversionFlag flags,
900771
double empty_string_val) {
901-
const base::uc16* end = str.begin() + str.length();
902-
return InternalStringToDouble(str.begin(), end, flags, empty_string_val);
772+
return InternalStringToDouble(str.begin(), str.end(), flags,
773+
empty_string_val);
903774
}
904775

905776
double BinaryStringToDouble(base::Vector<const uint8_t> str) {

0 commit comments

Comments
 (0)