diff --git a/.gitignore b/.gitignore index e8352aeb..1438a3b4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .sconsign.dblite +*~ *.o *.obj msvc/Release/ @@ -7,3 +8,22 @@ msvc/Debug/ *.opensdf *.sdf *.user +*.a +*.so +*.so.* +*.dylib +/run_tests +Makefile +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps +*.cmake +*.kdev4 +DartConfiguration.tcl diff --git a/AUTHORS b/AUTHORS index 88b38ae0..4edeeedc 100644 --- a/AUTHORS +++ b/AUTHORS @@ -12,3 +12,5 @@ Mike Hommey Martin Olsson Kent Williams Elan Ruusamäe +Colin Hirsch +Zhenyi Peng diff --git a/BUILD b/BUILD index d0e01083..6cab2258 100644 --- a/BUILD +++ b/BUILD @@ -2,6 +2,8 @@ licenses(["notice"]) +exports_files(["LICENSE"]) + cc_library( name = "double-conversion", srcs = [ diff --git a/CMakeLists.txt b/CMakeLists.txt index 01196ba6..a51add20 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,27 +1,36 @@ -cmake_minimum_required(VERSION 2.8.12) -project(double-conversion) +cmake_minimum_required(VERSION 3.0) +project(double-conversion VERSION 3.1.5) -include(GNUInstallDirs) +set(headers + double-conversion/bignum.h + double-conversion/cached-powers.h + double-conversion/diy-fp.h + double-conversion/double-conversion.h + double-conversion/double-to-string.h + double-conversion/fast-dtoa.h + double-conversion/fixed-dtoa.h + double-conversion/ieee.h + double-conversion/string-to-double.h + double-conversion/strtod.h + double-conversion/utils.h) -# pick a version # -set(double-conversion_VERSION 2.0.1) -set(double-conversion_SOVERSION_MAJOR 1) -set(double-conversion_SOVERSION_MINOR 0) -set(double-conversion_SOVERSION_PATCH 0) -set(double-conversion_SOVERSION - ${double-conversion_SOVERSION_MAJOR}.${double-conversion_SOVERSION_MINOR}.${double-conversion_SOVERSION_PATCH}) - -# set suffix for CMake files used for packaging -if(WIN32 AND NOT CYGWIN) - set(INSTALL_CMAKE_DIR CMake) -else() - set(INSTALL_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/double-conversion) -endif() +add_library(double-conversion + double-conversion/bignum.cc + double-conversion/bignum-dtoa.cc + double-conversion/cached-powers.cc + double-conversion/double-to-string.cc + double-conversion/fast-dtoa.cc + double-conversion/fixed-dtoa.cc + double-conversion/string-to-double.cc + double-conversion/strtod.cc + ${headers}) +target_include_directories( + double-conversion PUBLIC + $) -# Add src subdirectory -add_subdirectory(double-conversion) +# pick a version # +set_target_properties(double-conversion PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION 3) -# # set up testing if requested option(BUILD_TESTING "Build test programs" OFF) if(BUILD_TESTING) @@ -30,41 +39,76 @@ if(BUILD_TESTING) add_subdirectory(test) endif() -# -# mention the library target as export library -export(TARGETS double-conversion - FILE "${PROJECT_BINARY_DIR}/double-conversionLibraryDepends.cmake") - -# -# set this build as an importable package -export(PACKAGE double-conversion) - -# -# make a cmake file -- in this case, all that needs defining -# is double-conversion_INCLUDE_DIRS -configure_file(double-conversionBuildTreeSettings.cmake.in - "${PROJECT_BINARY_DIR}/double-conversionBuildTreeSettings.cmake" - @ONLY) - -# -# sets up config to be used by CMake find_package -configure_file(double-conversionConfig.cmake.in - "${PROJECT_BINARY_DIR}/double-conversionConfig.cmake" - @ONLY) -# -# Export version # checked by find_package -configure_file(double-conversionConfigVersion.cmake.in - "${PROJECT_BINARY_DIR}/double-conversionConfigVersion.cmake" - @ONLY) -# -# install config files for find_package -install(FILES - "${PROJECT_BINARY_DIR}/double-conversionConfig.cmake" - "${PROJECT_BINARY_DIR}/double-conversionConfigVersion.cmake" - DESTINATION "${INSTALL_CMAKE_DIR}" COMPONENT dev) - - -# -# generates install cmake files to find libraries in installation. -install(EXPORT double-conversionLibraryDepends DESTINATION - "${INSTALL_CMAKE_DIR}" COMPONENT dev) +#### +# Installation (https://github.com/forexample/package-example) + +include(GNUInstallDirs) + +# Layout. This works for all platforms: +# * /lib/cmake/ +# * /lib/ +# * /include/ +set(config_install_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") + +set(generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated") + +# Configuration +set(version_config "${generated_dir}/${PROJECT_NAME}ConfigVersion.cmake") +set(project_config "${generated_dir}/${PROJECT_NAME}Config.cmake") +set(targets_export_name "${PROJECT_NAME}Targets") +set(namespace "${PROJECT_NAME}::") + +# Include module with function 'write_basic_package_version_file' +include(CMakePackageConfigHelpers) + +# Configure 'ConfigVersion.cmake' +# Note: PROJECT_VERSION is used as a VERSION +write_basic_package_version_file( + "${version_config}" COMPATIBILITY SameMajorVersion +) + +# Configure 'Config.cmake' +# Use variables: +# * targets_export_name +# * PROJECT_NAME +configure_package_config_file( + "cmake/Config.cmake.in" + "${project_config}" + INSTALL_DESTINATION "${config_install_dir}" +) + +# Targets: +# * /lib/libdouble-conversion.a +# * header location after install: /include/double-conversion/*.h +# * headers can be included by C++ code `#include ` +install( + TARGETS double-conversion + EXPORT "${targets_export_name}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" + INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" +) + +# Headers: +# * double-conversion/*.h -> /include/double-conversion/*.h +install( + FILES ${headers} + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/double-conversion" +) + +# Config +# * /lib/cmake/double-conversion/double-conversionConfig.cmake +# * /lib/cmake/double-conversion/double-conversionConfigVersion.cmake +install( + FILES "${project_config}" "${version_config}" + DESTINATION "${config_install_dir}" +) + +# Config +# * /lib/cmake/double-conversion/double-conversionTargets.cmake +install( + EXPORT "${targets_export_name}" + NAMESPACE "${namespace}" + DESTINATION "${config_install_dir}" +) diff --git a/Changelog b/Changelog index 98247e72..5b54d544 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,96 @@ +2019-10-12: + Really add support for microblaze. A previous commit was lacking + the necessary line. + +2019-09-02: + Add support for e2k architectur. Thanks to Michael Shigorin. + +2019-08-01: + Add min exponent width option in double-to-string conversion. + +2019-06-22: + Remove redundant parenthesis. + +2019-06-11: + Changed all macros to use DOUBLE_CONVERSION_ as prefix. + Renamed ALLOW_CASE_INSENSIBILITY to ALLOW_CASE_INSENSITIVITY, + the old name is still available but officially deprecated. + Created and exposed new intermediate function StrtodTrimmed(). + +2019-05-25: + Fix `0x` for string->double conversion when Hex Floats are allowed. + Avoid integer overflow when exponents for hex floats were too big. + Update version number. + +2019-04-22: + Fixed warning in gcc4.9. Thanks to Scott McCaskill + (https://github.com/usefulcat) for the patch. + +2019-04-16: + Merged changes to install libraries in the correct place when + using 64-bit libraries. + Contributed by Jason Zaman and (independently) + Dan Church (https://github.com/h3xx) + +2019-03-11: + Use relative includes in the library. This shouldn't have any visible effect + for consumers of the library. + + Update version number. + +2019-03-06: + Fix typo in test. + Update version number. + +2019-03-03: + Fix separator characters when they they don't fit into 8 bits. + Update version number. + +2019-02-16: + Check correctly for _MSC_VER. + Patch by Ben Boeckel + +2019-01-17: + Allow the library to be compiled for Emscripten. + Patch by Tim Paine. + +2018-09-15: + Update version numbers. This also updates the shared-library version number. + +2018-09-09: + Fix bug where large hex literals would lose their minus sign. + Added support for separator characters (which adds a new optional + argument). Thus increasing the version number to 3.1.0 + Added support for hexadecimal float literals. + Support for more architectures. + +2017-12-06: + Renamed `DISALLOW_COPY_AND_ASSIGN` and `DISALLOW_IMPLICIT_CONSTRUCTORS` + macros to `DC_DISALLOW_COPY_AND_ASSIGN` and + `DC_DISALLOW_IMPLICIT_CONSTRUCTORS` to make it easier to integrate the + library with other libraries that have similar macros. + +2017-08-05: + Tagged v3.0.0. + Due to the directory rename switching to a new version number. + The API for the library itself hasn't changed. + +2017-03-04: + Avoid negative shift. Fixes #41. + +2016-11-17: + Support RISC-V. + + +2016-09-10: + Add fPIC flag on x86_64 if the compiler supports it. Fixes #34. + +2015 and 2016: + Lots of improvements to the build system. + +2015: + Warning fixes. + 2015-05-19: Rename 'src' directory to 'double-conversion'. diff --git a/README b/README.md similarity index 78% rename from README rename to README.md index 412cebee..7b9869eb 100644 --- a/README +++ b/README.md @@ -1,4 +1,4 @@ -https://github.com/google/double-conversion/ +https://github.com/google/double-conversion This project (double-conversion) provides binary-decimal and decimal-binary routines for IEEE doubles. @@ -7,14 +7,15 @@ The library consists of efficient conversion routines that have been extracted from the V8 JavaScript engine. The code has been refactored and improved so that it can be used more easily in other projects. -There is extensive documentation in double-conversion/double-conversion.h. Other -examples can be found in test/cctest/test-conversions.cc. +There is extensive documentation in `double-conversion/string-to-double.h` and +`double-conversion/double-to-string.h`. Other examples can be found in +`test/cctest/test-conversions.cc`. Building ======== -This library can be built with scons [0] or cmake [1]. +This library can be built with [scons][0] or [cmake][1]. The checked-in Makefile simply forwards to scons, and provides a shortcut to run all tests: @@ -50,5 +51,5 @@ Use `-DBUILD_TESTING=ON` to build the test executable. make test/cctest/cctest --list | tr -d '<' | xargs test/cctest/cctest -[0]: http://www.scons.org -[1]: http://www.cmake.org +[0]: http://www.scons.org/ +[1]: https://cmake.org/ diff --git a/SConstruct b/SConstruct index cff18b4c..cebd7e0a 100644 --- a/SConstruct +++ b/SConstruct @@ -16,13 +16,13 @@ optimize = ARGUMENTS.get('optimize', 0) env.Replace(CXX = ARGUMENTS.get('CXX', 'g++')) # for shared lib, requires scons 2.3.0 -env['SHLIBVERSION'] = '1.0.0' +env['SHLIBVERSION'] = '3.0.0' CCFLAGS = [] if int(debug): - CCFLAGS.append(ARGUMENTS.get('CXXFLAGS', '-g -Wall -Wshadow -Werror')) + CCFLAGS.append(ARGUMENTS.get('CXXFLAGS', '-g -Wall -Wshadow -Werror -UNDEBUG')) if int(optimize): - CCFLAGS.append(ARGUMENTS.get('CXXFLAGS', '-O3')) + CCFLAGS.append(ARGUMENTS.get('CXXFLAGS', '-O3 -DNDEBUG=1')) env.Append(CCFLAGS = " ".join(CCFLAGS)) diff --git a/cmake/Config.cmake.in b/cmake/Config.cmake.in new file mode 100644 index 00000000..9b4c9ee0 --- /dev/null +++ b/cmake/Config.cmake.in @@ -0,0 +1,4 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake") +check_required_components("@PROJECT_NAME@") diff --git a/double-conversion/.gitignore b/double-conversion/.gitignore new file mode 100644 index 00000000..1edeb79f --- /dev/null +++ b/double-conversion/.gitignore @@ -0,0 +1 @@ +*.os diff --git a/double-conversion/CMakeLists.txt b/double-conversion/CMakeLists.txt deleted file mode 100644 index 2d13b36a..00000000 --- a/double-conversion/CMakeLists.txt +++ /dev/null @@ -1,57 +0,0 @@ -set(headers - bignum.h - cached-powers.h - diy-fp.h - double-conversion.h - fast-dtoa.h - fixed-dtoa.h - ieee.h - strtod.h - utils.h - ) - -add_library(double-conversion -bignum.cc -bignum-dtoa.cc -cached-powers.cc -diy-fp.cc -double-conversion.cc -fast-dtoa.cc -fixed-dtoa.cc -strtod.cc -${headers} -) - -target_include_directories(double-conversion PUBLIC $) - -# Add fPIC on x86_64 when supported. -include(CheckCXXCompilerFlag) -check_cxx_compiler_flag(-fPIC CXX_HAS_FPIC) - -if(CXX_HAS_FPIC AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") - set_target_properties(double-conversion PROPERTIES COMPILE_FLAGS "-fPIC") -endif() - -# -# associates the list of headers with the library -# for the purposes of installation/import into other projects -set_target_properties(double-conversion - PROPERTIES PUBLIC_HEADER "${headers}") - -if (BUILD_SHARED_LIBS) - set_target_properties(double-conversion - PROPERTIES VERSION ${double-conversion_SOVERSION} - SOVERSION ${double-conversion_SOVERSION_MAJOR}) -endif() - -# -# install command to set up library install -# given the above PUBLIC_HEADER property set, this -# pulls along all the header files with the library. -install(TARGETS double-conversion - EXPORT double-conversionLibraryDepends - RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT bin - LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT shlib - ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT lib - PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/double-conversion" - COMPONENT dev) diff --git a/double-conversion/SConscript b/double-conversion/SConscript index a117c32b..d7ae3d78 100644 --- a/double-conversion/SConscript +++ b/double-conversion/SConscript @@ -3,10 +3,10 @@ double_conversion_sources = [ 'bignum.cc', 'bignum-dtoa.cc', 'cached-powers.cc', - 'diy-fp.cc', - 'double-conversion.cc', + 'double-to-string.cc', 'fast-dtoa.cc', 'fixed-dtoa.cc', + 'string-to-double.cc', 'strtod.cc' ] Return('double_conversion_sources') diff --git a/double-conversion/bignum-dtoa.cc b/double-conversion/bignum-dtoa.cc index f1ad7a5a..abdd7145 100644 --- a/double-conversion/bignum-dtoa.cc +++ b/double-conversion/bignum-dtoa.cc @@ -25,7 +25,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include +#include #include "bignum-dtoa.h" @@ -35,7 +35,7 @@ namespace double_conversion { static int NormalizedExponent(uint64_t significand, int exponent) { - ASSERT(significand != 0); + DOUBLE_CONVERSION_ASSERT(significand != 0); while ((significand & Double::kHiddenBit) == 0) { significand = significand << 1; exponent = exponent - 1; @@ -76,26 +76,26 @@ static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator, // Generates 'requested_digits' after the decimal point. static void BignumToFixed(int requested_digits, int* decimal_point, Bignum* numerator, Bignum* denominator, - Vector(buffer), int* length); + Vector buffer, int* length); // Generates 'count' digits of numerator/denominator. // Once 'count' digits have been produced rounds the result depending on the // remainder (remainders of exactly .5 round upwards). Might update the // decimal_point when rounding up (for example for 0.9999). static void GenerateCountedDigits(int count, int* decimal_point, Bignum* numerator, Bignum* denominator, - Vector(buffer), int* length); + Vector buffer, int* length); void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits, Vector buffer, int* length, int* decimal_point) { - ASSERT(v > 0); - ASSERT(!Double(v).IsSpecial()); + DOUBLE_CONVERSION_ASSERT(v > 0); + DOUBLE_CONVERSION_ASSERT(!Double(v).IsSpecial()); uint64_t significand; int exponent; bool lower_boundary_is_closer; if (mode == BIGNUM_DTOA_SHORTEST_SINGLE) { float f = static_cast(v); - ASSERT(f == v); + DOUBLE_CONVERSION_ASSERT(f == v); significand = Single(f).Significand(); exponent = Single(f).Exponent(); lower_boundary_is_closer = Single(f).LowerBoundaryIsCloser(); @@ -134,7 +134,7 @@ void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits, // 4e-324. In this case the denominator needs fewer than 324*4 binary digits. // The maximum double is 1.7976931348623157e308 which needs fewer than // 308*4 binary digits. - ASSERT(Bignum::kMaxSignificantBits >= 324*4); + DOUBLE_CONVERSION_ASSERT(Bignum::kMaxSignificantBits >= 324*4); InitialScaledStartValues(significand, exponent, lower_boundary_is_closer, estimated_power, need_boundary_deltas, &numerator, &denominator, @@ -163,7 +163,7 @@ void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits, buffer, length); break; default: - UNREACHABLE(); + DOUBLE_CONVERSION_UNREACHABLE(); } buffer[*length] = '\0'; } @@ -195,7 +195,7 @@ static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator, for (;;) { uint16_t digit; digit = numerator->DivideModuloIntBignum(*denominator); - ASSERT(digit <= 9); // digit is a uint16_t and therefore always positive. + DOUBLE_CONVERSION_ASSERT(digit <= 9); // digit is a uint16_t and therefore always positive. // digit = numerator / denominator (integer division). // numerator = numerator % denominator. buffer[(*length)++] = static_cast(digit + '0'); @@ -241,7 +241,7 @@ static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator, // loop would have stopped earlier. // We still have an assert here in case the preconditions were not // satisfied. - ASSERT(buffer[(*length) - 1] != '9'); + DOUBLE_CONVERSION_ASSERT(buffer[(*length) - 1] != '9'); buffer[(*length) - 1]++; } else { // Halfway case. @@ -252,7 +252,7 @@ static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator, if ((buffer[(*length) - 1] - '0') % 2 == 0) { // Round down => Do nothing. } else { - ASSERT(buffer[(*length) - 1] != '9'); + DOUBLE_CONVERSION_ASSERT(buffer[(*length) - 1] != '9'); buffer[(*length) - 1]++; } } @@ -264,9 +264,9 @@ static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator, // Round up. // Note again that the last digit could not be '9' since this would have // stopped the loop earlier. - // We still have an ASSERT here, in case the preconditions were not + // We still have an DOUBLE_CONVERSION_ASSERT here, in case the preconditions were not // satisfied. - ASSERT(buffer[(*length) -1] != '9'); + DOUBLE_CONVERSION_ASSERT(buffer[(*length) -1] != '9'); buffer[(*length) - 1]++; return; } @@ -283,11 +283,11 @@ static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator, static void GenerateCountedDigits(int count, int* decimal_point, Bignum* numerator, Bignum* denominator, Vector buffer, int* length) { - ASSERT(count >= 0); + DOUBLE_CONVERSION_ASSERT(count >= 0); for (int i = 0; i < count - 1; ++i) { uint16_t digit; digit = numerator->DivideModuloIntBignum(*denominator); - ASSERT(digit <= 9); // digit is a uint16_t and therefore always positive. + DOUBLE_CONVERSION_ASSERT(digit <= 9); // digit is a uint16_t and therefore always positive. // digit = numerator / denominator (integer division). // numerator = numerator % denominator. buffer[i] = static_cast(digit + '0'); @@ -300,7 +300,7 @@ static void GenerateCountedDigits(int count, int* decimal_point, if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) { digit++; } - ASSERT(digit <= 10); + DOUBLE_CONVERSION_ASSERT(digit <= 10); buffer[count - 1] = static_cast(digit + '0'); // Correct bad digits (in case we had a sequence of '9's). Propagate the // carry until we hat a non-'9' or til we reach the first digit. @@ -325,7 +325,7 @@ static void GenerateCountedDigits(int count, int* decimal_point, // Input verifies: 1 <= (numerator + delta) / denominator < 10. static void BignumToFixed(int requested_digits, int* decimal_point, Bignum* numerator, Bignum* denominator, - Vector(buffer), int* length) { + Vector buffer, int* length) { // Note that we have to look at more than just the requested_digits, since // a number could be rounded up. Example: v=0.5 with requested_digits=0. // Even though the power of v equals 0 we can't just stop here. @@ -341,7 +341,7 @@ static void BignumToFixed(int requested_digits, int* decimal_point, } else if (-(*decimal_point) == requested_digits) { // We only need to verify if the number rounds down or up. // Ex: 0.04 and 0.06 with requested_digits == 1. - ASSERT(*decimal_point == -requested_digits); + DOUBLE_CONVERSION_ASSERT(*decimal_point == -requested_digits); // Initially the fraction lies in range (1, 10]. Multiply the denominator // by 10 so that we can compare more easily. denominator->Times10(); @@ -420,7 +420,7 @@ static void InitialScaledStartValuesPositiveExponent( Bignum* numerator, Bignum* denominator, Bignum* delta_minus, Bignum* delta_plus) { // A positive exponent implies a positive power. - ASSERT(estimated_power >= 0); + DOUBLE_CONVERSION_ASSERT(estimated_power >= 0); // Since the estimated_power is positive we simply multiply the denominator // by 10^estimated_power. @@ -506,7 +506,7 @@ static void InitialScaledStartValuesNegativeExponentNegativePower( // numerator = v * 10^-estimated_power * 2 * 2^-exponent. // Remember: numerator has been abused as power_ten. So no need to assign it // to itself. - ASSERT(numerator == power_ten); + DOUBLE_CONVERSION_ASSERT(numerator == power_ten); numerator->MultiplyByUInt64(significand); // denominator = 2 * 2^-exponent with exponent < 0. diff --git a/double-conversion/bignum.cc b/double-conversion/bignum.cc index 8892de8f..d858c16c 100644 --- a/double-conversion/bignum.cc +++ b/double-conversion/bignum.cc @@ -25,141 +25,137 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#include +#include + #include "bignum.h" #include "utils.h" namespace double_conversion { -Bignum::Bignum() - : bigits_(bigits_buffer_, kBigitCapacity), used_digits_(0), exponent_(0) { - for (int i = 0; i < kBigitCapacity; ++i) { - bigits_[i] = 0; - } +Bignum::Chunk& Bignum::RawBigit(const int index) { + DOUBLE_CONVERSION_ASSERT(static_cast(index) < kBigitCapacity); + return bigits_buffer_[index]; +} + + +const Bignum::Chunk& Bignum::RawBigit(const int index) const { + DOUBLE_CONVERSION_ASSERT(static_cast(index) < kBigitCapacity); + return bigits_buffer_[index]; } template -static int BitSize(S value) { +static int BitSize(const S value) { (void) value; // Mark variable as used. return 8 * sizeof(value); } // Guaranteed to lie in one Bigit. -void Bignum::AssignUInt16(uint16_t value) { - ASSERT(kBigitSize >= BitSize(value)); +void Bignum::AssignUInt16(const uint16_t value) { + DOUBLE_CONVERSION_ASSERT(kBigitSize >= BitSize(value)); Zero(); - if (value == 0) return; - - EnsureCapacity(1); - bigits_[0] = value; - used_digits_ = 1; + if (value > 0) { + RawBigit(0) = value; + used_bigits_ = 1; + } } void Bignum::AssignUInt64(uint64_t value) { - const int kUInt64Size = 64; - Zero(); - if (value == 0) return; - - int needed_bigits = kUInt64Size / kBigitSize + 1; - EnsureCapacity(needed_bigits); - for (int i = 0; i < needed_bigits; ++i) { - bigits_[i] = value & kBigitMask; - value = value >> kBigitSize; + for(int i = 0; value > 0; ++i) { + RawBigit(i) = value & kBigitMask; + value >>= kBigitSize; + ++used_bigits_; } - used_digits_ = needed_bigits; - Clamp(); } void Bignum::AssignBignum(const Bignum& other) { exponent_ = other.exponent_; - for (int i = 0; i < other.used_digits_; ++i) { - bigits_[i] = other.bigits_[i]; + for (int i = 0; i < other.used_bigits_; ++i) { + RawBigit(i) = other.RawBigit(i); } - // Clear the excess digits (if there were any). - for (int i = other.used_digits_; i < used_digits_; ++i) { - bigits_[i] = 0; - } - used_digits_ = other.used_digits_; + used_bigits_ = other.used_bigits_; } -static uint64_t ReadUInt64(Vector buffer, - int from, - int digits_to_read) { +static uint64_t ReadUInt64(const Vector buffer, + const int from, + const int digits_to_read) { uint64_t result = 0; for (int i = from; i < from + digits_to_read; ++i) { - int digit = buffer[i] - '0'; - ASSERT(0 <= digit && digit <= 9); + const int digit = buffer[i] - '0'; + DOUBLE_CONVERSION_ASSERT(0 <= digit && digit <= 9); result = result * 10 + digit; } return result; } -void Bignum::AssignDecimalString(Vector value) { +void Bignum::AssignDecimalString(const Vector value) { // 2^64 = 18446744073709551616 > 10^19 - const int kMaxUint64DecimalDigits = 19; + static const int kMaxUint64DecimalDigits = 19; Zero(); int length = value.length(); - unsigned int pos = 0; + unsigned pos = 0; // Let's just say that each digit needs 4 bits. while (length >= kMaxUint64DecimalDigits) { - uint64_t digits = ReadUInt64(value, pos, kMaxUint64DecimalDigits); + const uint64_t digits = ReadUInt64(value, pos, kMaxUint64DecimalDigits); pos += kMaxUint64DecimalDigits; length -= kMaxUint64DecimalDigits; MultiplyByPowerOfTen(kMaxUint64DecimalDigits); AddUInt64(digits); } - uint64_t digits = ReadUInt64(value, pos, length); + const uint64_t digits = ReadUInt64(value, pos, length); MultiplyByPowerOfTen(length); AddUInt64(digits); Clamp(); } -static int HexCharValue(char c) { - if ('0' <= c && c <= '9') return c - '0'; - if ('a' <= c && c <= 'f') return 10 + c - 'a'; - ASSERT('A' <= c && c <= 'F'); +static uint64_t HexCharValue(const int c) { + if ('0' <= c && c <= '9') { + return c - '0'; + } + if ('a' <= c && c <= 'f') { + return 10 + c - 'a'; + } + DOUBLE_CONVERSION_ASSERT('A' <= c && c <= 'F'); return 10 + c - 'A'; } +// Unlike AssignDecimalString(), this function is "only" used +// for unit-tests and therefore not performance critical. void Bignum::AssignHexString(Vector value) { Zero(); - int length = value.length(); - - int needed_bigits = length * 4 / kBigitSize + 1; - EnsureCapacity(needed_bigits); - int string_index = length - 1; - for (int i = 0; i < needed_bigits - 1; ++i) { - // These bigits are guaranteed to be "full". - Chunk current_bigit = 0; - for (int j = 0; j < kBigitSize / 4; j++) { - current_bigit += HexCharValue(value[string_index--]) << (j * 4); + // Required capacity could be reduced by ignoring leading zeros. + EnsureCapacity(((value.length() * 4) + kBigitSize - 1) / kBigitSize); + DOUBLE_CONVERSION_ASSERT(sizeof(uint64_t) * 8 >= kBigitSize + 4); // TODO: static_assert + // Accumulates converted hex digits until at least kBigitSize bits. + // Works with non-factor-of-four kBigitSizes. + uint64_t tmp = 0; // Accumulates converted hex digits until at least + for (int cnt = 0; !value.is_empty(); value.pop_back()) { + tmp |= (HexCharValue(value.last()) << cnt); + if ((cnt += 4) >= kBigitSize) { + RawBigit(used_bigits_++) = (tmp & kBigitMask); + cnt -= kBigitSize; + tmp >>= kBigitSize; } - bigits_[i] = current_bigit; - } - used_digits_ = needed_bigits - 1; - - Chunk most_significant_bigit = 0; // Could be = 0; - for (int j = 0; j <= string_index; ++j) { - most_significant_bigit <<= 4; - most_significant_bigit += HexCharValue(value[j]); } - if (most_significant_bigit != 0) { - bigits_[used_digits_] = most_significant_bigit; - used_digits_++; + if (tmp > 0) { + RawBigit(used_bigits_++) = tmp; } Clamp(); } -void Bignum::AddUInt64(uint64_t operand) { - if (operand == 0) return; +void Bignum::AddUInt64(const uint64_t operand) { + if (operand == 0) { + return; + } Bignum other; other.AssignUInt64(operand); AddBignum(other); @@ -167,8 +163,8 @@ void Bignum::AddUInt64(uint64_t operand) { void Bignum::AddBignum(const Bignum& other) { - ASSERT(IsClamped()); - ASSERT(other.IsClamped()); + DOUBLE_CONVERSION_ASSERT(IsClamped()); + DOUBLE_CONVERSION_ASSERT(other.IsClamped()); // If this has a greater exponent than other append zero-bigits to this. // After this call exponent_ <= other.exponent_. @@ -186,48 +182,52 @@ void Bignum::AddBignum(const Bignum& other) { // cccccccccccc 0000 // In both cases we might need a carry bigit. - EnsureCapacity(1 + Max(BigitLength(), other.BigitLength()) - exponent_); + EnsureCapacity(1 + (std::max)(BigitLength(), other.BigitLength()) - exponent_); Chunk carry = 0; int bigit_pos = other.exponent_ - exponent_; - ASSERT(bigit_pos >= 0); - for (int i = 0; i < other.used_digits_; ++i) { - Chunk sum = bigits_[bigit_pos] + other.bigits_[i] + carry; - bigits_[bigit_pos] = sum & kBigitMask; + DOUBLE_CONVERSION_ASSERT(bigit_pos >= 0); + for (int i = used_bigits_; i < bigit_pos; ++i) { + RawBigit(i) = 0; + } + for (int i = 0; i < other.used_bigits_; ++i) { + const Chunk my = (bigit_pos < used_bigits_) ? RawBigit(bigit_pos) : 0; + const Chunk sum = my + other.RawBigit(i) + carry; + RawBigit(bigit_pos) = sum & kBigitMask; carry = sum >> kBigitSize; - bigit_pos++; + ++bigit_pos; } - while (carry != 0) { - Chunk sum = bigits_[bigit_pos] + carry; - bigits_[bigit_pos] = sum & kBigitMask; + const Chunk my = (bigit_pos < used_bigits_) ? RawBigit(bigit_pos) : 0; + const Chunk sum = my + carry; + RawBigit(bigit_pos) = sum & kBigitMask; carry = sum >> kBigitSize; - bigit_pos++; + ++bigit_pos; } - used_digits_ = Max(bigit_pos, used_digits_); - ASSERT(IsClamped()); + used_bigits_ = (std::max)(bigit_pos, static_cast(used_bigits_)); + DOUBLE_CONVERSION_ASSERT(IsClamped()); } void Bignum::SubtractBignum(const Bignum& other) { - ASSERT(IsClamped()); - ASSERT(other.IsClamped()); + DOUBLE_CONVERSION_ASSERT(IsClamped()); + DOUBLE_CONVERSION_ASSERT(other.IsClamped()); // We require this to be bigger than other. - ASSERT(LessEqual(other, *this)); + DOUBLE_CONVERSION_ASSERT(LessEqual(other, *this)); Align(other); - int offset = other.exponent_ - exponent_; + const int offset = other.exponent_ - exponent_; Chunk borrow = 0; int i; - for (i = 0; i < other.used_digits_; ++i) { - ASSERT((borrow == 0) || (borrow == 1)); - Chunk difference = bigits_[i + offset] - other.bigits_[i] - borrow; - bigits_[i + offset] = difference & kBigitMask; + for (i = 0; i < other.used_bigits_; ++i) { + DOUBLE_CONVERSION_ASSERT((borrow == 0) || (borrow == 1)); + const Chunk difference = RawBigit(i + offset) - other.RawBigit(i) - borrow; + RawBigit(i + offset) = difference & kBigitMask; borrow = difference >> (kChunkSize - 1); } while (borrow != 0) { - Chunk difference = bigits_[i + offset] - borrow; - bigits_[i + offset] = difference & kBigitMask; + const Chunk difference = RawBigit(i + offset) - borrow; + RawBigit(i + offset) = difference & kBigitMask; borrow = difference >> (kChunkSize - 1); ++i; } @@ -235,91 +235,105 @@ void Bignum::SubtractBignum(const Bignum& other) { } -void Bignum::ShiftLeft(int shift_amount) { - if (used_digits_ == 0) return; - exponent_ += shift_amount / kBigitSize; - int local_shift = shift_amount % kBigitSize; - EnsureCapacity(used_digits_ + 1); +void Bignum::ShiftLeft(const int shift_amount) { + if (used_bigits_ == 0) { + return; + } + exponent_ += (shift_amount / kBigitSize); + const int local_shift = shift_amount % kBigitSize; + EnsureCapacity(used_bigits_ + 1); BigitsShiftLeft(local_shift); } -void Bignum::MultiplyByUInt32(uint32_t factor) { - if (factor == 1) return; +void Bignum::MultiplyByUInt32(const uint32_t factor) { + if (factor == 1) { + return; + } if (factor == 0) { Zero(); return; } - if (used_digits_ == 0) return; - + if (used_bigits_ == 0) { + return; + } // The product of a bigit with the factor is of size kBigitSize + 32. // Assert that this number + 1 (for the carry) fits into double chunk. - ASSERT(kDoubleChunkSize >= kBigitSize + 32 + 1); + DOUBLE_CONVERSION_ASSERT(kDoubleChunkSize >= kBigitSize + 32 + 1); DoubleChunk carry = 0; - for (int i = 0; i < used_digits_; ++i) { - DoubleChunk product = static_cast(factor) * bigits_[i] + carry; - bigits_[i] = static_cast(product & kBigitMask); + for (int i = 0; i < used_bigits_; ++i) { + const DoubleChunk product = static_cast(factor) * RawBigit(i) + carry; + RawBigit(i) = static_cast(product & kBigitMask); carry = (product >> kBigitSize); } while (carry != 0) { - EnsureCapacity(used_digits_ + 1); - bigits_[used_digits_] = carry & kBigitMask; - used_digits_++; + EnsureCapacity(used_bigits_ + 1); + RawBigit(used_bigits_) = carry & kBigitMask; + used_bigits_++; carry >>= kBigitSize; } } -void Bignum::MultiplyByUInt64(uint64_t factor) { - if (factor == 1) return; +void Bignum::MultiplyByUInt64(const uint64_t factor) { + if (factor == 1) { + return; + } if (factor == 0) { Zero(); return; } - ASSERT(kBigitSize < 32); + if (used_bigits_ == 0) { + return; + } + DOUBLE_CONVERSION_ASSERT(kBigitSize < 32); uint64_t carry = 0; - uint64_t low = factor & 0xFFFFFFFF; - uint64_t high = factor >> 32; - for (int i = 0; i < used_digits_; ++i) { - uint64_t product_low = low * bigits_[i]; - uint64_t product_high = high * bigits_[i]; - uint64_t tmp = (carry & kBigitMask) + product_low; - bigits_[i] = tmp & kBigitMask; + const uint64_t low = factor & 0xFFFFFFFF; + const uint64_t high = factor >> 32; + for (int i = 0; i < used_bigits_; ++i) { + const uint64_t product_low = low * RawBigit(i); + const uint64_t product_high = high * RawBigit(i); + const uint64_t tmp = (carry & kBigitMask) + product_low; + RawBigit(i) = tmp & kBigitMask; carry = (carry >> kBigitSize) + (tmp >> kBigitSize) + (product_high << (32 - kBigitSize)); } while (carry != 0) { - EnsureCapacity(used_digits_ + 1); - bigits_[used_digits_] = carry & kBigitMask; - used_digits_++; + EnsureCapacity(used_bigits_ + 1); + RawBigit(used_bigits_) = carry & kBigitMask; + used_bigits_++; carry >>= kBigitSize; } } -void Bignum::MultiplyByPowerOfTen(int exponent) { - const uint64_t kFive27 = UINT64_2PART_C(0x6765c793, fa10079d); - const uint16_t kFive1 = 5; - const uint16_t kFive2 = kFive1 * 5; - const uint16_t kFive3 = kFive2 * 5; - const uint16_t kFive4 = kFive3 * 5; - const uint16_t kFive5 = kFive4 * 5; - const uint16_t kFive6 = kFive5 * 5; - const uint32_t kFive7 = kFive6 * 5; - const uint32_t kFive8 = kFive7 * 5; - const uint32_t kFive9 = kFive8 * 5; - const uint32_t kFive10 = kFive9 * 5; - const uint32_t kFive11 = kFive10 * 5; - const uint32_t kFive12 = kFive11 * 5; - const uint32_t kFive13 = kFive12 * 5; - const uint32_t kFive1_to_12[] = +void Bignum::MultiplyByPowerOfTen(const int exponent) { + static const uint64_t kFive27 = DOUBLE_CONVERSION_UINT64_2PART_C(0x6765c793, fa10079d); + static const uint16_t kFive1 = 5; + static const uint16_t kFive2 = kFive1 * 5; + static const uint16_t kFive3 = kFive2 * 5; + static const uint16_t kFive4 = kFive3 * 5; + static const uint16_t kFive5 = kFive4 * 5; + static const uint16_t kFive6 = kFive5 * 5; + static const uint32_t kFive7 = kFive6 * 5; + static const uint32_t kFive8 = kFive7 * 5; + static const uint32_t kFive9 = kFive8 * 5; + static const uint32_t kFive10 = kFive9 * 5; + static const uint32_t kFive11 = kFive10 * 5; + static const uint32_t kFive12 = kFive11 * 5; + static const uint32_t kFive13 = kFive12 * 5; + static const uint32_t kFive1_to_12[] = { kFive1, kFive2, kFive3, kFive4, kFive5, kFive6, kFive7, kFive8, kFive9, kFive10, kFive11, kFive12 }; - ASSERT(exponent >= 0); - if (exponent == 0) return; - if (used_digits_ == 0) return; + DOUBLE_CONVERSION_ASSERT(exponent >= 0); + if (exponent == 0) { + return; + } + if (used_bigits_ == 0) { + return; + } // We shift by exponent at the end just before returning. int remaining_exponent = exponent; while (remaining_exponent >= 27) { @@ -338,8 +352,8 @@ void Bignum::MultiplyByPowerOfTen(int exponent) { void Bignum::Square() { - ASSERT(IsClamped()); - int product_length = 2 * used_digits_; + DOUBLE_CONVERSION_ASSERT(IsClamped()); + const int product_length = 2 * used_bigits_; EnsureCapacity(product_length); // Comba multiplication: compute each column separately. @@ -354,64 +368,64 @@ void Bignum::Square() { // // Assert that the additional number of bits in a DoubleChunk are enough to // sum up used_digits of Bigit*Bigit. - if ((1 << (2 * (kChunkSize - kBigitSize))) <= used_digits_) { - UNIMPLEMENTED(); + if ((1 << (2 * (kChunkSize - kBigitSize))) <= used_bigits_) { + DOUBLE_CONVERSION_UNIMPLEMENTED(); } DoubleChunk accumulator = 0; // First shift the digits so we don't overwrite them. - int copy_offset = used_digits_; - for (int i = 0; i < used_digits_; ++i) { - bigits_[copy_offset + i] = bigits_[i]; + const int copy_offset = used_bigits_; + for (int i = 0; i < used_bigits_; ++i) { + RawBigit(copy_offset + i) = RawBigit(i); } // We have two loops to avoid some 'if's in the loop. - for (int i = 0; i < used_digits_; ++i) { + for (int i = 0; i < used_bigits_; ++i) { // Process temporary digit i with power i. // The sum of the two indices must be equal to i. int bigit_index1 = i; int bigit_index2 = 0; // Sum all of the sub-products. while (bigit_index1 >= 0) { - Chunk chunk1 = bigits_[copy_offset + bigit_index1]; - Chunk chunk2 = bigits_[copy_offset + bigit_index2]; + const Chunk chunk1 = RawBigit(copy_offset + bigit_index1); + const Chunk chunk2 = RawBigit(copy_offset + bigit_index2); accumulator += static_cast(chunk1) * chunk2; bigit_index1--; bigit_index2++; } - bigits_[i] = static_cast(accumulator) & kBigitMask; + RawBigit(i) = static_cast(accumulator) & kBigitMask; accumulator >>= kBigitSize; } - for (int i = used_digits_; i < product_length; ++i) { - int bigit_index1 = used_digits_ - 1; + for (int i = used_bigits_; i < product_length; ++i) { + int bigit_index1 = used_bigits_ - 1; int bigit_index2 = i - bigit_index1; // Invariant: sum of both indices is again equal to i. // Inner loop runs 0 times on last iteration, emptying accumulator. - while (bigit_index2 < used_digits_) { - Chunk chunk1 = bigits_[copy_offset + bigit_index1]; - Chunk chunk2 = bigits_[copy_offset + bigit_index2]; + while (bigit_index2 < used_bigits_) { + const Chunk chunk1 = RawBigit(copy_offset + bigit_index1); + const Chunk chunk2 = RawBigit(copy_offset + bigit_index2); accumulator += static_cast(chunk1) * chunk2; bigit_index1--; bigit_index2++; } - // The overwritten bigits_[i] will never be read in further loop iterations, + // The overwritten RawBigit(i) will never be read in further loop iterations, // because bigit_index1 and bigit_index2 are always greater - // than i - used_digits_. - bigits_[i] = static_cast(accumulator) & kBigitMask; + // than i - used_bigits_. + RawBigit(i) = static_cast(accumulator) & kBigitMask; accumulator >>= kBigitSize; } // Since the result was guaranteed to lie inside the number the // accumulator must be 0 now. - ASSERT(accumulator == 0); + DOUBLE_CONVERSION_ASSERT(accumulator == 0); // Don't forget to update the used_digits and the exponent. - used_digits_ = product_length; + used_bigits_ = product_length; exponent_ *= 2; Clamp(); } -void Bignum::AssignPowerUInt16(uint16_t base, int power_exponent) { - ASSERT(base != 0); - ASSERT(power_exponent >= 0); +void Bignum::AssignPowerUInt16(uint16_t base, const int power_exponent) { + DOUBLE_CONVERSION_ASSERT(base != 0); + DOUBLE_CONVERSION_ASSERT(power_exponent >= 0); if (power_exponent == 0) { AssignUInt16(1); return; @@ -431,7 +445,7 @@ void Bignum::AssignPowerUInt16(uint16_t base, int power_exponent) { tmp_base >>= 1; bit_size++; } - int final_size = bit_size * power_exponent; + const int final_size = bit_size * power_exponent; // 1 extra bigit for the shifting, and one for rounded final_size. EnsureCapacity(final_size / kBigitSize + 2); @@ -445,26 +459,27 @@ void Bignum::AssignPowerUInt16(uint16_t base, int power_exponent) { mask >>= 2; uint64_t this_value = base; - bool delayed_multipliciation = false; + bool delayed_multiplication = false; const uint64_t max_32bits = 0xFFFFFFFF; while (mask != 0 && this_value <= max_32bits) { this_value = this_value * this_value; // Verify that there is enough space in this_value to perform the // multiplication. The first bit_size bits must be 0. if ((power_exponent & mask) != 0) { - uint64_t base_bits_mask = - ~((static_cast(1) << (64 - bit_size)) - 1); - bool high_bits_zero = (this_value & base_bits_mask) == 0; + DOUBLE_CONVERSION_ASSERT(bit_size > 0); + const uint64_t base_bits_mask = + ~((static_cast(1) << (64 - bit_size)) - 1); + const bool high_bits_zero = (this_value & base_bits_mask) == 0; if (high_bits_zero) { this_value *= base; } else { - delayed_multipliciation = true; + delayed_multiplication = true; } } mask >>= 1; } AssignUInt64(this_value); - if (delayed_multipliciation) { + if (delayed_multiplication) { MultiplyByUInt32(base); } @@ -484,9 +499,9 @@ void Bignum::AssignPowerUInt16(uint16_t base, int power_exponent) { // Precondition: this/other < 16bit. uint16_t Bignum::DivideModuloIntBignum(const Bignum& other) { - ASSERT(IsClamped()); - ASSERT(other.IsClamped()); - ASSERT(other.used_digits_ > 0); + DOUBLE_CONVERSION_ASSERT(IsClamped()); + DOUBLE_CONVERSION_ASSERT(other.IsClamped()); + DOUBLE_CONVERSION_ASSERT(other.used_bigits_ > 0); // Easy case: if we have less digits than the divisor than the result is 0. // Note: this handles the case where this == 0, too. @@ -504,34 +519,34 @@ uint16_t Bignum::DivideModuloIntBignum(const Bignum& other) { // This naive approach is extremely inefficient if `this` divided by other // is big. This function is implemented for doubleToString where // the result should be small (less than 10). - ASSERT(other.bigits_[other.used_digits_ - 1] >= ((1 << kBigitSize) / 16)); - ASSERT(bigits_[used_digits_ - 1] < 0x10000); + DOUBLE_CONVERSION_ASSERT(other.RawBigit(other.used_bigits_ - 1) >= ((1 << kBigitSize) / 16)); + DOUBLE_CONVERSION_ASSERT(RawBigit(used_bigits_ - 1) < 0x10000); // Remove the multiples of the first digit. // Example this = 23 and other equals 9. -> Remove 2 multiples. - result += static_cast(bigits_[used_digits_ - 1]); - SubtractTimes(other, bigits_[used_digits_ - 1]); + result += static_cast(RawBigit(used_bigits_ - 1)); + SubtractTimes(other, RawBigit(used_bigits_ - 1)); } - ASSERT(BigitLength() == other.BigitLength()); + DOUBLE_CONVERSION_ASSERT(BigitLength() == other.BigitLength()); // Both bignums are at the same length now. // Since other has more than 0 digits we know that the access to - // bigits_[used_digits_ - 1] is safe. - Chunk this_bigit = bigits_[used_digits_ - 1]; - Chunk other_bigit = other.bigits_[other.used_digits_ - 1]; + // RawBigit(used_bigits_ - 1) is safe. + const Chunk this_bigit = RawBigit(used_bigits_ - 1); + const Chunk other_bigit = other.RawBigit(other.used_bigits_ - 1); - if (other.used_digits_ == 1) { + if (other.used_bigits_ == 1) { // Shortcut for easy (and common) case. int quotient = this_bigit / other_bigit; - bigits_[used_digits_ - 1] = this_bigit - other_bigit * quotient; - ASSERT(quotient < 0x10000); + RawBigit(used_bigits_ - 1) = this_bigit - other_bigit * quotient; + DOUBLE_CONVERSION_ASSERT(quotient < 0x10000); result += static_cast(quotient); Clamp(); return result; } - int division_estimate = this_bigit / (other_bigit + 1); - ASSERT(division_estimate < 0x10000); + const int division_estimate = this_bigit / (other_bigit + 1); + DOUBLE_CONVERSION_ASSERT(division_estimate < 0x10000); result += static_cast(division_estimate); SubtractTimes(other, division_estimate); @@ -551,7 +566,7 @@ uint16_t Bignum::DivideModuloIntBignum(const Bignum& other) { template static int SizeInHexChars(S number) { - ASSERT(number > 0); + DOUBLE_CONVERSION_ASSERT(number > 0); int result = 0; while (number != 0) { number >>= 4; @@ -561,29 +576,35 @@ static int SizeInHexChars(S number) { } -static char HexCharOfValue(int value) { - ASSERT(0 <= value && value <= 16); - if (value < 10) return static_cast(value + '0'); +static char HexCharOfValue(const int value) { + DOUBLE_CONVERSION_ASSERT(0 <= value && value <= 16); + if (value < 10) { + return static_cast(value + '0'); + } return static_cast(value - 10 + 'A'); } -bool Bignum::ToHexString(char* buffer, int buffer_size) const { - ASSERT(IsClamped()); +bool Bignum::ToHexString(char* buffer, const int buffer_size) const { + DOUBLE_CONVERSION_ASSERT(IsClamped()); // Each bigit must be printable as separate hex-character. - ASSERT(kBigitSize % 4 == 0); - const int kHexCharsPerBigit = kBigitSize / 4; + DOUBLE_CONVERSION_ASSERT(kBigitSize % 4 == 0); + static const int kHexCharsPerBigit = kBigitSize / 4; - if (used_digits_ == 0) { - if (buffer_size < 2) return false; + if (used_bigits_ == 0) { + if (buffer_size < 2) { + return false; + } buffer[0] = '0'; buffer[1] = '\0'; return true; } // We add 1 for the terminating '\0' character. - int needed_chars = (BigitLength() - 1) * kHexCharsPerBigit + - SizeInHexChars(bigits_[used_digits_ - 1]) + 1; - if (needed_chars > buffer_size) return false; + const int needed_chars = (BigitLength() - 1) * kHexCharsPerBigit + + SizeInHexChars(RawBigit(used_bigits_ - 1)) + 1; + if (needed_chars > buffer_size) { + return false; + } int string_index = needed_chars - 1; buffer[string_index--] = '\0'; for (int i = 0; i < exponent_; ++i) { @@ -591,15 +612,15 @@ bool Bignum::ToHexString(char* buffer, int buffer_size) const { buffer[string_index--] = '0'; } } - for (int i = 0; i < used_digits_ - 1; ++i) { - Chunk current_bigit = bigits_[i]; + for (int i = 0; i < used_bigits_ - 1; ++i) { + Chunk current_bigit = RawBigit(i); for (int j = 0; j < kHexCharsPerBigit; ++j) { buffer[string_index--] = HexCharOfValue(current_bigit & 0xF); current_bigit >>= 4; } } // And finally the last bigit. - Chunk most_significant_bigit = bigits_[used_digits_ - 1]; + Chunk most_significant_bigit = RawBigit(used_bigits_ - 1); while (most_significant_bigit != 0) { buffer[string_index--] = HexCharOfValue(most_significant_bigit & 0xF); most_significant_bigit >>= 4; @@ -608,25 +629,37 @@ bool Bignum::ToHexString(char* buffer, int buffer_size) const { } -Bignum::Chunk Bignum::BigitAt(int index) const { - if (index >= BigitLength()) return 0; - if (index < exponent_) return 0; - return bigits_[index - exponent_]; +Bignum::Chunk Bignum::BigitOrZero(const int index) const { + if (index >= BigitLength()) { + return 0; + } + if (index < exponent_) { + return 0; + } + return RawBigit(index - exponent_); } int Bignum::Compare(const Bignum& a, const Bignum& b) { - ASSERT(a.IsClamped()); - ASSERT(b.IsClamped()); - int bigit_length_a = a.BigitLength(); - int bigit_length_b = b.BigitLength(); - if (bigit_length_a < bigit_length_b) return -1; - if (bigit_length_a > bigit_length_b) return +1; - for (int i = bigit_length_a - 1; i >= Min(a.exponent_, b.exponent_); --i) { - Chunk bigit_a = a.BigitAt(i); - Chunk bigit_b = b.BigitAt(i); - if (bigit_a < bigit_b) return -1; - if (bigit_a > bigit_b) return +1; + DOUBLE_CONVERSION_ASSERT(a.IsClamped()); + DOUBLE_CONVERSION_ASSERT(b.IsClamped()); + const int bigit_length_a = a.BigitLength(); + const int bigit_length_b = b.BigitLength(); + if (bigit_length_a < bigit_length_b) { + return -1; + } + if (bigit_length_a > bigit_length_b) { + return +1; + } + for (int i = bigit_length_a - 1; i >= (std::min)(a.exponent_, b.exponent_); --i) { + const Chunk bigit_a = a.BigitOrZero(i); + const Chunk bigit_b = b.BigitOrZero(i); + if (bigit_a < bigit_b) { + return -1; + } + if (bigit_a > bigit_b) { + return +1; + } // Otherwise they are equal up to this digit. Try the next digit. } return 0; @@ -634,14 +667,18 @@ int Bignum::Compare(const Bignum& a, const Bignum& b) { int Bignum::PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c) { - ASSERT(a.IsClamped()); - ASSERT(b.IsClamped()); - ASSERT(c.IsClamped()); + DOUBLE_CONVERSION_ASSERT(a.IsClamped()); + DOUBLE_CONVERSION_ASSERT(b.IsClamped()); + DOUBLE_CONVERSION_ASSERT(c.IsClamped()); if (a.BigitLength() < b.BigitLength()) { return PlusCompare(b, a, c); } - if (a.BigitLength() + 1 < c.BigitLength()) return -1; - if (a.BigitLength() > c.BigitLength()) return +1; + if (a.BigitLength() + 1 < c.BigitLength()) { + return -1; + } + if (a.BigitLength() > c.BigitLength()) { + return +1; + } // The exponent encodes 0-bigits. So if there are more 0-digits in 'a' than // 'b' has digits, then the bigit-length of 'a'+'b' must be equal to the one // of 'a'. @@ -651,92 +688,83 @@ int Bignum::PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c) { Chunk borrow = 0; // Starting at min_exponent all digits are == 0. So no need to compare them. - int min_exponent = Min(Min(a.exponent_, b.exponent_), c.exponent_); + const int min_exponent = (std::min)((std::min)(a.exponent_, b.exponent_), c.exponent_); for (int i = c.BigitLength() - 1; i >= min_exponent; --i) { - Chunk chunk_a = a.BigitAt(i); - Chunk chunk_b = b.BigitAt(i); - Chunk chunk_c = c.BigitAt(i); - Chunk sum = chunk_a + chunk_b; + const Chunk chunk_a = a.BigitOrZero(i); + const Chunk chunk_b = b.BigitOrZero(i); + const Chunk chunk_c = c.BigitOrZero(i); + const Chunk sum = chunk_a + chunk_b; if (sum > chunk_c + borrow) { return +1; } else { borrow = chunk_c + borrow - sum; - if (borrow > 1) return -1; + if (borrow > 1) { + return -1; + } borrow <<= kBigitSize; } } - if (borrow == 0) return 0; + if (borrow == 0) { + return 0; + } return -1; } void Bignum::Clamp() { - while (used_digits_ > 0 && bigits_[used_digits_ - 1] == 0) { - used_digits_--; + while (used_bigits_ > 0 && RawBigit(used_bigits_ - 1) == 0) { + used_bigits_--; } - if (used_digits_ == 0) { + if (used_bigits_ == 0) { // Zero. exponent_ = 0; } } -bool Bignum::IsClamped() const { - return used_digits_ == 0 || bigits_[used_digits_ - 1] != 0; -} - - -void Bignum::Zero() { - for (int i = 0; i < used_digits_; ++i) { - bigits_[i] = 0; - } - used_digits_ = 0; - exponent_ = 0; -} - - void Bignum::Align(const Bignum& other) { if (exponent_ > other.exponent_) { - // If "X" represents a "hidden" digit (by the exponent) then we are in the + // If "X" represents a "hidden" bigit (by the exponent) then we are in the // following case (a == this, b == other): // a: aaaaaaXXXX or a: aaaaaXXX // b: bbbbbbX b: bbbbbbbbXX // We replace some of the hidden digits (X) of a with 0 digits. // a: aaaaaa000X or a: aaaaa0XX - int zero_digits = exponent_ - other.exponent_; - EnsureCapacity(used_digits_ + zero_digits); - for (int i = used_digits_ - 1; i >= 0; --i) { - bigits_[i + zero_digits] = bigits_[i]; + const int zero_bigits = exponent_ - other.exponent_; + EnsureCapacity(used_bigits_ + zero_bigits); + for (int i = used_bigits_ - 1; i >= 0; --i) { + RawBigit(i + zero_bigits) = RawBigit(i); } - for (int i = 0; i < zero_digits; ++i) { - bigits_[i] = 0; + for (int i = 0; i < zero_bigits; ++i) { + RawBigit(i) = 0; } - used_digits_ += zero_digits; - exponent_ -= zero_digits; - ASSERT(used_digits_ >= 0); - ASSERT(exponent_ >= 0); + used_bigits_ += zero_bigits; + exponent_ -= zero_bigits; + + DOUBLE_CONVERSION_ASSERT(used_bigits_ >= 0); + DOUBLE_CONVERSION_ASSERT(exponent_ >= 0); } } -void Bignum::BigitsShiftLeft(int shift_amount) { - ASSERT(shift_amount < kBigitSize); - ASSERT(shift_amount >= 0); +void Bignum::BigitsShiftLeft(const int shift_amount) { + DOUBLE_CONVERSION_ASSERT(shift_amount < kBigitSize); + DOUBLE_CONVERSION_ASSERT(shift_amount >= 0); Chunk carry = 0; - for (int i = 0; i < used_digits_; ++i) { - Chunk new_carry = bigits_[i] >> (kBigitSize - shift_amount); - bigits_[i] = ((bigits_[i] << shift_amount) + carry) & kBigitMask; + for (int i = 0; i < used_bigits_; ++i) { + const Chunk new_carry = RawBigit(i) >> (kBigitSize - shift_amount); + RawBigit(i) = ((RawBigit(i) << shift_amount) + carry) & kBigitMask; carry = new_carry; } if (carry != 0) { - bigits_[used_digits_] = carry; - used_digits_++; + RawBigit(used_bigits_) = carry; + used_bigits_++; } } -void Bignum::SubtractTimes(const Bignum& other, int factor) { - ASSERT(exponent_ <= other.exponent_); +void Bignum::SubtractTimes(const Bignum& other, const int factor) { + DOUBLE_CONVERSION_ASSERT(exponent_ <= other.exponent_); if (factor < 3) { for (int i = 0; i < factor; ++i) { SubtractBignum(other); @@ -744,19 +772,21 @@ void Bignum::SubtractTimes(const Bignum& other, int factor) { return; } Chunk borrow = 0; - int exponent_diff = other.exponent_ - exponent_; - for (int i = 0; i < other.used_digits_; ++i) { - DoubleChunk product = static_cast(factor) * other.bigits_[i]; - DoubleChunk remove = borrow + product; - Chunk difference = bigits_[i + exponent_diff] - (remove & kBigitMask); - bigits_[i + exponent_diff] = difference & kBigitMask; + const int exponent_diff = other.exponent_ - exponent_; + for (int i = 0; i < other.used_bigits_; ++i) { + const DoubleChunk product = static_cast(factor) * other.RawBigit(i); + const DoubleChunk remove = borrow + product; + const Chunk difference = RawBigit(i + exponent_diff) - (remove & kBigitMask); + RawBigit(i + exponent_diff) = difference & kBigitMask; borrow = static_cast((difference >> (kChunkSize - 1)) + (remove >> kBigitSize)); } - for (int i = other.used_digits_ + exponent_diff; i < used_digits_; ++i) { - if (borrow == 0) return; - Chunk difference = bigits_[i] - borrow; - bigits_[i] = difference & kBigitMask; + for (int i = other.used_bigits_ + exponent_diff; i < used_bigits_; ++i) { + if (borrow == 0) { + return; + } + const Chunk difference = RawBigit(i) - borrow; + RawBigit(i) = difference & kBigitMask; borrow = difference >> (kChunkSize - 1); } Clamp(); diff --git a/double-conversion/bignum.h b/double-conversion/bignum.h index c385f223..14d1ca86 100644 --- a/double-conversion/bignum.h +++ b/double-conversion/bignum.h @@ -39,26 +39,27 @@ class Bignum { // exponent. static const int kMaxSignificantBits = 3584; - Bignum(); - void AssignUInt16(uint16_t value); + Bignum() : used_bigits_(0), exponent_(0) {} + + void AssignUInt16(const uint16_t value); void AssignUInt64(uint64_t value); void AssignBignum(const Bignum& other); - void AssignDecimalString(Vector value); - void AssignHexString(Vector value); + void AssignDecimalString(const Vector value); + void AssignHexString(const Vector value); - void AssignPowerUInt16(uint16_t base, int exponent); + void AssignPowerUInt16(uint16_t base, const int exponent); - void AddUInt64(uint64_t operand); + void AddUInt64(const uint64_t operand); void AddBignum(const Bignum& other); // Precondition: this >= other. void SubtractBignum(const Bignum& other); void Square(); - void ShiftLeft(int shift_amount); - void MultiplyByUInt32(uint32_t factor); - void MultiplyByUInt64(uint64_t factor); - void MultiplyByPowerOfTen(int exponent); + void ShiftLeft(const int shift_amount); + void MultiplyByUInt32(const uint32_t factor); + void MultiplyByUInt64(const uint64_t factor); + void MultiplyByPowerOfTen(const int exponent); void Times10() { return MultiplyByUInt32(10); } // Pseudocode: // int result = this / other; @@ -66,7 +67,7 @@ class Bignum { // In the worst case this function is in O(this/other). uint16_t DivideModuloIntBignum(const Bignum& other); - bool ToHexString(char* buffer, int buffer_size) const; + bool ToHexString(char* buffer, const int buffer_size) const; // Returns // -1 if a < b, @@ -110,33 +111,40 @@ class Bignum { // grow. There are no checks if the stack-allocated space is sufficient. static const int kBigitCapacity = kMaxSignificantBits / kBigitSize; - void EnsureCapacity(int size) { + static void EnsureCapacity(const int size) { if (size > kBigitCapacity) { - UNREACHABLE(); + DOUBLE_CONVERSION_UNREACHABLE(); } } void Align(const Bignum& other); void Clamp(); - bool IsClamped() const; - void Zero(); + bool IsClamped() const { + return used_bigits_ == 0 || RawBigit(used_bigits_ - 1) != 0; + } + void Zero() { + used_bigits_ = 0; + exponent_ = 0; + } // Requires this to have enough capacity (no tests done). - // Updates used_digits_ if necessary. + // Updates used_bigits_ if necessary. // shift_amount must be < kBigitSize. - void BigitsShiftLeft(int shift_amount); - // BigitLength includes the "hidden" digits encoded in the exponent. - int BigitLength() const { return used_digits_ + exponent_; } - Chunk BigitAt(int index) const; - void SubtractTimes(const Bignum& other, int factor); - + void BigitsShiftLeft(const int shift_amount); + // BigitLength includes the "hidden" bigits encoded in the exponent. + int BigitLength() const { return used_bigits_ + exponent_; } + Chunk& RawBigit(const int index); + const Chunk& RawBigit(const int index) const; + Chunk BigitOrZero(const int index) const; + void SubtractTimes(const Bignum& other, const int factor); + + // The Bignum's value is value(bigits_buffer_) * 2^(exponent_ * kBigitSize), + // where the value of the buffer consists of the lower kBigitSize bits of + // the first used_bigits_ Chunks in bigits_buffer_, first chunk has lowest + // significant bits. + int16_t used_bigits_; + int16_t exponent_; Chunk bigits_buffer_[kBigitCapacity]; - // A vector backed by bigits_buffer_. This way accesses to the array are - // checked for out-of-bounds errors. - Vector bigits_; - int used_digits_; - // The Bignum's value equals value(bigits_) * 2^(exponent_ * kBigitSize). - int exponent_; - - DISALLOW_COPY_AND_ASSIGN(Bignum); + + DOUBLE_CONVERSION_DISALLOW_COPY_AND_ASSIGN(Bignum); }; } // namespace double_conversion diff --git a/double-conversion/cached-powers.cc b/double-conversion/cached-powers.cc index 2b43f064..56bdfc9d 100644 --- a/double-conversion/cached-powers.cc +++ b/double-conversion/cached-powers.cc @@ -25,9 +25,9 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include -#include -#include +#include +#include +#include #include "utils.h" @@ -35,6 +35,8 @@ namespace double_conversion { +namespace PowersOfTenCache { + struct CachedPower { uint64_t significand; int16_t binary_exponent; @@ -42,103 +44,99 @@ struct CachedPower { }; static const CachedPower kCachedPowers[] = { - {UINT64_2PART_C(0xfa8fd5a0, 081c0288), -1220, -348}, - {UINT64_2PART_C(0xbaaee17f, a23ebf76), -1193, -340}, - {UINT64_2PART_C(0x8b16fb20, 3055ac76), -1166, -332}, - {UINT64_2PART_C(0xcf42894a, 5dce35ea), -1140, -324}, - {UINT64_2PART_C(0x9a6bb0aa, 55653b2d), -1113, -316}, - {UINT64_2PART_C(0xe61acf03, 3d1a45df), -1087, -308}, - {UINT64_2PART_C(0xab70fe17, c79ac6ca), -1060, -300}, - {UINT64_2PART_C(0xff77b1fc, bebcdc4f), -1034, -292}, - {UINT64_2PART_C(0xbe5691ef, 416bd60c), -1007, -284}, - {UINT64_2PART_C(0x8dd01fad, 907ffc3c), -980, -276}, - {UINT64_2PART_C(0xd3515c28, 31559a83), -954, -268}, - {UINT64_2PART_C(0x9d71ac8f, ada6c9b5), -927, -260}, - {UINT64_2PART_C(0xea9c2277, 23ee8bcb), -901, -252}, - {UINT64_2PART_C(0xaecc4991, 4078536d), -874, -244}, - {UINT64_2PART_C(0x823c1279, 5db6ce57), -847, -236}, - {UINT64_2PART_C(0xc2109436, 4dfb5637), -821, -228}, - {UINT64_2PART_C(0x9096ea6f, 3848984f), -794, -220}, - {UINT64_2PART_C(0xd77485cb, 25823ac7), -768, -212}, - {UINT64_2PART_C(0xa086cfcd, 97bf97f4), -741, -204}, - {UINT64_2PART_C(0xef340a98, 172aace5), -715, -196}, - {UINT64_2PART_C(0xb23867fb, 2a35b28e), -688, -188}, - {UINT64_2PART_C(0x84c8d4df, d2c63f3b), -661, -180}, - {UINT64_2PART_C(0xc5dd4427, 1ad3cdba), -635, -172}, - {UINT64_2PART_C(0x936b9fce, bb25c996), -608, -164}, - {UINT64_2PART_C(0xdbac6c24, 7d62a584), -582, -156}, - {UINT64_2PART_C(0xa3ab6658, 0d5fdaf6), -555, -148}, - {UINT64_2PART_C(0xf3e2f893, dec3f126), -529, -140}, - {UINT64_2PART_C(0xb5b5ada8, aaff80b8), -502, -132}, - {UINT64_2PART_C(0x87625f05, 6c7c4a8b), -475, -124}, - {UINT64_2PART_C(0xc9bcff60, 34c13053), -449, -116}, - {UINT64_2PART_C(0x964e858c, 91ba2655), -422, -108}, - {UINT64_2PART_C(0xdff97724, 70297ebd), -396, -100}, - {UINT64_2PART_C(0xa6dfbd9f, b8e5b88f), -369, -92}, - {UINT64_2PART_C(0xf8a95fcf, 88747d94), -343, -84}, - {UINT64_2PART_C(0xb9447093, 8fa89bcf), -316, -76}, - {UINT64_2PART_C(0x8a08f0f8, bf0f156b), -289, -68}, - {UINT64_2PART_C(0xcdb02555, 653131b6), -263, -60}, - {UINT64_2PART_C(0x993fe2c6, d07b7fac), -236, -52}, - {UINT64_2PART_C(0xe45c10c4, 2a2b3b06), -210, -44}, - {UINT64_2PART_C(0xaa242499, 697392d3), -183, -36}, - {UINT64_2PART_C(0xfd87b5f2, 8300ca0e), -157, -28}, - {UINT64_2PART_C(0xbce50864, 92111aeb), -130, -20}, - {UINT64_2PART_C(0x8cbccc09, 6f5088cc), -103, -12}, - {UINT64_2PART_C(0xd1b71758, e219652c), -77, -4}, - {UINT64_2PART_C(0x9c400000, 00000000), -50, 4}, - {UINT64_2PART_C(0xe8d4a510, 00000000), -24, 12}, - {UINT64_2PART_C(0xad78ebc5, ac620000), 3, 20}, - {UINT64_2PART_C(0x813f3978, f8940984), 30, 28}, - {UINT64_2PART_C(0xc097ce7b, c90715b3), 56, 36}, - {UINT64_2PART_C(0x8f7e32ce, 7bea5c70), 83, 44}, - {UINT64_2PART_C(0xd5d238a4, abe98068), 109, 52}, - {UINT64_2PART_C(0x9f4f2726, 179a2245), 136, 60}, - {UINT64_2PART_C(0xed63a231, d4c4fb27), 162, 68}, - {UINT64_2PART_C(0xb0de6538, 8cc8ada8), 189, 76}, - {UINT64_2PART_C(0x83c7088e, 1aab65db), 216, 84}, - {UINT64_2PART_C(0xc45d1df9, 42711d9a), 242, 92}, - {UINT64_2PART_C(0x924d692c, a61be758), 269, 100}, - {UINT64_2PART_C(0xda01ee64, 1a708dea), 295, 108}, - {UINT64_2PART_C(0xa26da399, 9aef774a), 322, 116}, - {UINT64_2PART_C(0xf209787b, b47d6b85), 348, 124}, - {UINT64_2PART_C(0xb454e4a1, 79dd1877), 375, 132}, - {UINT64_2PART_C(0x865b8692, 5b9bc5c2), 402, 140}, - {UINT64_2PART_C(0xc83553c5, c8965d3d), 428, 148}, - {UINT64_2PART_C(0x952ab45c, fa97a0b3), 455, 156}, - {UINT64_2PART_C(0xde469fbd, 99a05fe3), 481, 164}, - {UINT64_2PART_C(0xa59bc234, db398c25), 508, 172}, - {UINT64_2PART_C(0xf6c69a72, a3989f5c), 534, 180}, - {UINT64_2PART_C(0xb7dcbf53, 54e9bece), 561, 188}, - {UINT64_2PART_C(0x88fcf317, f22241e2), 588, 196}, - {UINT64_2PART_C(0xcc20ce9b, d35c78a5), 614, 204}, - {UINT64_2PART_C(0x98165af3, 7b2153df), 641, 212}, - {UINT64_2PART_C(0xe2a0b5dc, 971f303a), 667, 220}, - {UINT64_2PART_C(0xa8d9d153, 5ce3b396), 694, 228}, - {UINT64_2PART_C(0xfb9b7cd9, a4a7443c), 720, 236}, - {UINT64_2PART_C(0xbb764c4c, a7a44410), 747, 244}, - {UINT64_2PART_C(0x8bab8eef, b6409c1a), 774, 252}, - {UINT64_2PART_C(0xd01fef10, a657842c), 800, 260}, - {UINT64_2PART_C(0x9b10a4e5, e9913129), 827, 268}, - {UINT64_2PART_C(0xe7109bfb, a19c0c9d), 853, 276}, - {UINT64_2PART_C(0xac2820d9, 623bf429), 880, 284}, - {UINT64_2PART_C(0x80444b5e, 7aa7cf85), 907, 292}, - {UINT64_2PART_C(0xbf21e440, 03acdd2d), 933, 300}, - {UINT64_2PART_C(0x8e679c2f, 5e44ff8f), 960, 308}, - {UINT64_2PART_C(0xd433179d, 9c8cb841), 986, 316}, - {UINT64_2PART_C(0x9e19db92, b4e31ba9), 1013, 324}, - {UINT64_2PART_C(0xeb96bf6e, badf77d9), 1039, 332}, - {UINT64_2PART_C(0xaf87023b, 9bf0ee6b), 1066, 340}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xfa8fd5a0, 081c0288), -1220, -348}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xbaaee17f, a23ebf76), -1193, -340}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x8b16fb20, 3055ac76), -1166, -332}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xcf42894a, 5dce35ea), -1140, -324}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x9a6bb0aa, 55653b2d), -1113, -316}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xe61acf03, 3d1a45df), -1087, -308}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xab70fe17, c79ac6ca), -1060, -300}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xff77b1fc, bebcdc4f), -1034, -292}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xbe5691ef, 416bd60c), -1007, -284}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x8dd01fad, 907ffc3c), -980, -276}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xd3515c28, 31559a83), -954, -268}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x9d71ac8f, ada6c9b5), -927, -260}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xea9c2277, 23ee8bcb), -901, -252}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xaecc4991, 4078536d), -874, -244}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x823c1279, 5db6ce57), -847, -236}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xc2109436, 4dfb5637), -821, -228}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x9096ea6f, 3848984f), -794, -220}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xd77485cb, 25823ac7), -768, -212}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xa086cfcd, 97bf97f4), -741, -204}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xef340a98, 172aace5), -715, -196}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xb23867fb, 2a35b28e), -688, -188}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x84c8d4df, d2c63f3b), -661, -180}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xc5dd4427, 1ad3cdba), -635, -172}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x936b9fce, bb25c996), -608, -164}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xdbac6c24, 7d62a584), -582, -156}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xa3ab6658, 0d5fdaf6), -555, -148}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xf3e2f893, dec3f126), -529, -140}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xb5b5ada8, aaff80b8), -502, -132}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x87625f05, 6c7c4a8b), -475, -124}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xc9bcff60, 34c13053), -449, -116}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x964e858c, 91ba2655), -422, -108}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xdff97724, 70297ebd), -396, -100}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xa6dfbd9f, b8e5b88f), -369, -92}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xf8a95fcf, 88747d94), -343, -84}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xb9447093, 8fa89bcf), -316, -76}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x8a08f0f8, bf0f156b), -289, -68}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xcdb02555, 653131b6), -263, -60}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x993fe2c6, d07b7fac), -236, -52}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xe45c10c4, 2a2b3b06), -210, -44}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xaa242499, 697392d3), -183, -36}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xfd87b5f2, 8300ca0e), -157, -28}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xbce50864, 92111aeb), -130, -20}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x8cbccc09, 6f5088cc), -103, -12}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xd1b71758, e219652c), -77, -4}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x9c400000, 00000000), -50, 4}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xe8d4a510, 00000000), -24, 12}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xad78ebc5, ac620000), 3, 20}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x813f3978, f8940984), 30, 28}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xc097ce7b, c90715b3), 56, 36}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x8f7e32ce, 7bea5c70), 83, 44}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xd5d238a4, abe98068), 109, 52}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x9f4f2726, 179a2245), 136, 60}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xed63a231, d4c4fb27), 162, 68}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xb0de6538, 8cc8ada8), 189, 76}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x83c7088e, 1aab65db), 216, 84}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xc45d1df9, 42711d9a), 242, 92}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x924d692c, a61be758), 269, 100}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xda01ee64, 1a708dea), 295, 108}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xa26da399, 9aef774a), 322, 116}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xf209787b, b47d6b85), 348, 124}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xb454e4a1, 79dd1877), 375, 132}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x865b8692, 5b9bc5c2), 402, 140}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xc83553c5, c8965d3d), 428, 148}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x952ab45c, fa97a0b3), 455, 156}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xde469fbd, 99a05fe3), 481, 164}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xa59bc234, db398c25), 508, 172}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xf6c69a72, a3989f5c), 534, 180}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xb7dcbf53, 54e9bece), 561, 188}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x88fcf317, f22241e2), 588, 196}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xcc20ce9b, d35c78a5), 614, 204}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x98165af3, 7b2153df), 641, 212}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xe2a0b5dc, 971f303a), 667, 220}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xa8d9d153, 5ce3b396), 694, 228}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xfb9b7cd9, a4a7443c), 720, 236}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xbb764c4c, a7a44410), 747, 244}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x8bab8eef, b6409c1a), 774, 252}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xd01fef10, a657842c), 800, 260}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x9b10a4e5, e9913129), 827, 268}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xe7109bfb, a19c0c9d), 853, 276}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xac2820d9, 623bf429), 880, 284}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x80444b5e, 7aa7cf85), 907, 292}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xbf21e440, 03acdd2d), 933, 300}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x8e679c2f, 5e44ff8f), 960, 308}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xd433179d, 9c8cb841), 986, 316}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x9e19db92, b4e31ba9), 1013, 324}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xeb96bf6e, badf77d9), 1039, 332}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xaf87023b, 9bf0ee6b), 1066, 340}, }; static const int kCachedPowersOffset = 348; // -1 * the first decimal_exponent. static const double kD_1_LOG2_10 = 0.30102999566398114; // 1 / lg(10) -// Difference between the decimal exponents in the table above. -const int PowersOfTenCache::kDecimalExponentDistance = 8; -const int PowersOfTenCache::kMinDecimalExponent = -348; -const int PowersOfTenCache::kMaxDecimalExponent = 340; -void PowersOfTenCache::GetCachedPowerForBinaryExponentRange( +void GetCachedPowerForBinaryExponentRange( int min_exponent, int max_exponent, DiyFp* power, @@ -148,28 +146,30 @@ void PowersOfTenCache::GetCachedPowerForBinaryExponentRange( int foo = kCachedPowersOffset; int index = (foo + static_cast(k) - 1) / kDecimalExponentDistance + 1; - ASSERT(0 <= index && index < static_cast(ARRAY_SIZE(kCachedPowers))); + DOUBLE_CONVERSION_ASSERT(0 <= index && index < static_cast(DOUBLE_CONVERSION_ARRAY_SIZE(kCachedPowers))); CachedPower cached_power = kCachedPowers[index]; - ASSERT(min_exponent <= cached_power.binary_exponent); + DOUBLE_CONVERSION_ASSERT(min_exponent <= cached_power.binary_exponent); (void) max_exponent; // Mark variable as used. - ASSERT(cached_power.binary_exponent <= max_exponent); + DOUBLE_CONVERSION_ASSERT(cached_power.binary_exponent <= max_exponent); *decimal_exponent = cached_power.decimal_exponent; *power = DiyFp(cached_power.significand, cached_power.binary_exponent); } -void PowersOfTenCache::GetCachedPowerForDecimalExponent(int requested_exponent, - DiyFp* power, - int* found_exponent) { - ASSERT(kMinDecimalExponent <= requested_exponent); - ASSERT(requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance); +void GetCachedPowerForDecimalExponent(int requested_exponent, + DiyFp* power, + int* found_exponent) { + DOUBLE_CONVERSION_ASSERT(kMinDecimalExponent <= requested_exponent); + DOUBLE_CONVERSION_ASSERT(requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance); int index = (requested_exponent + kCachedPowersOffset) / kDecimalExponentDistance; CachedPower cached_power = kCachedPowers[index]; *power = DiyFp(cached_power.significand, cached_power.binary_exponent); *found_exponent = cached_power.decimal_exponent; - ASSERT(*found_exponent <= requested_exponent); - ASSERT(requested_exponent < *found_exponent + kDecimalExponentDistance); + DOUBLE_CONVERSION_ASSERT(*found_exponent <= requested_exponent); + DOUBLE_CONVERSION_ASSERT(requested_exponent < *found_exponent + kDecimalExponentDistance); } +} // namespace PowersOfTenCache + } // namespace double_conversion diff --git a/double-conversion/cached-powers.h b/double-conversion/cached-powers.h index 61a50614..f38c26d2 100644 --- a/double-conversion/cached-powers.h +++ b/double-conversion/cached-powers.h @@ -32,32 +32,32 @@ namespace double_conversion { -class PowersOfTenCache { - public: +namespace PowersOfTenCache { // Not all powers of ten are cached. The decimal exponent of two neighboring // cached numbers will differ by kDecimalExponentDistance. - static const int kDecimalExponentDistance; + static const int kDecimalExponentDistance = 8; - static const int kMinDecimalExponent; - static const int kMaxDecimalExponent; + static const int kMinDecimalExponent = -348; + static const int kMaxDecimalExponent = 340; // Returns a cached power-of-ten with a binary exponent in the range // [min_exponent; max_exponent] (boundaries included). - static void GetCachedPowerForBinaryExponentRange(int min_exponent, - int max_exponent, - DiyFp* power, - int* decimal_exponent); + void GetCachedPowerForBinaryExponentRange(int min_exponent, + int max_exponent, + DiyFp* power, + int* decimal_exponent); // Returns a cached power of ten x ~= 10^k such that // k <= decimal_exponent < k + kCachedPowersDecimalDistance. // The given decimal_exponent must satisfy // kMinDecimalExponent <= requested_exponent, and // requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance. - static void GetCachedPowerForDecimalExponent(int requested_exponent, - DiyFp* power, - int* found_exponent); -}; + void GetCachedPowerForDecimalExponent(int requested_exponent, + DiyFp* power, + int* found_exponent); + +} // namespace PowersOfTenCache } // namespace double_conversion diff --git a/double-conversion/diy-fp.cc b/double-conversion/diy-fp.cc deleted file mode 100644 index ddd1891b..00000000 --- a/double-conversion/diy-fp.cc +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2010 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -#include "diy-fp.h" -#include "utils.h" - -namespace double_conversion { - -void DiyFp::Multiply(const DiyFp& other) { - // Simply "emulates" a 128 bit multiplication. - // However: the resulting number only contains 64 bits. The least - // significant 64 bits are only used for rounding the most significant 64 - // bits. - const uint64_t kM32 = 0xFFFFFFFFU; - uint64_t a = f_ >> 32; - uint64_t b = f_ & kM32; - uint64_t c = other.f_ >> 32; - uint64_t d = other.f_ & kM32; - uint64_t ac = a * c; - uint64_t bc = b * c; - uint64_t ad = a * d; - uint64_t bd = b * d; - uint64_t tmp = (bd >> 32) + (ad & kM32) + (bc & kM32); - // By adding 1U << 31 to tmp we round the final result. - // Halfway cases will be round up. - tmp += 1U << 31; - uint64_t result_f = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32); - e_ += other.e_ + 64; - f_ = result_f; -} - -} // namespace double_conversion diff --git a/double-conversion/diy-fp.h b/double-conversion/diy-fp.h index 2edf3467..a2200c4d 100644 --- a/double-conversion/diy-fp.h +++ b/double-conversion/diy-fp.h @@ -36,36 +36,55 @@ namespace double_conversion { // with a uint64 significand and an int exponent. Normalized DiyFp numbers will // have the most significant bit of the significand set. // Multiplication and Subtraction do not normalize their results. -// DiyFp are not designed to contain special doubles (NaN and Infinity). +// DiyFp store only non-negative numbers and are not designed to contain special +// doubles (NaN and Infinity). class DiyFp { public: static const int kSignificandSize = 64; DiyFp() : f_(0), e_(0) {} - DiyFp(uint64_t significand, int exponent) : f_(significand), e_(exponent) {} + DiyFp(const uint64_t significand, const int32_t exponent) : f_(significand), e_(exponent) {} - // this = this - other. + // this -= other. // The exponents of both numbers must be the same and the significand of this - // must be bigger than the significand of other. + // must be greater or equal than the significand of other. // The result will not be normalized. void Subtract(const DiyFp& other) { - ASSERT(e_ == other.e_); - ASSERT(f_ >= other.f_); + DOUBLE_CONVERSION_ASSERT(e_ == other.e_); + DOUBLE_CONVERSION_ASSERT(f_ >= other.f_); f_ -= other.f_; } // Returns a - b. - // The exponents of both numbers must be the same and this must be bigger - // than other. The result will not be normalized. + // The exponents of both numbers must be the same and a must be greater + // or equal than b. The result will not be normalized. static DiyFp Minus(const DiyFp& a, const DiyFp& b) { DiyFp result = a; result.Subtract(b); return result; } - - // this = this * other. - void Multiply(const DiyFp& other); + // this *= other. + void Multiply(const DiyFp& other) { + // Simply "emulates" a 128 bit multiplication. + // However: the resulting number only contains 64 bits. The least + // significant 64 bits are only used for rounding the most significant 64 + // bits. + const uint64_t kM32 = 0xFFFFFFFFU; + const uint64_t a = f_ >> 32; + const uint64_t b = f_ & kM32; + const uint64_t c = other.f_ >> 32; + const uint64_t d = other.f_ & kM32; + const uint64_t ac = a * c; + const uint64_t bc = b * c; + const uint64_t ad = a * d; + const uint64_t bd = b * d; + // By adding 1U << 31 to tmp we round the final result. + // Halfway cases will be rounded up. + const uint64_t tmp = (bd >> 32) + (ad & kM32) + (bc & kM32) + (1U << 31); + e_ += other.e_ + 64; + f_ = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32); + } // returns a * b; static DiyFp Times(const DiyFp& a, const DiyFp& b) { @@ -75,13 +94,13 @@ class DiyFp { } void Normalize() { - ASSERT(f_ != 0); + DOUBLE_CONVERSION_ASSERT(f_ != 0); uint64_t significand = f_; - int exponent = e_; + int32_t exponent = e_; - // This method is mainly called for normalizing boundaries. In general - // boundaries need to be shifted by 10 bits. We thus optimize for this case. - const uint64_t k10MSBits = UINT64_2PART_C(0xFFC00000, 00000000); + // This method is mainly called for normalizing boundaries. In general, + // boundaries need to be shifted by 10 bits, and we optimize for this case. + const uint64_t k10MSBits = DOUBLE_CONVERSION_UINT64_2PART_C(0xFFC00000, 00000000); while ((significand & k10MSBits) == 0) { significand <<= 10; exponent -= 10; @@ -101,16 +120,16 @@ class DiyFp { } uint64_t f() const { return f_; } - int e() const { return e_; } + int32_t e() const { return e_; } void set_f(uint64_t new_value) { f_ = new_value; } - void set_e(int new_value) { e_ = new_value; } + void set_e(int32_t new_value) { e_ = new_value; } private: - static const uint64_t kUint64MSB = UINT64_2PART_C(0x80000000, 00000000); + static const uint64_t kUint64MSB = DOUBLE_CONVERSION_UINT64_2PART_C(0x80000000, 00000000); uint64_t f_; - int e_; + int32_t e_; }; } // namespace double_conversion diff --git a/double-conversion/double-conversion.h b/double-conversion/double-conversion.h index 6bdfa8d2..6e8884d8 100644 --- a/double-conversion/double-conversion.h +++ b/double-conversion/double-conversion.h @@ -28,516 +28,7 @@ #ifndef DOUBLE_CONVERSION_DOUBLE_CONVERSION_H_ #define DOUBLE_CONVERSION_DOUBLE_CONVERSION_H_ -#include "utils.h" - -namespace double_conversion { - -class DoubleToStringConverter { - public: - // When calling ToFixed with a double > 10^kMaxFixedDigitsBeforePoint - // or a requested_digits parameter > kMaxFixedDigitsAfterPoint then the - // function returns false. - static const int kMaxFixedDigitsBeforePoint = 60; - static const int kMaxFixedDigitsAfterPoint = 60; - - // When calling ToExponential with a requested_digits - // parameter > kMaxExponentialDigits then the function returns false. - static const int kMaxExponentialDigits = 120; - - // When calling ToPrecision with a requested_digits - // parameter < kMinPrecisionDigits or requested_digits > kMaxPrecisionDigits - // then the function returns false. - static const int kMinPrecisionDigits = 1; - static const int kMaxPrecisionDigits = 120; - - enum Flags { - NO_FLAGS = 0, - EMIT_POSITIVE_EXPONENT_SIGN = 1, - EMIT_TRAILING_DECIMAL_POINT = 2, - EMIT_TRAILING_ZERO_AFTER_POINT = 4, - UNIQUE_ZERO = 8 - }; - - // Flags should be a bit-or combination of the possible Flags-enum. - // - NO_FLAGS: no special flags. - // - EMIT_POSITIVE_EXPONENT_SIGN: when the number is converted into exponent - // form, emits a '+' for positive exponents. Example: 1.2e+2. - // - EMIT_TRAILING_DECIMAL_POINT: when the input number is an integer and is - // converted into decimal format then a trailing decimal point is appended. - // Example: 2345.0 is converted to "2345.". - // - EMIT_TRAILING_ZERO_AFTER_POINT: in addition to a trailing decimal point - // emits a trailing '0'-character. This flag requires the - // EXMIT_TRAILING_DECIMAL_POINT flag. - // Example: 2345.0 is converted to "2345.0". - // - UNIQUE_ZERO: "-0.0" is converted to "0.0". - // - // Infinity symbol and nan_symbol provide the string representation for these - // special values. If the string is NULL and the special value is encountered - // then the conversion functions return false. - // - // The exponent_character is used in exponential representations. It is - // usually 'e' or 'E'. - // - // When converting to the shortest representation the converter will - // represent input numbers in decimal format if they are in the interval - // [10^decimal_in_shortest_low; 10^decimal_in_shortest_high[ - // (lower boundary included, greater boundary excluded). - // Example: with decimal_in_shortest_low = -6 and - // decimal_in_shortest_high = 21: - // ToShortest(0.000001) -> "0.000001" - // ToShortest(0.0000001) -> "1e-7" - // ToShortest(111111111111111111111.0) -> "111111111111111110000" - // ToShortest(100000000000000000000.0) -> "100000000000000000000" - // ToShortest(1111111111111111111111.0) -> "1.1111111111111111e+21" - // - // When converting to precision mode the converter may add - // max_leading_padding_zeroes before returning the number in exponential - // format. - // Example with max_leading_padding_zeroes_in_precision_mode = 6. - // ToPrecision(0.0000012345, 2) -> "0.0000012" - // ToPrecision(0.00000012345, 2) -> "1.2e-7" - // Similarily the converter may add up to - // max_trailing_padding_zeroes_in_precision_mode in precision mode to avoid - // returning an exponential representation. A zero added by the - // EMIT_TRAILING_ZERO_AFTER_POINT flag is counted for this limit. - // Examples for max_trailing_padding_zeroes_in_precision_mode = 1: - // ToPrecision(230.0, 2) -> "230" - // ToPrecision(230.0, 2) -> "230." with EMIT_TRAILING_DECIMAL_POINT. - // ToPrecision(230.0, 2) -> "2.3e2" with EMIT_TRAILING_ZERO_AFTER_POINT. - DoubleToStringConverter(int flags, - const char* infinity_symbol, - const char* nan_symbol, - char exponent_character, - int decimal_in_shortest_low, - int decimal_in_shortest_high, - int max_leading_padding_zeroes_in_precision_mode, - int max_trailing_padding_zeroes_in_precision_mode) - : flags_(flags), - infinity_symbol_(infinity_symbol), - nan_symbol_(nan_symbol), - exponent_character_(exponent_character), - decimal_in_shortest_low_(decimal_in_shortest_low), - decimal_in_shortest_high_(decimal_in_shortest_high), - max_leading_padding_zeroes_in_precision_mode_( - max_leading_padding_zeroes_in_precision_mode), - max_trailing_padding_zeroes_in_precision_mode_( - max_trailing_padding_zeroes_in_precision_mode) { - // When 'trailing zero after the point' is set, then 'trailing point' - // must be set too. - ASSERT(((flags & EMIT_TRAILING_DECIMAL_POINT) != 0) || - !((flags & EMIT_TRAILING_ZERO_AFTER_POINT) != 0)); - } - - // Returns a converter following the EcmaScript specification. - static const DoubleToStringConverter& EcmaScriptConverter(); - - // Computes the shortest string of digits that correctly represent the input - // number. Depending on decimal_in_shortest_low and decimal_in_shortest_high - // (see constructor) it then either returns a decimal representation, or an - // exponential representation. - // Example with decimal_in_shortest_low = -6, - // decimal_in_shortest_high = 21, - // EMIT_POSITIVE_EXPONENT_SIGN activated, and - // EMIT_TRAILING_DECIMAL_POINT deactived: - // ToShortest(0.000001) -> "0.000001" - // ToShortest(0.0000001) -> "1e-7" - // ToShortest(111111111111111111111.0) -> "111111111111111110000" - // ToShortest(100000000000000000000.0) -> "100000000000000000000" - // ToShortest(1111111111111111111111.0) -> "1.1111111111111111e+21" - // - // Note: the conversion may round the output if the returned string - // is accurate enough to uniquely identify the input-number. - // For example the most precise representation of the double 9e59 equals - // "899999999999999918767229449717619953810131273674690656206848", but - // the converter will return the shorter (but still correct) "9e59". - // - // Returns true if the conversion succeeds. The conversion always succeeds - // except when the input value is special and no infinity_symbol or - // nan_symbol has been given to the constructor. - bool ToShortest(double value, StringBuilder* result_builder) const { - return ToShortestIeeeNumber(value, result_builder, SHORTEST); - } - - // Same as ToShortest, but for single-precision floats. - bool ToShortestSingle(float value, StringBuilder* result_builder) const { - return ToShortestIeeeNumber(value, result_builder, SHORTEST_SINGLE); - } - - - // Computes a decimal representation with a fixed number of digits after the - // decimal point. The last emitted digit is rounded. - // - // Examples: - // ToFixed(3.12, 1) -> "3.1" - // ToFixed(3.1415, 3) -> "3.142" - // ToFixed(1234.56789, 4) -> "1234.5679" - // ToFixed(1.23, 5) -> "1.23000" - // ToFixed(0.1, 4) -> "0.1000" - // ToFixed(1e30, 2) -> "1000000000000000019884624838656.00" - // ToFixed(0.1, 30) -> "0.100000000000000005551115123126" - // ToFixed(0.1, 17) -> "0.10000000000000001" - // - // If requested_digits equals 0, then the tail of the result depends on - // the EMIT_TRAILING_DECIMAL_POINT and EMIT_TRAILING_ZERO_AFTER_POINT. - // Examples, for requested_digits == 0, - // let EMIT_TRAILING_DECIMAL_POINT and EMIT_TRAILING_ZERO_AFTER_POINT be - // - false and false: then 123.45 -> 123 - // 0.678 -> 1 - // - true and false: then 123.45 -> 123. - // 0.678 -> 1. - // - true and true: then 123.45 -> 123.0 - // 0.678 -> 1.0 - // - // Returns true if the conversion succeeds. The conversion always succeeds - // except for the following cases: - // - the input value is special and no infinity_symbol or nan_symbol has - // been provided to the constructor, - // - 'value' > 10^kMaxFixedDigitsBeforePoint, or - // - 'requested_digits' > kMaxFixedDigitsAfterPoint. - // The last two conditions imply that the result will never contain more than - // 1 + kMaxFixedDigitsBeforePoint + 1 + kMaxFixedDigitsAfterPoint characters - // (one additional character for the sign, and one for the decimal point). - bool ToFixed(double value, - int requested_digits, - StringBuilder* result_builder) const; - - // Computes a representation in exponential format with requested_digits - // after the decimal point. The last emitted digit is rounded. - // If requested_digits equals -1, then the shortest exponential representation - // is computed. - // - // Examples with EMIT_POSITIVE_EXPONENT_SIGN deactivated, and - // exponent_character set to 'e'. - // ToExponential(3.12, 1) -> "3.1e0" - // ToExponential(5.0, 3) -> "5.000e0" - // ToExponential(0.001, 2) -> "1.00e-3" - // ToExponential(3.1415, -1) -> "3.1415e0" - // ToExponential(3.1415, 4) -> "3.1415e0" - // ToExponential(3.1415, 3) -> "3.142e0" - // ToExponential(123456789000000, 3) -> "1.235e14" - // ToExponential(1000000000000000019884624838656.0, -1) -> "1e30" - // ToExponential(1000000000000000019884624838656.0, 32) -> - // "1.00000000000000001988462483865600e30" - // ToExponential(1234, 0) -> "1e3" - // - // Returns true if the conversion succeeds. The conversion always succeeds - // except for the following cases: - // - the input value is special and no infinity_symbol or nan_symbol has - // been provided to the constructor, - // - 'requested_digits' > kMaxExponentialDigits. - // The last condition implies that the result will never contain more than - // kMaxExponentialDigits + 8 characters (the sign, the digit before the - // decimal point, the decimal point, the exponent character, the - // exponent's sign, and at most 3 exponent digits). - bool ToExponential(double value, - int requested_digits, - StringBuilder* result_builder) const; - - // Computes 'precision' leading digits of the given 'value' and returns them - // either in exponential or decimal format, depending on - // max_{leading|trailing}_padding_zeroes_in_precision_mode (given to the - // constructor). - // The last computed digit is rounded. - // - // Example with max_leading_padding_zeroes_in_precision_mode = 6. - // ToPrecision(0.0000012345, 2) -> "0.0000012" - // ToPrecision(0.00000012345, 2) -> "1.2e-7" - // Similarily the converter may add up to - // max_trailing_padding_zeroes_in_precision_mode in precision mode to avoid - // returning an exponential representation. A zero added by the - // EMIT_TRAILING_ZERO_AFTER_POINT flag is counted for this limit. - // Examples for max_trailing_padding_zeroes_in_precision_mode = 1: - // ToPrecision(230.0, 2) -> "230" - // ToPrecision(230.0, 2) -> "230." with EMIT_TRAILING_DECIMAL_POINT. - // ToPrecision(230.0, 2) -> "2.3e2" with EMIT_TRAILING_ZERO_AFTER_POINT. - // Examples for max_trailing_padding_zeroes_in_precision_mode = 3, and no - // EMIT_TRAILING_ZERO_AFTER_POINT: - // ToPrecision(123450.0, 6) -> "123450" - // ToPrecision(123450.0, 5) -> "123450" - // ToPrecision(123450.0, 4) -> "123500" - // ToPrecision(123450.0, 3) -> "123000" - // ToPrecision(123450.0, 2) -> "1.2e5" - // - // Returns true if the conversion succeeds. The conversion always succeeds - // except for the following cases: - // - the input value is special and no infinity_symbol or nan_symbol has - // been provided to the constructor, - // - precision < kMinPericisionDigits - // - precision > kMaxPrecisionDigits - // The last condition implies that the result will never contain more than - // kMaxPrecisionDigits + 7 characters (the sign, the decimal point, the - // exponent character, the exponent's sign, and at most 3 exponent digits). - bool ToPrecision(double value, - int precision, - StringBuilder* result_builder) const; - - enum DtoaMode { - // Produce the shortest correct representation. - // For example the output of 0.299999999999999988897 is (the less accurate - // but correct) 0.3. - SHORTEST, - // Same as SHORTEST, but for single-precision floats. - SHORTEST_SINGLE, - // Produce a fixed number of digits after the decimal point. - // For instance fixed(0.1, 4) becomes 0.1000 - // If the input number is big, the output will be big. - FIXED, - // Fixed number of digits (independent of the decimal point). - PRECISION - }; - - // The maximal number of digits that are needed to emit a double in base 10. - // A higher precision can be achieved by using more digits, but the shortest - // accurate representation of any double will never use more digits than - // kBase10MaximalLength. - // Note that DoubleToAscii null-terminates its input. So the given buffer - // should be at least kBase10MaximalLength + 1 characters long. - static const int kBase10MaximalLength = 17; - - // Converts the given double 'v' to ascii. 'v' must not be NaN, +Infinity, or - // -Infinity. In SHORTEST_SINGLE-mode this restriction also applies to 'v' - // after it has been casted to a single-precision float. That is, in this - // mode static_cast(v) must not be NaN, +Infinity or -Infinity. - // - // The result should be interpreted as buffer * 10^(point-length). - // - // The output depends on the given mode: - // - SHORTEST: produce the least amount of digits for which the internal - // identity requirement is still satisfied. If the digits are printed - // (together with the correct exponent) then reading this number will give - // 'v' again. The buffer will choose the representation that is closest to - // 'v'. If there are two at the same distance, than the one farther away - // from 0 is chosen (halfway cases - ending with 5 - are rounded up). - // In this mode the 'requested_digits' parameter is ignored. - // - SHORTEST_SINGLE: same as SHORTEST but with single-precision. - // - FIXED: produces digits necessary to print a given number with - // 'requested_digits' digits after the decimal point. The produced digits - // might be too short in which case the caller has to fill the remainder - // with '0's. - // Example: toFixed(0.001, 5) is allowed to return buffer="1", point=-2. - // Halfway cases are rounded towards +/-Infinity (away from 0). The call - // toFixed(0.15, 2) thus returns buffer="2", point=0. - // The returned buffer may contain digits that would be truncated from the - // shortest representation of the input. - // - PRECISION: produces 'requested_digits' where the first digit is not '0'. - // Even though the length of produced digits usually equals - // 'requested_digits', the function is allowed to return fewer digits, in - // which case the caller has to fill the missing digits with '0's. - // Halfway cases are again rounded away from 0. - // DoubleToAscii expects the given buffer to be big enough to hold all - // digits and a terminating null-character. In SHORTEST-mode it expects a - // buffer of at least kBase10MaximalLength + 1. In all other modes the - // requested_digits parameter and the padding-zeroes limit the size of the - // output. Don't forget the decimal point, the exponent character and the - // terminating null-character when computing the maximal output size. - // The given length is only used in debug mode to ensure the buffer is big - // enough. - static void DoubleToAscii(double v, - DtoaMode mode, - int requested_digits, - char* buffer, - int buffer_length, - bool* sign, - int* length, - int* point); - - private: - // Implementation for ToShortest and ToShortestSingle. - bool ToShortestIeeeNumber(double value, - StringBuilder* result_builder, - DtoaMode mode) const; - - // If the value is a special value (NaN or Infinity) constructs the - // corresponding string using the configured infinity/nan-symbol. - // If either of them is NULL or the value is not special then the - // function returns false. - bool HandleSpecialValues(double value, StringBuilder* result_builder) const; - // Constructs an exponential representation (i.e. 1.234e56). - // The given exponent assumes a decimal point after the first decimal digit. - void CreateExponentialRepresentation(const char* decimal_digits, - int length, - int exponent, - StringBuilder* result_builder) const; - // Creates a decimal representation (i.e 1234.5678). - void CreateDecimalRepresentation(const char* decimal_digits, - int length, - int decimal_point, - int digits_after_point, - StringBuilder* result_builder) const; - - const int flags_; - const char* const infinity_symbol_; - const char* const nan_symbol_; - const char exponent_character_; - const int decimal_in_shortest_low_; - const int decimal_in_shortest_high_; - const int max_leading_padding_zeroes_in_precision_mode_; - const int max_trailing_padding_zeroes_in_precision_mode_; - - DISALLOW_IMPLICIT_CONSTRUCTORS(DoubleToStringConverter); -}; - - -class StringToDoubleConverter { - public: - // Enumeration for allowing octals and ignoring junk when converting - // strings to numbers. - enum Flags { - NO_FLAGS = 0, - ALLOW_HEX = 1, - ALLOW_OCTALS = 2, - ALLOW_TRAILING_JUNK = 4, - ALLOW_LEADING_SPACES = 8, - ALLOW_TRAILING_SPACES = 16, - ALLOW_SPACES_AFTER_SIGN = 32 - }; - - // Flags should be a bit-or combination of the possible Flags-enum. - // - NO_FLAGS: no special flags. - // - ALLOW_HEX: recognizes the prefix "0x". Hex numbers may only be integers. - // Ex: StringToDouble("0x1234") -> 4660.0 - // In StringToDouble("0x1234.56") the characters ".56" are trailing - // junk. The result of the call is hence dependent on - // the ALLOW_TRAILING_JUNK flag and/or the junk value. - // With this flag "0x" is a junk-string. Even with ALLOW_TRAILING_JUNK, - // the string will not be parsed as "0" followed by junk. - // - // - ALLOW_OCTALS: recognizes the prefix "0" for octals: - // If a sequence of octal digits starts with '0', then the number is - // read as octal integer. Octal numbers may only be integers. - // Ex: StringToDouble("01234") -> 668.0 - // StringToDouble("012349") -> 12349.0 // Not a sequence of octal - // // digits. - // In StringToDouble("01234.56") the characters ".56" are trailing - // junk. The result of the call is hence dependent on - // the ALLOW_TRAILING_JUNK flag and/or the junk value. - // In StringToDouble("01234e56") the characters "e56" are trailing - // junk, too. - // - ALLOW_TRAILING_JUNK: ignore trailing characters that are not part of - // a double literal. - // - ALLOW_LEADING_SPACES: skip over leading whitespace, including spaces, - // new-lines, and tabs. - // - ALLOW_TRAILING_SPACES: ignore trailing whitespace. - // - ALLOW_SPACES_AFTER_SIGN: ignore whitespace after the sign. - // Ex: StringToDouble("- 123.2") -> -123.2. - // StringToDouble("+ 123.2") -> 123.2 - // - // empty_string_value is returned when an empty string is given as input. - // If ALLOW_LEADING_SPACES or ALLOW_TRAILING_SPACES are set, then a string - // containing only spaces is converted to the 'empty_string_value', too. - // - // junk_string_value is returned when - // a) ALLOW_TRAILING_JUNK is not set, and a junk character (a character not - // part of a double-literal) is found. - // b) ALLOW_TRAILING_JUNK is set, but the string does not start with a - // double literal. - // - // infinity_symbol and nan_symbol are strings that are used to detect - // inputs that represent infinity and NaN. They can be null, in which case - // they are ignored. - // The conversion routine first reads any possible signs. Then it compares the - // following character of the input-string with the first character of - // the infinity, and nan-symbol. If either matches, the function assumes, that - // a match has been found, and expects the following input characters to match - // the remaining characters of the special-value symbol. - // This means that the following restrictions apply to special-value symbols: - // - they must not start with signs ('+', or '-'), - // - they must not have the same first character. - // - they must not start with digits. - // - // Examples: - // flags = ALLOW_HEX | ALLOW_TRAILING_JUNK, - // empty_string_value = 0.0, - // junk_string_value = NaN, - // infinity_symbol = "infinity", - // nan_symbol = "nan": - // StringToDouble("0x1234") -> 4660.0. - // StringToDouble("0x1234K") -> 4660.0. - // StringToDouble("") -> 0.0 // empty_string_value. - // StringToDouble(" ") -> NaN // junk_string_value. - // StringToDouble(" 1") -> NaN // junk_string_value. - // StringToDouble("0x") -> NaN // junk_string_value. - // StringToDouble("-123.45") -> -123.45. - // StringToDouble("--123.45") -> NaN // junk_string_value. - // StringToDouble("123e45") -> 123e45. - // StringToDouble("123E45") -> 123e45. - // StringToDouble("123e+45") -> 123e45. - // StringToDouble("123E-45") -> 123e-45. - // StringToDouble("123e") -> 123.0 // trailing junk ignored. - // StringToDouble("123e-") -> 123.0 // trailing junk ignored. - // StringToDouble("+NaN") -> NaN // NaN string literal. - // StringToDouble("-infinity") -> -inf. // infinity literal. - // StringToDouble("Infinity") -> NaN // junk_string_value. - // - // flags = ALLOW_OCTAL | ALLOW_LEADING_SPACES, - // empty_string_value = 0.0, - // junk_string_value = NaN, - // infinity_symbol = NULL, - // nan_symbol = NULL: - // StringToDouble("0x1234") -> NaN // junk_string_value. - // StringToDouble("01234") -> 668.0. - // StringToDouble("") -> 0.0 // empty_string_value. - // StringToDouble(" ") -> 0.0 // empty_string_value. - // StringToDouble(" 1") -> 1.0 - // StringToDouble("0x") -> NaN // junk_string_value. - // StringToDouble("0123e45") -> NaN // junk_string_value. - // StringToDouble("01239E45") -> 1239e45. - // StringToDouble("-infinity") -> NaN // junk_string_value. - // StringToDouble("NaN") -> NaN // junk_string_value. - StringToDoubleConverter(int flags, - double empty_string_value, - double junk_string_value, - const char* infinity_symbol, - const char* nan_symbol) - : flags_(flags), - empty_string_value_(empty_string_value), - junk_string_value_(junk_string_value), - infinity_symbol_(infinity_symbol), - nan_symbol_(nan_symbol) { - } - - // Performs the conversion. - // The output parameter 'processed_characters_count' is set to the number - // of characters that have been processed to read the number. - // Spaces than are processed with ALLOW_{LEADING|TRAILING}_SPACES are included - // in the 'processed_characters_count'. Trailing junk is never included. - double StringToDouble(const char* buffer, - int length, - int* processed_characters_count) const; - - // Same as StringToDouble above but for 16 bit characters. - double StringToDouble(const uc16* buffer, - int length, - int* processed_characters_count) const; - - // Same as StringToDouble but reads a float. - // Note that this is not equivalent to static_cast(StringToDouble(...)) - // due to potential double-rounding. - float StringToFloat(const char* buffer, - int length, - int* processed_characters_count) const; - - // Same as StringToFloat above but for 16 bit characters. - float StringToFloat(const uc16* buffer, - int length, - int* processed_characters_count) const; - - private: - const int flags_; - const double empty_string_value_; - const double junk_string_value_; - const char* const infinity_symbol_; - const char* const nan_symbol_; - - template - double StringToIeee(Iterator start_pointer, - int length, - bool read_as_double, - int* processed_characters_count) const; - - DISALLOW_IMPLICIT_CONSTRUCTORS(StringToDoubleConverter); -}; - -} // namespace double_conversion +#include "string-to-double.h" +#include "double-to-string.h" #endif // DOUBLE_CONVERSION_DOUBLE_CONVERSION_H_ diff --git a/double-conversion/double-to-string.cc b/double-conversion/double-to-string.cc new file mode 100644 index 00000000..4562f99f --- /dev/null +++ b/double-conversion/double-to-string.cc @@ -0,0 +1,428 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include +#include + +#include "double-to-string.h" + +#include "bignum-dtoa.h" +#include "fast-dtoa.h" +#include "fixed-dtoa.h" +#include "ieee.h" +#include "utils.h" + +namespace double_conversion { + +const DoubleToStringConverter& DoubleToStringConverter::EcmaScriptConverter() { + int flags = UNIQUE_ZERO | EMIT_POSITIVE_EXPONENT_SIGN; + static DoubleToStringConverter converter(flags, + "Infinity", + "NaN", + 'e', + -6, 21, + 6, 0); + return converter; +} + + +bool DoubleToStringConverter::HandleSpecialValues( + double value, + StringBuilder* result_builder) const { + Double double_inspect(value); + if (double_inspect.IsInfinite()) { + if (infinity_symbol_ == NULL) return false; + if (value < 0) { + result_builder->AddCharacter('-'); + } + result_builder->AddString(infinity_symbol_); + return true; + } + if (double_inspect.IsNan()) { + if (nan_symbol_ == NULL) return false; + result_builder->AddString(nan_symbol_); + return true; + } + return false; +} + + +void DoubleToStringConverter::CreateExponentialRepresentation( + const char* decimal_digits, + int length, + int exponent, + StringBuilder* result_builder) const { + DOUBLE_CONVERSION_ASSERT(length != 0); + result_builder->AddCharacter(decimal_digits[0]); + if (length != 1) { + result_builder->AddCharacter('.'); + result_builder->AddSubstring(&decimal_digits[1], length-1); + } + result_builder->AddCharacter(exponent_character_); + if (exponent < 0) { + result_builder->AddCharacter('-'); + exponent = -exponent; + } else { + if ((flags_ & EMIT_POSITIVE_EXPONENT_SIGN) != 0) { + result_builder->AddCharacter('+'); + } + } + if (exponent == 0) { + result_builder->AddCharacter('0'); + return; + } + DOUBLE_CONVERSION_ASSERT(exponent < 1e4); + // Changing this constant requires updating the comment of DoubleToStringConverter constructor + const int kMaxExponentLength = 5; + char buffer[kMaxExponentLength + 1]; + buffer[kMaxExponentLength] = '\0'; + int first_char_pos = kMaxExponentLength; + while (exponent > 0) { + buffer[--first_char_pos] = '0' + (exponent % 10); + exponent /= 10; + } + // Add prefix '0' to make exponent width >= min(min_exponent_with_, kMaxExponentLength) + // For example: convert 1e+9 -> 1e+09, if min_exponent_with_ is set to 2 + while(kMaxExponentLength - first_char_pos < std::min(min_exponent_width_, kMaxExponentLength)) { + buffer[--first_char_pos] = '0'; + } + result_builder->AddSubstring(&buffer[first_char_pos], + kMaxExponentLength - first_char_pos); +} + + +void DoubleToStringConverter::CreateDecimalRepresentation( + const char* decimal_digits, + int length, + int decimal_point, + int digits_after_point, + StringBuilder* result_builder) const { + // Create a representation that is padded with zeros if needed. + if (decimal_point <= 0) { + // "0.00000decimal_rep" or "0.000decimal_rep00". + result_builder->AddCharacter('0'); + if (digits_after_point > 0) { + result_builder->AddCharacter('.'); + result_builder->AddPadding('0', -decimal_point); + DOUBLE_CONVERSION_ASSERT(length <= digits_after_point - (-decimal_point)); + result_builder->AddSubstring(decimal_digits, length); + int remaining_digits = digits_after_point - (-decimal_point) - length; + result_builder->AddPadding('0', remaining_digits); + } + } else if (decimal_point >= length) { + // "decimal_rep0000.00000" or "decimal_rep.0000". + result_builder->AddSubstring(decimal_digits, length); + result_builder->AddPadding('0', decimal_point - length); + if (digits_after_point > 0) { + result_builder->AddCharacter('.'); + result_builder->AddPadding('0', digits_after_point); + } + } else { + // "decima.l_rep000". + DOUBLE_CONVERSION_ASSERT(digits_after_point > 0); + result_builder->AddSubstring(decimal_digits, decimal_point); + result_builder->AddCharacter('.'); + DOUBLE_CONVERSION_ASSERT(length - decimal_point <= digits_after_point); + result_builder->AddSubstring(&decimal_digits[decimal_point], + length - decimal_point); + int remaining_digits = digits_after_point - (length - decimal_point); + result_builder->AddPadding('0', remaining_digits); + } + if (digits_after_point == 0) { + if ((flags_ & EMIT_TRAILING_DECIMAL_POINT) != 0) { + result_builder->AddCharacter('.'); + } + if ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) { + result_builder->AddCharacter('0'); + } + } +} + + +bool DoubleToStringConverter::ToShortestIeeeNumber( + double value, + StringBuilder* result_builder, + DoubleToStringConverter::DtoaMode mode) const { + DOUBLE_CONVERSION_ASSERT(mode == SHORTEST || mode == SHORTEST_SINGLE); + if (Double(value).IsSpecial()) { + return HandleSpecialValues(value, result_builder); + } + + int decimal_point; + bool sign; + const int kDecimalRepCapacity = kBase10MaximalLength + 1; + char decimal_rep[kDecimalRepCapacity]; + int decimal_rep_length; + + DoubleToAscii(value, mode, 0, decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + + bool unique_zero = (flags_ & UNIQUE_ZERO) != 0; + if (sign && (value != 0.0 || !unique_zero)) { + result_builder->AddCharacter('-'); + } + + int exponent = decimal_point - 1; + if ((decimal_in_shortest_low_ <= exponent) && + (exponent < decimal_in_shortest_high_)) { + CreateDecimalRepresentation(decimal_rep, decimal_rep_length, + decimal_point, + (std::max)(0, decimal_rep_length - decimal_point), + result_builder); + } else { + CreateExponentialRepresentation(decimal_rep, decimal_rep_length, exponent, + result_builder); + } + return true; +} + + +bool DoubleToStringConverter::ToFixed(double value, + int requested_digits, + StringBuilder* result_builder) const { + DOUBLE_CONVERSION_ASSERT(kMaxFixedDigitsBeforePoint == 60); + const double kFirstNonFixed = 1e60; + + if (Double(value).IsSpecial()) { + return HandleSpecialValues(value, result_builder); + } + + if (requested_digits > kMaxFixedDigitsAfterPoint) return false; + if (value >= kFirstNonFixed || value <= -kFirstNonFixed) return false; + + // Find a sufficiently precise decimal representation of n. + int decimal_point; + bool sign; + // Add space for the '\0' byte. + const int kDecimalRepCapacity = + kMaxFixedDigitsBeforePoint + kMaxFixedDigitsAfterPoint + 1; + char decimal_rep[kDecimalRepCapacity]; + int decimal_rep_length; + DoubleToAscii(value, FIXED, requested_digits, + decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + + bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); + if (sign && (value != 0.0 || !unique_zero)) { + result_builder->AddCharacter('-'); + } + + CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point, + requested_digits, result_builder); + return true; +} + + +bool DoubleToStringConverter::ToExponential( + double value, + int requested_digits, + StringBuilder* result_builder) const { + if (Double(value).IsSpecial()) { + return HandleSpecialValues(value, result_builder); + } + + if (requested_digits < -1) return false; + if (requested_digits > kMaxExponentialDigits) return false; + + int decimal_point; + bool sign; + // Add space for digit before the decimal point and the '\0' character. + const int kDecimalRepCapacity = kMaxExponentialDigits + 2; + DOUBLE_CONVERSION_ASSERT(kDecimalRepCapacity > kBase10MaximalLength); + char decimal_rep[kDecimalRepCapacity]; +#ifndef NDEBUG + // Problem: there is an assert in StringBuilder::AddSubstring() that + // will pass this buffer to strlen(), and this buffer is not generally + // null-terminated. + memset(decimal_rep, 0, sizeof(decimal_rep)); +#endif + int decimal_rep_length; + + if (requested_digits == -1) { + DoubleToAscii(value, SHORTEST, 0, + decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + } else { + DoubleToAscii(value, PRECISION, requested_digits + 1, + decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + DOUBLE_CONVERSION_ASSERT(decimal_rep_length <= requested_digits + 1); + + for (int i = decimal_rep_length; i < requested_digits + 1; ++i) { + decimal_rep[i] = '0'; + } + decimal_rep_length = requested_digits + 1; + } + + bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); + if (sign && (value != 0.0 || !unique_zero)) { + result_builder->AddCharacter('-'); + } + + int exponent = decimal_point - 1; + CreateExponentialRepresentation(decimal_rep, + decimal_rep_length, + exponent, + result_builder); + return true; +} + + +bool DoubleToStringConverter::ToPrecision(double value, + int precision, + StringBuilder* result_builder) const { + if (Double(value).IsSpecial()) { + return HandleSpecialValues(value, result_builder); + } + + if (precision < kMinPrecisionDigits || precision > kMaxPrecisionDigits) { + return false; + } + + // Find a sufficiently precise decimal representation of n. + int decimal_point; + bool sign; + // Add one for the terminating null character. + const int kDecimalRepCapacity = kMaxPrecisionDigits + 1; + char decimal_rep[kDecimalRepCapacity]; + int decimal_rep_length; + + DoubleToAscii(value, PRECISION, precision, + decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + DOUBLE_CONVERSION_ASSERT(decimal_rep_length <= precision); + + bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); + if (sign && (value != 0.0 || !unique_zero)) { + result_builder->AddCharacter('-'); + } + + // The exponent if we print the number as x.xxeyyy. That is with the + // decimal point after the first digit. + int exponent = decimal_point - 1; + + int extra_zero = ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) ? 1 : 0; + if ((-decimal_point + 1 > max_leading_padding_zeroes_in_precision_mode_) || + (decimal_point - precision + extra_zero > + max_trailing_padding_zeroes_in_precision_mode_)) { + // Fill buffer to contain 'precision' digits. + // Usually the buffer is already at the correct length, but 'DoubleToAscii' + // is allowed to return less characters. + for (int i = decimal_rep_length; i < precision; ++i) { + decimal_rep[i] = '0'; + } + + CreateExponentialRepresentation(decimal_rep, + precision, + exponent, + result_builder); + } else { + CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point, + (std::max)(0, precision - decimal_point), + result_builder); + } + return true; +} + + +static BignumDtoaMode DtoaToBignumDtoaMode( + DoubleToStringConverter::DtoaMode dtoa_mode) { + switch (dtoa_mode) { + case DoubleToStringConverter::SHORTEST: return BIGNUM_DTOA_SHORTEST; + case DoubleToStringConverter::SHORTEST_SINGLE: + return BIGNUM_DTOA_SHORTEST_SINGLE; + case DoubleToStringConverter::FIXED: return BIGNUM_DTOA_FIXED; + case DoubleToStringConverter::PRECISION: return BIGNUM_DTOA_PRECISION; + default: + DOUBLE_CONVERSION_UNREACHABLE(); + } +} + + +void DoubleToStringConverter::DoubleToAscii(double v, + DtoaMode mode, + int requested_digits, + char* buffer, + int buffer_length, + bool* sign, + int* length, + int* point) { + Vector vector(buffer, buffer_length); + DOUBLE_CONVERSION_ASSERT(!Double(v).IsSpecial()); + DOUBLE_CONVERSION_ASSERT(mode == SHORTEST || mode == SHORTEST_SINGLE || requested_digits >= 0); + + if (Double(v).Sign() < 0) { + *sign = true; + v = -v; + } else { + *sign = false; + } + + if (mode == PRECISION && requested_digits == 0) { + vector[0] = '\0'; + *length = 0; + return; + } + + if (v == 0) { + vector[0] = '0'; + vector[1] = '\0'; + *length = 1; + *point = 1; + return; + } + + bool fast_worked; + switch (mode) { + case SHORTEST: + fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST, 0, vector, length, point); + break; + case SHORTEST_SINGLE: + fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST_SINGLE, 0, + vector, length, point); + break; + case FIXED: + fast_worked = FastFixedDtoa(v, requested_digits, vector, length, point); + break; + case PRECISION: + fast_worked = FastDtoa(v, FAST_DTOA_PRECISION, requested_digits, + vector, length, point); + break; + default: + fast_worked = false; + DOUBLE_CONVERSION_UNREACHABLE(); + } + if (fast_worked) return; + + // If the fast dtoa didn't succeed use the slower bignum version. + BignumDtoaMode bignum_mode = DtoaToBignumDtoaMode(mode); + BignumDtoa(v, bignum_mode, requested_digits, vector, length, point); + vector[*length] = '\0'; +} + +} // namespace double_conversion diff --git a/double-conversion/double-to-string.h b/double-conversion/double-to-string.h new file mode 100644 index 00000000..a44fa3c7 --- /dev/null +++ b/double-conversion/double-to-string.h @@ -0,0 +1,396 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_DOUBLE_TO_STRING_H_ +#define DOUBLE_CONVERSION_DOUBLE_TO_STRING_H_ + +#include "utils.h" + +namespace double_conversion { + +class DoubleToStringConverter { + public: + // When calling ToFixed with a double > 10^kMaxFixedDigitsBeforePoint + // or a requested_digits parameter > kMaxFixedDigitsAfterPoint then the + // function returns false. + static const int kMaxFixedDigitsBeforePoint = 60; + static const int kMaxFixedDigitsAfterPoint = 60; + + // When calling ToExponential with a requested_digits + // parameter > kMaxExponentialDigits then the function returns false. + static const int kMaxExponentialDigits = 120; + + // When calling ToPrecision with a requested_digits + // parameter < kMinPrecisionDigits or requested_digits > kMaxPrecisionDigits + // then the function returns false. + static const int kMinPrecisionDigits = 1; + static const int kMaxPrecisionDigits = 120; + + enum Flags { + NO_FLAGS = 0, + EMIT_POSITIVE_EXPONENT_SIGN = 1, + EMIT_TRAILING_DECIMAL_POINT = 2, + EMIT_TRAILING_ZERO_AFTER_POINT = 4, + UNIQUE_ZERO = 8 + }; + + // Flags should be a bit-or combination of the possible Flags-enum. + // - NO_FLAGS: no special flags. + // - EMIT_POSITIVE_EXPONENT_SIGN: when the number is converted into exponent + // form, emits a '+' for positive exponents. Example: 1.2e+2. + // - EMIT_TRAILING_DECIMAL_POINT: when the input number is an integer and is + // converted into decimal format then a trailing decimal point is appended. + // Example: 2345.0 is converted to "2345.". + // - EMIT_TRAILING_ZERO_AFTER_POINT: in addition to a trailing decimal point + // emits a trailing '0'-character. This flag requires the + // EXMIT_TRAILING_DECIMAL_POINT flag. + // Example: 2345.0 is converted to "2345.0". + // - UNIQUE_ZERO: "-0.0" is converted to "0.0". + // + // Infinity symbol and nan_symbol provide the string representation for these + // special values. If the string is NULL and the special value is encountered + // then the conversion functions return false. + // + // The exponent_character is used in exponential representations. It is + // usually 'e' or 'E'. + // + // When converting to the shortest representation the converter will + // represent input numbers in decimal format if they are in the interval + // [10^decimal_in_shortest_low; 10^decimal_in_shortest_high[ + // (lower boundary included, greater boundary excluded). + // Example: with decimal_in_shortest_low = -6 and + // decimal_in_shortest_high = 21: + // ToShortest(0.000001) -> "0.000001" + // ToShortest(0.0000001) -> "1e-7" + // ToShortest(111111111111111111111.0) -> "111111111111111110000" + // ToShortest(100000000000000000000.0) -> "100000000000000000000" + // ToShortest(1111111111111111111111.0) -> "1.1111111111111111e+21" + // + // When converting to precision mode the converter may add + // max_leading_padding_zeroes before returning the number in exponential + // format. + // Example with max_leading_padding_zeroes_in_precision_mode = 6. + // ToPrecision(0.0000012345, 2) -> "0.0000012" + // ToPrecision(0.00000012345, 2) -> "1.2e-7" + // Similarily the converter may add up to + // max_trailing_padding_zeroes_in_precision_mode in precision mode to avoid + // returning an exponential representation. A zero added by the + // EMIT_TRAILING_ZERO_AFTER_POINT flag is counted for this limit. + // Examples for max_trailing_padding_zeroes_in_precision_mode = 1: + // ToPrecision(230.0, 2) -> "230" + // ToPrecision(230.0, 2) -> "230." with EMIT_TRAILING_DECIMAL_POINT. + // ToPrecision(230.0, 2) -> "2.3e2" with EMIT_TRAILING_ZERO_AFTER_POINT. + // + // The min_exponent_width is used for exponential representations. + // The converter adds leading '0's to the exponent until the exponent + // is at least min_exponent_width digits long. + // The min_exponent_width is clamped to 5. + // As such, the exponent may never have more than 5 digits in total. + DoubleToStringConverter(int flags, + const char* infinity_symbol, + const char* nan_symbol, + char exponent_character, + int decimal_in_shortest_low, + int decimal_in_shortest_high, + int max_leading_padding_zeroes_in_precision_mode, + int max_trailing_padding_zeroes_in_precision_mode, + int min_exponent_width = 0) + : flags_(flags), + infinity_symbol_(infinity_symbol), + nan_symbol_(nan_symbol), + exponent_character_(exponent_character), + decimal_in_shortest_low_(decimal_in_shortest_low), + decimal_in_shortest_high_(decimal_in_shortest_high), + max_leading_padding_zeroes_in_precision_mode_( + max_leading_padding_zeroes_in_precision_mode), + max_trailing_padding_zeroes_in_precision_mode_( + max_trailing_padding_zeroes_in_precision_mode), + min_exponent_width_(min_exponent_width) { + // When 'trailing zero after the point' is set, then 'trailing point' + // must be set too. + DOUBLE_CONVERSION_ASSERT(((flags & EMIT_TRAILING_DECIMAL_POINT) != 0) || + !((flags & EMIT_TRAILING_ZERO_AFTER_POINT) != 0)); + } + + // Returns a converter following the EcmaScript specification. + static const DoubleToStringConverter& EcmaScriptConverter(); + + // Computes the shortest string of digits that correctly represent the input + // number. Depending on decimal_in_shortest_low and decimal_in_shortest_high + // (see constructor) it then either returns a decimal representation, or an + // exponential representation. + // Example with decimal_in_shortest_low = -6, + // decimal_in_shortest_high = 21, + // EMIT_POSITIVE_EXPONENT_SIGN activated, and + // EMIT_TRAILING_DECIMAL_POINT deactived: + // ToShortest(0.000001) -> "0.000001" + // ToShortest(0.0000001) -> "1e-7" + // ToShortest(111111111111111111111.0) -> "111111111111111110000" + // ToShortest(100000000000000000000.0) -> "100000000000000000000" + // ToShortest(1111111111111111111111.0) -> "1.1111111111111111e+21" + // + // Note: the conversion may round the output if the returned string + // is accurate enough to uniquely identify the input-number. + // For example the most precise representation of the double 9e59 equals + // "899999999999999918767229449717619953810131273674690656206848", but + // the converter will return the shorter (but still correct) "9e59". + // + // Returns true if the conversion succeeds. The conversion always succeeds + // except when the input value is special and no infinity_symbol or + // nan_symbol has been given to the constructor. + bool ToShortest(double value, StringBuilder* result_builder) const { + return ToShortestIeeeNumber(value, result_builder, SHORTEST); + } + + // Same as ToShortest, but for single-precision floats. + bool ToShortestSingle(float value, StringBuilder* result_builder) const { + return ToShortestIeeeNumber(value, result_builder, SHORTEST_SINGLE); + } + + + // Computes a decimal representation with a fixed number of digits after the + // decimal point. The last emitted digit is rounded. + // + // Examples: + // ToFixed(3.12, 1) -> "3.1" + // ToFixed(3.1415, 3) -> "3.142" + // ToFixed(1234.56789, 4) -> "1234.5679" + // ToFixed(1.23, 5) -> "1.23000" + // ToFixed(0.1, 4) -> "0.1000" + // ToFixed(1e30, 2) -> "1000000000000000019884624838656.00" + // ToFixed(0.1, 30) -> "0.100000000000000005551115123126" + // ToFixed(0.1, 17) -> "0.10000000000000001" + // + // If requested_digits equals 0, then the tail of the result depends on + // the EMIT_TRAILING_DECIMAL_POINT and EMIT_TRAILING_ZERO_AFTER_POINT. + // Examples, for requested_digits == 0, + // let EMIT_TRAILING_DECIMAL_POINT and EMIT_TRAILING_ZERO_AFTER_POINT be + // - false and false: then 123.45 -> 123 + // 0.678 -> 1 + // - true and false: then 123.45 -> 123. + // 0.678 -> 1. + // - true and true: then 123.45 -> 123.0 + // 0.678 -> 1.0 + // + // Returns true if the conversion succeeds. The conversion always succeeds + // except for the following cases: + // - the input value is special and no infinity_symbol or nan_symbol has + // been provided to the constructor, + // - 'value' > 10^kMaxFixedDigitsBeforePoint, or + // - 'requested_digits' > kMaxFixedDigitsAfterPoint. + // The last two conditions imply that the result will never contain more than + // 1 + kMaxFixedDigitsBeforePoint + 1 + kMaxFixedDigitsAfterPoint characters + // (one additional character for the sign, and one for the decimal point). + bool ToFixed(double value, + int requested_digits, + StringBuilder* result_builder) const; + + // Computes a representation in exponential format with requested_digits + // after the decimal point. The last emitted digit is rounded. + // If requested_digits equals -1, then the shortest exponential representation + // is computed. + // + // Examples with EMIT_POSITIVE_EXPONENT_SIGN deactivated, and + // exponent_character set to 'e'. + // ToExponential(3.12, 1) -> "3.1e0" + // ToExponential(5.0, 3) -> "5.000e0" + // ToExponential(0.001, 2) -> "1.00e-3" + // ToExponential(3.1415, -1) -> "3.1415e0" + // ToExponential(3.1415, 4) -> "3.1415e0" + // ToExponential(3.1415, 3) -> "3.142e0" + // ToExponential(123456789000000, 3) -> "1.235e14" + // ToExponential(1000000000000000019884624838656.0, -1) -> "1e30" + // ToExponential(1000000000000000019884624838656.0, 32) -> + // "1.00000000000000001988462483865600e30" + // ToExponential(1234, 0) -> "1e3" + // + // Returns true if the conversion succeeds. The conversion always succeeds + // except for the following cases: + // - the input value is special and no infinity_symbol or nan_symbol has + // been provided to the constructor, + // - 'requested_digits' > kMaxExponentialDigits. + // The last condition implies that the result will never contain more than + // kMaxExponentialDigits + 8 characters (the sign, the digit before the + // decimal point, the decimal point, the exponent character, the + // exponent's sign, and at most 3 exponent digits). + bool ToExponential(double value, + int requested_digits, + StringBuilder* result_builder) const; + + // Computes 'precision' leading digits of the given 'value' and returns them + // either in exponential or decimal format, depending on + // max_{leading|trailing}_padding_zeroes_in_precision_mode (given to the + // constructor). + // The last computed digit is rounded. + // + // Example with max_leading_padding_zeroes_in_precision_mode = 6. + // ToPrecision(0.0000012345, 2) -> "0.0000012" + // ToPrecision(0.00000012345, 2) -> "1.2e-7" + // Similarily the converter may add up to + // max_trailing_padding_zeroes_in_precision_mode in precision mode to avoid + // returning an exponential representation. A zero added by the + // EMIT_TRAILING_ZERO_AFTER_POINT flag is counted for this limit. + // Examples for max_trailing_padding_zeroes_in_precision_mode = 1: + // ToPrecision(230.0, 2) -> "230" + // ToPrecision(230.0, 2) -> "230." with EMIT_TRAILING_DECIMAL_POINT. + // ToPrecision(230.0, 2) -> "2.3e2" with EMIT_TRAILING_ZERO_AFTER_POINT. + // Examples for max_trailing_padding_zeroes_in_precision_mode = 3, and no + // EMIT_TRAILING_ZERO_AFTER_POINT: + // ToPrecision(123450.0, 6) -> "123450" + // ToPrecision(123450.0, 5) -> "123450" + // ToPrecision(123450.0, 4) -> "123500" + // ToPrecision(123450.0, 3) -> "123000" + // ToPrecision(123450.0, 2) -> "1.2e5" + // + // Returns true if the conversion succeeds. The conversion always succeeds + // except for the following cases: + // - the input value is special and no infinity_symbol or nan_symbol has + // been provided to the constructor, + // - precision < kMinPericisionDigits + // - precision > kMaxPrecisionDigits + // The last condition implies that the result will never contain more than + // kMaxPrecisionDigits + 7 characters (the sign, the decimal point, the + // exponent character, the exponent's sign, and at most 3 exponent digits). + bool ToPrecision(double value, + int precision, + StringBuilder* result_builder) const; + + enum DtoaMode { + // Produce the shortest correct representation. + // For example the output of 0.299999999999999988897 is (the less accurate + // but correct) 0.3. + SHORTEST, + // Same as SHORTEST, but for single-precision floats. + SHORTEST_SINGLE, + // Produce a fixed number of digits after the decimal point. + // For instance fixed(0.1, 4) becomes 0.1000 + // If the input number is big, the output will be big. + FIXED, + // Fixed number of digits (independent of the decimal point). + PRECISION + }; + + // The maximal number of digits that are needed to emit a double in base 10. + // A higher precision can be achieved by using more digits, but the shortest + // accurate representation of any double will never use more digits than + // kBase10MaximalLength. + // Note that DoubleToAscii null-terminates its input. So the given buffer + // should be at least kBase10MaximalLength + 1 characters long. + static const int kBase10MaximalLength = 17; + + // Converts the given double 'v' to digit characters. 'v' must not be NaN, + // +Infinity, or -Infinity. In SHORTEST_SINGLE-mode this restriction also + // applies to 'v' after it has been casted to a single-precision float. That + // is, in this mode static_cast(v) must not be NaN, +Infinity or + // -Infinity. + // + // The result should be interpreted as buffer * 10^(point-length). + // + // The digits are written to the buffer in the platform's charset, which is + // often UTF-8 (with ASCII-range digits) but may be another charset, such + // as EBCDIC. + // + // The output depends on the given mode: + // - SHORTEST: produce the least amount of digits for which the internal + // identity requirement is still satisfied. If the digits are printed + // (together with the correct exponent) then reading this number will give + // 'v' again. The buffer will choose the representation that is closest to + // 'v'. If there are two at the same distance, than the one farther away + // from 0 is chosen (halfway cases - ending with 5 - are rounded up). + // In this mode the 'requested_digits' parameter is ignored. + // - SHORTEST_SINGLE: same as SHORTEST but with single-precision. + // - FIXED: produces digits necessary to print a given number with + // 'requested_digits' digits after the decimal point. The produced digits + // might be too short in which case the caller has to fill the remainder + // with '0's. + // Example: toFixed(0.001, 5) is allowed to return buffer="1", point=-2. + // Halfway cases are rounded towards +/-Infinity (away from 0). The call + // toFixed(0.15, 2) thus returns buffer="2", point=0. + // The returned buffer may contain digits that would be truncated from the + // shortest representation of the input. + // - PRECISION: produces 'requested_digits' where the first digit is not '0'. + // Even though the length of produced digits usually equals + // 'requested_digits', the function is allowed to return fewer digits, in + // which case the caller has to fill the missing digits with '0's. + // Halfway cases are again rounded away from 0. + // DoubleToAscii expects the given buffer to be big enough to hold all + // digits and a terminating null-character. In SHORTEST-mode it expects a + // buffer of at least kBase10MaximalLength + 1. In all other modes the + // requested_digits parameter and the padding-zeroes limit the size of the + // output. Don't forget the decimal point, the exponent character and the + // terminating null-character when computing the maximal output size. + // The given length is only used in debug mode to ensure the buffer is big + // enough. + static void DoubleToAscii(double v, + DtoaMode mode, + int requested_digits, + char* buffer, + int buffer_length, + bool* sign, + int* length, + int* point); + + private: + // Implementation for ToShortest and ToShortestSingle. + bool ToShortestIeeeNumber(double value, + StringBuilder* result_builder, + DtoaMode mode) const; + + // If the value is a special value (NaN or Infinity) constructs the + // corresponding string using the configured infinity/nan-symbol. + // If either of them is NULL or the value is not special then the + // function returns false. + bool HandleSpecialValues(double value, StringBuilder* result_builder) const; + // Constructs an exponential representation (i.e. 1.234e56). + // The given exponent assumes a decimal point after the first decimal digit. + void CreateExponentialRepresentation(const char* decimal_digits, + int length, + int exponent, + StringBuilder* result_builder) const; + // Creates a decimal representation (i.e 1234.5678). + void CreateDecimalRepresentation(const char* decimal_digits, + int length, + int decimal_point, + int digits_after_point, + StringBuilder* result_builder) const; + + const int flags_; + const char* const infinity_symbol_; + const char* const nan_symbol_; + const char exponent_character_; + const int decimal_in_shortest_low_; + const int decimal_in_shortest_high_; + const int max_leading_padding_zeroes_in_precision_mode_; + const int max_trailing_padding_zeroes_in_precision_mode_; + const int min_exponent_width_; + + DOUBLE_CONVERSION_DISALLOW_IMPLICIT_CONSTRUCTORS(DoubleToStringConverter); +}; + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_DOUBLE_TO_STRING_H_ diff --git a/double-conversion/fast-dtoa.cc b/double-conversion/fast-dtoa.cc index 61350383..f4702864 100644 --- a/double-conversion/fast-dtoa.cc +++ b/double-conversion/fast-dtoa.cc @@ -138,7 +138,7 @@ static bool RoundWeed(Vector buffer, // Conceptually rest ~= too_high - buffer // We need to do the following tests in this order to avoid over- and // underflows. - ASSERT(rest <= unsafe_interval); + DOUBLE_CONVERSION_ASSERT(rest <= unsafe_interval); while (rest < small_distance && // Negated condition 1 unsafe_interval - rest >= ten_kappa && // Negated condition 2 (rest + ten_kappa < small_distance || // buffer{-1} > w_high @@ -184,7 +184,7 @@ static bool RoundWeedCounted(Vector buffer, uint64_t ten_kappa, uint64_t unit, int* kappa) { - ASSERT(rest < ten_kappa); + DOUBLE_CONVERSION_ASSERT(rest < ten_kappa); // The following tests are done in a specific order to avoid overflows. They // will work correctly with any uint64 values of rest < ten_kappa and unit. // @@ -241,7 +241,7 @@ static void BiggestPowerTen(uint32_t number, int number_bits, uint32_t* power, int* exponent_plus_one) { - ASSERT(number < (1u << (number_bits + 1))); + DOUBLE_CONVERSION_ASSERT(number < (1u << (number_bits + 1))); // 1233/4096 is approximately 1/lg(10). int exponent_plus_one_guess = ((number_bits + 1) * 1233 >> 12); // We increment to skip over the first entry in the kPowersOf10 table. @@ -303,9 +303,9 @@ static bool DigitGen(DiyFp low, Vector buffer, int* length, int* kappa) { - ASSERT(low.e() == w.e() && w.e() == high.e()); - ASSERT(low.f() + 1 <= high.f() - 1); - ASSERT(kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent); + DOUBLE_CONVERSION_ASSERT(low.e() == w.e() && w.e() == high.e()); + DOUBLE_CONVERSION_ASSERT(low.f() + 1 <= high.f() - 1); + DOUBLE_CONVERSION_ASSERT(kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent); // low, w and high are imprecise, but by less than one ulp (unit in the last // place). // If we remove (resp. add) 1 ulp from low (resp. high) we are certain that @@ -347,7 +347,7 @@ static bool DigitGen(DiyFp low, // that is smaller than integrals. while (*kappa > 0) { int digit = integrals / divisor; - ASSERT(digit <= 9); + DOUBLE_CONVERSION_ASSERT(digit <= 9); buffer[*length] = static_cast('0' + digit); (*length)++; integrals %= divisor; @@ -374,16 +374,16 @@ static bool DigitGen(DiyFp low, // data (like the interval or 'unit'), too. // Note that the multiplication by 10 does not overflow, because w.e >= -60 // and thus one.e >= -60. - ASSERT(one.e() >= -60); - ASSERT(fractionals < one.f()); - ASSERT(UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF) / 10 >= one.f()); + DOUBLE_CONVERSION_ASSERT(one.e() >= -60); + DOUBLE_CONVERSION_ASSERT(fractionals < one.f()); + DOUBLE_CONVERSION_ASSERT(DOUBLE_CONVERSION_UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF) / 10 >= one.f()); for (;;) { fractionals *= 10; unit *= 10; unsafe_interval.set_f(unsafe_interval.f() * 10); // Integer division by one. int digit = static_cast(fractionals >> -one.e()); - ASSERT(digit <= 9); + DOUBLE_CONVERSION_ASSERT(digit <= 9); buffer[*length] = static_cast('0' + digit); (*length)++; fractionals &= one.f() - 1; // Modulo by one. @@ -430,9 +430,9 @@ static bool DigitGenCounted(DiyFp w, Vector buffer, int* length, int* kappa) { - ASSERT(kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent); - ASSERT(kMinimalTargetExponent >= -60); - ASSERT(kMaximalTargetExponent <= -32); + DOUBLE_CONVERSION_ASSERT(kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent); + DOUBLE_CONVERSION_ASSERT(kMinimalTargetExponent >= -60); + DOUBLE_CONVERSION_ASSERT(kMaximalTargetExponent <= -32); // w is assumed to have an error less than 1 unit. Whenever w is scaled we // also scale its error. uint64_t w_error = 1; @@ -458,7 +458,7 @@ static bool DigitGenCounted(DiyFp w, // that is smaller than 'integrals'. while (*kappa > 0) { int digit = integrals / divisor; - ASSERT(digit <= 9); + DOUBLE_CONVERSION_ASSERT(digit <= 9); buffer[*length] = static_cast('0' + digit); (*length)++; requested_digits--; @@ -484,15 +484,15 @@ static bool DigitGenCounted(DiyFp w, // data (the 'unit'), too. // Note that the multiplication by 10 does not overflow, because w.e >= -60 // and thus one.e >= -60. - ASSERT(one.e() >= -60); - ASSERT(fractionals < one.f()); - ASSERT(UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF) / 10 >= one.f()); + DOUBLE_CONVERSION_ASSERT(one.e() >= -60); + DOUBLE_CONVERSION_ASSERT(fractionals < one.f()); + DOUBLE_CONVERSION_ASSERT(DOUBLE_CONVERSION_UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF) / 10 >= one.f()); while (requested_digits > 0 && fractionals > w_error) { fractionals *= 10; w_error *= 10; // Integer division by one. int digit = static_cast(fractionals >> -one.e()); - ASSERT(digit <= 9); + DOUBLE_CONVERSION_ASSERT(digit <= 9); buffer[*length] = static_cast('0' + digit); (*length)++; requested_digits--; @@ -530,11 +530,11 @@ static bool Grisu3(double v, if (mode == FAST_DTOA_SHORTEST) { Double(v).NormalizedBoundaries(&boundary_minus, &boundary_plus); } else { - ASSERT(mode == FAST_DTOA_SHORTEST_SINGLE); + DOUBLE_CONVERSION_ASSERT(mode == FAST_DTOA_SHORTEST_SINGLE); float single_v = static_cast(v); Single(single_v).NormalizedBoundaries(&boundary_minus, &boundary_plus); } - ASSERT(boundary_plus.e() == w.e()); + DOUBLE_CONVERSION_ASSERT(boundary_plus.e() == w.e()); DiyFp ten_mk; // Cached power of ten: 10^-k int mk; // -k int ten_mk_minimal_binary_exponent = @@ -545,7 +545,7 @@ static bool Grisu3(double v, ten_mk_minimal_binary_exponent, ten_mk_maximal_binary_exponent, &ten_mk, &mk); - ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() + + DOUBLE_CONVERSION_ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() + DiyFp::kSignificandSize) && (kMaximalTargetExponent >= w.e() + ten_mk.e() + DiyFp::kSignificandSize)); @@ -559,7 +559,7 @@ static bool Grisu3(double v, // In other words: let f = scaled_w.f() and e = scaled_w.e(), then // (f-1) * 2^e < w*10^k < (f+1) * 2^e DiyFp scaled_w = DiyFp::Times(w, ten_mk); - ASSERT(scaled_w.e() == + DOUBLE_CONVERSION_ASSERT(scaled_w.e() == boundary_plus.e() + ten_mk.e() + DiyFp::kSignificandSize); // In theory it would be possible to avoid some recomputations by computing // the difference between w and boundary_minus/plus (a power of 2) and to @@ -604,7 +604,7 @@ static bool Grisu3Counted(double v, ten_mk_minimal_binary_exponent, ten_mk_maximal_binary_exponent, &ten_mk, &mk); - ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() + + DOUBLE_CONVERSION_ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() + DiyFp::kSignificandSize) && (kMaximalTargetExponent >= w.e() + ten_mk.e() + DiyFp::kSignificandSize)); @@ -638,8 +638,8 @@ bool FastDtoa(double v, Vector buffer, int* length, int* decimal_point) { - ASSERT(v > 0); - ASSERT(!Double(v).IsSpecial()); + DOUBLE_CONVERSION_ASSERT(v > 0); + DOUBLE_CONVERSION_ASSERT(!Double(v).IsSpecial()); bool result = false; int decimal_exponent = 0; @@ -653,7 +653,7 @@ bool FastDtoa(double v, buffer, length, &decimal_exponent); break; default: - UNREACHABLE(); + DOUBLE_CONVERSION_UNREACHABLE(); } if (result) { *decimal_point = *length + decimal_exponent; diff --git a/double-conversion/fixed-dtoa.cc b/double-conversion/fixed-dtoa.cc index aef65fdc..ab6ef10e 100644 --- a/double-conversion/fixed-dtoa.cc +++ b/double-conversion/fixed-dtoa.cc @@ -25,7 +25,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include +#include #include "fixed-dtoa.h" #include "ieee.h" @@ -53,11 +53,11 @@ class UInt128 { accumulator >>= 32; accumulator = accumulator + (high_bits_ >> 32) * multiplicand; high_bits_ = (accumulator << 32) + part; - ASSERT((accumulator >> 32) == 0); + DOUBLE_CONVERSION_ASSERT((accumulator >> 32) == 0); } void Shift(int shift_amount) { - ASSERT(-64 <= shift_amount && shift_amount <= 64); + DOUBLE_CONVERSION_ASSERT(-64 <= shift_amount && shift_amount <= 64); if (shift_amount == 0) { return; } else if (shift_amount == -64) { @@ -98,7 +98,7 @@ class UInt128 { return high_bits_ == 0 && low_bits_ == 0; } - int BitAt(int position) { + int BitAt(int position) const { if (position >= 64) { return static_cast(high_bits_ >> (position - 64)) & 1; } else { @@ -230,13 +230,13 @@ static void RoundUp(Vector buffer, int* length, int* decimal_point) { static void FillFractionals(uint64_t fractionals, int exponent, int fractional_count, Vector buffer, int* length, int* decimal_point) { - ASSERT(-128 <= exponent && exponent <= 0); + DOUBLE_CONVERSION_ASSERT(-128 <= exponent && exponent <= 0); // 'fractionals' is a fixed-point number, with binary point at bit // (-exponent). Inside the function the non-converted remainder of fractionals // is a fixed-point number, with binary point at bit 'point'. if (-exponent <= 64) { // One 64 bit number is sufficient. - ASSERT(fractionals >> 56 == 0); + DOUBLE_CONVERSION_ASSERT(fractionals >> 56 == 0); int point = -exponent; for (int i = 0; i < fractional_count; ++i) { if (fractionals == 0) break; @@ -253,17 +253,18 @@ static void FillFractionals(uint64_t fractionals, int exponent, fractionals *= 5; point--; int digit = static_cast(fractionals >> point); - ASSERT(digit <= 9); + DOUBLE_CONVERSION_ASSERT(digit <= 9); buffer[*length] = static_cast('0' + digit); (*length)++; fractionals -= static_cast(digit) << point; } // If the first bit after the point is set we have to round up. - if (((fractionals >> (point - 1)) & 1) == 1) { + DOUBLE_CONVERSION_ASSERT(fractionals == 0 || point - 1 >= 0); + if ((fractionals != 0) && ((fractionals >> (point - 1)) & 1) == 1) { RoundUp(buffer, length, decimal_point); } } else { // We need 128 bits. - ASSERT(64 < -exponent && -exponent <= 128); + DOUBLE_CONVERSION_ASSERT(64 < -exponent && -exponent <= 128); UInt128 fractionals128 = UInt128(fractionals, 0); fractionals128.Shift(-exponent - 64); int point = 128; @@ -275,7 +276,7 @@ static void FillFractionals(uint64_t fractionals, int exponent, fractionals128.Multiply(5); point--; int digit = fractionals128.DivModPowerOf2(point); - ASSERT(digit <= 9); + DOUBLE_CONVERSION_ASSERT(digit <= 9); buffer[*length] = static_cast('0' + digit); (*length)++; } @@ -334,7 +335,7 @@ bool FastFixedDtoa(double v, // The quotient delivers the first digits, and the remainder fits into a 64 // bit number. // Dividing by 10^17 is equivalent to dividing by 5^17*2^17. - const uint64_t kFive17 = UINT64_2PART_C(0xB1, A2BC2EC5); // 5^17 + const uint64_t kFive17 = DOUBLE_CONVERSION_UINT64_2PART_C(0xB1, A2BC2EC5); // 5^17 uint64_t divisor = kFive17; int divisor_power = 17; uint64_t dividend = significand; @@ -382,7 +383,7 @@ bool FastFixedDtoa(double v, } else if (exponent < -128) { // This configuration (with at most 20 digits) means that all digits must be // 0. - ASSERT(fractional_count <= 20); + DOUBLE_CONVERSION_ASSERT(fractional_count <= 20); buffer[0] = '\0'; *length = 0; *decimal_point = -fractional_count; diff --git a/double-conversion/ieee.h b/double-conversion/ieee.h index 661141d1..8c3b862e 100644 --- a/double-conversion/ieee.h +++ b/double-conversion/ieee.h @@ -41,12 +41,14 @@ static float uint32_to_float(uint32_t d32) { return BitCast(d32); } // Helper functions for doubles. class Double { public: - static const uint64_t kSignMask = UINT64_2PART_C(0x80000000, 00000000); - static const uint64_t kExponentMask = UINT64_2PART_C(0x7FF00000, 00000000); - static const uint64_t kSignificandMask = UINT64_2PART_C(0x000FFFFF, FFFFFFFF); - static const uint64_t kHiddenBit = UINT64_2PART_C(0x00100000, 00000000); + static const uint64_t kSignMask = DOUBLE_CONVERSION_UINT64_2PART_C(0x80000000, 00000000); + static const uint64_t kExponentMask = DOUBLE_CONVERSION_UINT64_2PART_C(0x7FF00000, 00000000); + static const uint64_t kSignificandMask = DOUBLE_CONVERSION_UINT64_2PART_C(0x000FFFFF, FFFFFFFF); + static const uint64_t kHiddenBit = DOUBLE_CONVERSION_UINT64_2PART_C(0x00100000, 00000000); static const int kPhysicalSignificandSize = 52; // Excludes the hidden bit. static const int kSignificandSize = 53; + static const int kExponentBias = 0x3FF + kPhysicalSignificandSize; + static const int kMaxExponent = 0x7FF - kExponentBias; Double() : d64_(0) {} explicit Double(double d) : d64_(double_to_uint64(d)) {} @@ -57,14 +59,14 @@ class Double { // The value encoded by this Double must be greater or equal to +0.0. // It must not be special (infinity, or NaN). DiyFp AsDiyFp() const { - ASSERT(Sign() > 0); - ASSERT(!IsSpecial()); + DOUBLE_CONVERSION_ASSERT(Sign() > 0); + DOUBLE_CONVERSION_ASSERT(!IsSpecial()); return DiyFp(Significand(), Exponent()); } // The value encoded by this Double must be strictly greater than 0. DiyFp AsNormalizedDiyFp() const { - ASSERT(value() > 0.0); + DOUBLE_CONVERSION_ASSERT(value() > 0.0); uint64_t f = Significand(); int e = Exponent(); @@ -99,7 +101,7 @@ class Double { } double PreviousDouble() const { - if (d64_ == (kInfinity | kSignMask)) return -Double::Infinity(); + if (d64_ == (kInfinity | kSignMask)) return -Infinity(); if (Sign() < 0) { return Double(d64_ + 1).value(); } else { @@ -160,7 +162,7 @@ class Double { // Precondition: the value encoded by this Double must be greater or equal // than +0.0. DiyFp UpperBoundary() const { - ASSERT(Sign() > 0); + DOUBLE_CONVERSION_ASSERT(Sign() > 0); return DiyFp(Significand() * 2 + 1, Exponent() - 1); } @@ -169,7 +171,7 @@ class Double { // exponent as m_plus. // Precondition: the value encoded by this Double must be greater than 0. void NormalizedBoundaries(DiyFp* out_m_minus, DiyFp* out_m_plus) const { - ASSERT(value() > 0.0); + DOUBLE_CONVERSION_ASSERT(value() > 0.0); DiyFp v = this->AsDiyFp(); DiyFp m_plus = DiyFp::Normalize(DiyFp((v.f() << 1) + 1, v.e() - 1)); DiyFp m_minus; @@ -222,11 +224,9 @@ class Double { } private: - static const int kExponentBias = 0x3FF + kPhysicalSignificandSize; static const int kDenormalExponent = -kExponentBias + 1; - static const int kMaxExponent = 0x7FF - kExponentBias; - static const uint64_t kInfinity = UINT64_2PART_C(0x7FF00000, 00000000); - static const uint64_t kNaN = UINT64_2PART_C(0x7FF80000, 00000000); + static const uint64_t kInfinity = DOUBLE_CONVERSION_UINT64_2PART_C(0x7FF00000, 00000000); + static const uint64_t kNaN = DOUBLE_CONVERSION_UINT64_2PART_C(0x7FF80000, 00000000); const uint64_t d64_; @@ -257,7 +257,7 @@ class Double { (biased_exponent << kPhysicalSignificandSize); } - DISALLOW_COPY_AND_ASSIGN(Double); + DOUBLE_CONVERSION_DISALLOW_COPY_AND_ASSIGN(Double); }; class Single { @@ -276,8 +276,8 @@ class Single { // The value encoded by this Single must be greater or equal to +0.0. // It must not be special (infinity, or NaN). DiyFp AsDiyFp() const { - ASSERT(Sign() > 0); - ASSERT(!IsSpecial()); + DOUBLE_CONVERSION_ASSERT(Sign() > 0); + DOUBLE_CONVERSION_ASSERT(!IsSpecial()); return DiyFp(Significand(), Exponent()); } @@ -340,7 +340,7 @@ class Single { // exponent as m_plus. // Precondition: the value encoded by this Single must be greater than 0. void NormalizedBoundaries(DiyFp* out_m_minus, DiyFp* out_m_plus) const { - ASSERT(value() > 0.0); + DOUBLE_CONVERSION_ASSERT(value() > 0.0); DiyFp v = this->AsDiyFp(); DiyFp m_plus = DiyFp::Normalize(DiyFp((v.f() << 1) + 1, v.e() - 1)); DiyFp m_minus; @@ -358,7 +358,7 @@ class Single { // Precondition: the value encoded by this Single must be greater or equal // than +0.0. DiyFp UpperBoundary() const { - ASSERT(Sign() > 0); + DOUBLE_CONVERSION_ASSERT(Sign() > 0); return DiyFp(Significand() * 2 + 1, Exponent() - 1); } @@ -394,7 +394,7 @@ class Single { const uint32_t d32_; - DISALLOW_COPY_AND_ASSIGN(Single); + DOUBLE_CONVERSION_DISALLOW_COPY_AND_ASSIGN(Single); }; } // namespace double_conversion diff --git a/double-conversion/double-conversion.cc b/double-conversion/string-to-double.cc similarity index 55% rename from double-conversion/double-conversion.cc rename to double-conversion/string-to-double.cc index 6f21a012..d7f7a4aa 100644 --- a/double-conversion/double-conversion.cc +++ b/double-conversion/string-to-double.cc @@ -25,410 +25,67 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include -#include +#include +#include +#include -#include "double-conversion.h" +#include "string-to-double.h" -#include "bignum-dtoa.h" -#include "fast-dtoa.h" -#include "fixed-dtoa.h" #include "ieee.h" #include "strtod.h" #include "utils.h" namespace double_conversion { -const DoubleToStringConverter& DoubleToStringConverter::EcmaScriptConverter() { - int flags = UNIQUE_ZERO | EMIT_POSITIVE_EXPONENT_SIGN; - static DoubleToStringConverter converter(flags, - "Infinity", - "NaN", - 'e', - -6, 21, - 6, 0); - return converter; -} - - -bool DoubleToStringConverter::HandleSpecialValues( - double value, - StringBuilder* result_builder) const { - Double double_inspect(value); - if (double_inspect.IsInfinite()) { - if (infinity_symbol_ == NULL) return false; - if (value < 0) { - result_builder->AddCharacter('-'); - } - result_builder->AddString(infinity_symbol_); - return true; - } - if (double_inspect.IsNan()) { - if (nan_symbol_ == NULL) return false; - result_builder->AddString(nan_symbol_); - return true; - } - return false; -} - - -void DoubleToStringConverter::CreateExponentialRepresentation( - const char* decimal_digits, - int length, - int exponent, - StringBuilder* result_builder) const { - ASSERT(length != 0); - result_builder->AddCharacter(decimal_digits[0]); - if (length != 1) { - result_builder->AddCharacter('.'); - result_builder->AddSubstring(&decimal_digits[1], length-1); - } - result_builder->AddCharacter(exponent_character_); - if (exponent < 0) { - result_builder->AddCharacter('-'); - exponent = -exponent; - } else { - if ((flags_ & EMIT_POSITIVE_EXPONENT_SIGN) != 0) { - result_builder->AddCharacter('+'); - } - } - if (exponent == 0) { - result_builder->AddCharacter('0'); - return; - } - ASSERT(exponent < 1e4); - const int kMaxExponentLength = 5; - char buffer[kMaxExponentLength + 1]; - buffer[kMaxExponentLength] = '\0'; - int first_char_pos = kMaxExponentLength; - while (exponent > 0) { - buffer[--first_char_pos] = '0' + (exponent % 10); - exponent /= 10; - } - result_builder->AddSubstring(&buffer[first_char_pos], - kMaxExponentLength - first_char_pos); -} - - -void DoubleToStringConverter::CreateDecimalRepresentation( - const char* decimal_digits, - int length, - int decimal_point, - int digits_after_point, - StringBuilder* result_builder) const { - // Create a representation that is padded with zeros if needed. - if (decimal_point <= 0) { - // "0.00000decimal_rep" or "0.000decimal_rep00". - result_builder->AddCharacter('0'); - if (digits_after_point > 0) { - result_builder->AddCharacter('.'); - result_builder->AddPadding('0', -decimal_point); - ASSERT(length <= digits_after_point - (-decimal_point)); - result_builder->AddSubstring(decimal_digits, length); - int remaining_digits = digits_after_point - (-decimal_point) - length; - result_builder->AddPadding('0', remaining_digits); - } - } else if (decimal_point >= length) { - // "decimal_rep0000.00000" or "decimal_rep.0000". - result_builder->AddSubstring(decimal_digits, length); - result_builder->AddPadding('0', decimal_point - length); - if (digits_after_point > 0) { - result_builder->AddCharacter('.'); - result_builder->AddPadding('0', digits_after_point); - } - } else { - // "decima.l_rep000". - ASSERT(digits_after_point > 0); - result_builder->AddSubstring(decimal_digits, decimal_point); - result_builder->AddCharacter('.'); - ASSERT(length - decimal_point <= digits_after_point); - result_builder->AddSubstring(&decimal_digits[decimal_point], - length - decimal_point); - int remaining_digits = digits_after_point - (length - decimal_point); - result_builder->AddPadding('0', remaining_digits); - } - if (digits_after_point == 0) { - if ((flags_ & EMIT_TRAILING_DECIMAL_POINT) != 0) { - result_builder->AddCharacter('.'); - } - if ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) { - result_builder->AddCharacter('0'); - } - } -} - - -bool DoubleToStringConverter::ToShortestIeeeNumber( - double value, - StringBuilder* result_builder, - DoubleToStringConverter::DtoaMode mode) const { - ASSERT(mode == SHORTEST || mode == SHORTEST_SINGLE); - if (Double(value).IsSpecial()) { - return HandleSpecialValues(value, result_builder); - } - - int decimal_point; - bool sign; - const int kDecimalRepCapacity = kBase10MaximalLength + 1; - char decimal_rep[kDecimalRepCapacity]; - int decimal_rep_length; - - DoubleToAscii(value, mode, 0, decimal_rep, kDecimalRepCapacity, - &sign, &decimal_rep_length, &decimal_point); - - bool unique_zero = (flags_ & UNIQUE_ZERO) != 0; - if (sign && (value != 0.0 || !unique_zero)) { - result_builder->AddCharacter('-'); - } - - int exponent = decimal_point - 1; - if ((decimal_in_shortest_low_ <= exponent) && - (exponent < decimal_in_shortest_high_)) { - CreateDecimalRepresentation(decimal_rep, decimal_rep_length, - decimal_point, - Max(0, decimal_rep_length - decimal_point), - result_builder); - } else { - CreateExponentialRepresentation(decimal_rep, decimal_rep_length, exponent, - result_builder); - } - return true; -} - - -bool DoubleToStringConverter::ToFixed(double value, - int requested_digits, - StringBuilder* result_builder) const { - ASSERT(kMaxFixedDigitsBeforePoint == 60); - const double kFirstNonFixed = 1e60; - - if (Double(value).IsSpecial()) { - return HandleSpecialValues(value, result_builder); - } - - if (requested_digits > kMaxFixedDigitsAfterPoint) return false; - if (value >= kFirstNonFixed || value <= -kFirstNonFixed) return false; - - // Find a sufficiently precise decimal representation of n. - int decimal_point; - bool sign; - // Add space for the '\0' byte. - const int kDecimalRepCapacity = - kMaxFixedDigitsBeforePoint + kMaxFixedDigitsAfterPoint + 1; - char decimal_rep[kDecimalRepCapacity]; - int decimal_rep_length; - DoubleToAscii(value, FIXED, requested_digits, - decimal_rep, kDecimalRepCapacity, - &sign, &decimal_rep_length, &decimal_point); - - bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); - if (sign && (value != 0.0 || !unique_zero)) { - result_builder->AddCharacter('-'); - } +namespace { - CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point, - requested_digits, result_builder); - return true; +inline char ToLower(char ch) { + static const std::ctype& cType = + std::use_facet >(std::locale::classic()); + return cType.tolower(ch); } - -bool DoubleToStringConverter::ToExponential( - double value, - int requested_digits, - StringBuilder* result_builder) const { - if (Double(value).IsSpecial()) { - return HandleSpecialValues(value, result_builder); - } - - if (requested_digits < -1) return false; - if (requested_digits > kMaxExponentialDigits) return false; - - int decimal_point; - bool sign; - // Add space for digit before the decimal point and the '\0' character. - const int kDecimalRepCapacity = kMaxExponentialDigits + 2; - ASSERT(kDecimalRepCapacity > kBase10MaximalLength); - char decimal_rep[kDecimalRepCapacity]; - int decimal_rep_length; - - if (requested_digits == -1) { - DoubleToAscii(value, SHORTEST, 0, - decimal_rep, kDecimalRepCapacity, - &sign, &decimal_rep_length, &decimal_point); - } else { - DoubleToAscii(value, PRECISION, requested_digits + 1, - decimal_rep, kDecimalRepCapacity, - &sign, &decimal_rep_length, &decimal_point); - ASSERT(decimal_rep_length <= requested_digits + 1); - - for (int i = decimal_rep_length; i < requested_digits + 1; ++i) { - decimal_rep[i] = '0'; - } - decimal_rep_length = requested_digits + 1; - } - - bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); - if (sign && (value != 0.0 || !unique_zero)) { - result_builder->AddCharacter('-'); - } - - int exponent = decimal_point - 1; - CreateExponentialRepresentation(decimal_rep, - decimal_rep_length, - exponent, - result_builder); - return true; +inline char Pass(char ch) { + return ch; } - -bool DoubleToStringConverter::ToPrecision(double value, - int precision, - StringBuilder* result_builder) const { - if (Double(value).IsSpecial()) { - return HandleSpecialValues(value, result_builder); - } - - if (precision < kMinPrecisionDigits || precision > kMaxPrecisionDigits) { - return false; - } - - // Find a sufficiently precise decimal representation of n. - int decimal_point; - bool sign; - // Add one for the terminating null character. - const int kDecimalRepCapacity = kMaxPrecisionDigits + 1; - char decimal_rep[kDecimalRepCapacity]; - int decimal_rep_length; - - DoubleToAscii(value, PRECISION, precision, - decimal_rep, kDecimalRepCapacity, - &sign, &decimal_rep_length, &decimal_point); - ASSERT(decimal_rep_length <= precision); - - bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); - if (sign && (value != 0.0 || !unique_zero)) { - result_builder->AddCharacter('-'); - } - - // The exponent if we print the number as x.xxeyyy. That is with the - // decimal point after the first digit. - int exponent = decimal_point - 1; - - int extra_zero = ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) ? 1 : 0; - if ((-decimal_point + 1 > max_leading_padding_zeroes_in_precision_mode_) || - (decimal_point - precision + extra_zero > - max_trailing_padding_zeroes_in_precision_mode_)) { - // Fill buffer to contain 'precision' digits. - // Usually the buffer is already at the correct length, but 'DoubleToAscii' - // is allowed to return less characters. - for (int i = decimal_rep_length; i < precision; ++i) { - decimal_rep[i] = '0'; +template +static inline bool ConsumeSubStringImpl(Iterator* current, + Iterator end, + const char* substring, + Converter converter) { + DOUBLE_CONVERSION_ASSERT(converter(**current) == *substring); + for (substring++; *substring != '\0'; substring++) { + ++*current; + if (*current == end || converter(**current) != *substring) { + return false; } - - CreateExponentialRepresentation(decimal_rep, - precision, - exponent, - result_builder); - } else { - CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point, - Max(0, precision - decimal_point), - result_builder); } + ++*current; return true; } - -static BignumDtoaMode DtoaToBignumDtoaMode( - DoubleToStringConverter::DtoaMode dtoa_mode) { - switch (dtoa_mode) { - case DoubleToStringConverter::SHORTEST: return BIGNUM_DTOA_SHORTEST; - case DoubleToStringConverter::SHORTEST_SINGLE: - return BIGNUM_DTOA_SHORTEST_SINGLE; - case DoubleToStringConverter::FIXED: return BIGNUM_DTOA_FIXED; - case DoubleToStringConverter::PRECISION: return BIGNUM_DTOA_PRECISION; - default: - UNREACHABLE(); - } -} - - -void DoubleToStringConverter::DoubleToAscii(double v, - DtoaMode mode, - int requested_digits, - char* buffer, - int buffer_length, - bool* sign, - int* length, - int* point) { - Vector vector(buffer, buffer_length); - ASSERT(!Double(v).IsSpecial()); - ASSERT(mode == SHORTEST || mode == SHORTEST_SINGLE || requested_digits >= 0); - - if (Double(v).Sign() < 0) { - *sign = true; - v = -v; - } else { - *sign = false; - } - - if (mode == PRECISION && requested_digits == 0) { - vector[0] = '\0'; - *length = 0; - return; - } - - if (v == 0) { - vector[0] = '0'; - vector[1] = '\0'; - *length = 1; - *point = 1; - return; - } - - bool fast_worked; - switch (mode) { - case SHORTEST: - fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST, 0, vector, length, point); - break; - case SHORTEST_SINGLE: - fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST_SINGLE, 0, - vector, length, point); - break; - case FIXED: - fast_worked = FastFixedDtoa(v, requested_digits, vector, length, point); - break; - case PRECISION: - fast_worked = FastDtoa(v, FAST_DTOA_PRECISION, requested_digits, - vector, length, point); - break; - default: - fast_worked = false; - UNREACHABLE(); - } - if (fast_worked) return; - - // If the fast dtoa didn't succeed use the slower bignum version. - BignumDtoaMode bignum_mode = DtoaToBignumDtoaMode(mode); - BignumDtoa(v, bignum_mode, requested_digits, vector, length, point); - vector[*length] = '\0'; -} - - // Consumes the given substring from the iterator. // Returns false, if the substring does not match. template static bool ConsumeSubString(Iterator* current, Iterator end, - const char* substring) { - ASSERT(**current == *substring); - for (substring++; *substring != '\0'; substring++) { - ++*current; - if (*current == end || **current != *substring) return false; + const char* substring, + bool allow_case_insensitivity) { + if (allow_case_insensitivity) { + return ConsumeSubStringImpl(current, end, substring, ToLower); + } else { + return ConsumeSubStringImpl(current, end, substring, Pass); } - ++*current; - return true; } +// Consumes first character of the str is equal to ch +inline bool ConsumeFirstCharacter(char ch, + const char* str, + bool case_insensitivity) { + return case_insensitivity ? ToLower(ch) == str[0] : ch == str[0]; +} +} // namespace // Maximum number of significant digits in decimal representation. // The longest possible double in decimal representation is @@ -441,14 +98,14 @@ const int kMaxSignificantDigits = 772; static const char kWhitespaceTable7[] = { 32, 13, 10, 9, 11, 12 }; -static const int kWhitespaceTable7Length = ARRAY_SIZE(kWhitespaceTable7); +static const int kWhitespaceTable7Length = DOUBLE_CONVERSION_ARRAY_SIZE(kWhitespaceTable7); static const uc16 kWhitespaceTable16[] = { 160, 8232, 8233, 5760, 6158, 8192, 8193, 8194, 8195, 8196, 8197, 8198, 8199, 8200, 8201, 8202, 8239, 8287, 12288, 65279 }; -static const int kWhitespaceTable16Length = ARRAY_SIZE(kWhitespaceTable16); +static const int kWhitespaceTable16Length = DOUBLE_CONVERSION_ARRAY_SIZE(kWhitespaceTable16); static bool isWhitespace(int x) { @@ -494,7 +151,7 @@ static double SignedZero(bool sign) { // because it constant-propagated the radix and concluded that the last // condition was always true. By moving it into a separate function the // compiler wouldn't warn anymore. -#if _MSC_VER +#ifdef _MSC_VER #pragma optimize("",off) static bool IsDecimalDigitForRadix(int c, int radix) { return '0' <= c && c <= '9' && (c - '0') < radix; @@ -502,7 +159,7 @@ static bool IsDecimalDigitForRadix(int c, int radix) { #pragma optimize("",on) #else static bool inline IsDecimalDigitForRadix(int c, int radix) { - return '0' <= c && c <= '9' && (c - '0') < radix; + return '0' <= c && c <= '9' && (c - '0') < radix; } #endif // Returns true if 'c' is a character digit that is valid for the given radix. @@ -516,17 +173,86 @@ static bool IsCharacterDigitForRadix(int c, int radix, char a_character) { return radix > 10 && c >= a_character && c < a_character + radix - 10; } +// Returns true, when the iterator is equal to end. +template +static bool Advance (Iterator* it, uc16 separator, int base, Iterator& end) { + if (separator == StringToDoubleConverter::kNoSeparator) { + ++(*it); + return *it == end; + } + if (!isDigit(**it, base)) { + ++(*it); + return *it == end; + } + ++(*it); + if (*it == end) return true; + if (*it + 1 == end) return false; + if (**it == separator && isDigit(*(*it + 1), base)) { + ++(*it); + } + return *it == end; +} + +// Checks whether the string in the range start-end is a hex-float string. +// This function assumes that the leading '0x'/'0X' is already consumed. +// +// Hex float strings are of one of the following forms: +// - hex_digits+ 'p' ('+'|'-')? exponent_digits+ +// - hex_digits* '.' hex_digits+ 'p' ('+'|'-')? exponent_digits+ +// - hex_digits+ '.' 'p' ('+'|'-')? exponent_digits+ +template +static bool IsHexFloatString(Iterator start, + Iterator end, + uc16 separator, + bool allow_trailing_junk) { + DOUBLE_CONVERSION_ASSERT(start != end); + + Iterator current = start; + + bool saw_digit = false; + while (isDigit(*current, 16)) { + saw_digit = true; + if (Advance(¤t, separator, 16, end)) return false; + } + if (*current == '.') { + if (Advance(¤t, separator, 16, end)) return false; + while (isDigit(*current, 16)) { + saw_digit = true; + if (Advance(¤t, separator, 16, end)) return false; + } + } + if (!saw_digit) return false; + if (*current != 'p' && *current != 'P') return false; + if (Advance(¤t, separator, 16, end)) return false; + if (*current == '+' || *current == '-') { + if (Advance(¤t, separator, 16, end)) return false; + } + if (!isDigit(*current, 10)) return false; + if (Advance(¤t, separator, 16, end)) return true; + while (isDigit(*current, 10)) { + if (Advance(¤t, separator, 16, end)) return true; + } + return allow_trailing_junk || !AdvanceToNonspace(¤t, end); +} + // Parsing integers with radix 2, 4, 8, 16, 32. Assumes current != end. +// +// If parse_as_hex_float is true, then the string must be a valid +// hex-float. template static double RadixStringToIeee(Iterator* current, Iterator end, bool sign, + uc16 separator, + bool parse_as_hex_float, bool allow_trailing_junk, double junk_string_value, bool read_as_double, bool* result_is_junk) { - ASSERT(*current != end); + DOUBLE_CONVERSION_ASSERT(*current != end); + DOUBLE_CONVERSION_ASSERT(!parse_as_hex_float || + IsHexFloatString(*current, end, separator, allow_trailing_junk)); const int kDoubleSize = Double::kSignificandSize; const int kSingleSize = Single::kSignificandSize; @@ -534,27 +260,39 @@ static double RadixStringToIeee(Iterator* current, *result_is_junk = true; + int64_t number = 0; + int exponent = 0; + const int radix = (1 << radix_log_2); + // Whether we have encountered a '.' and are parsing the decimal digits. + // Only relevant if parse_as_hex_float is true. + bool post_decimal = false; + // Skip leading 0s. while (**current == '0') { - ++(*current); - if (*current == end) { + if (Advance(current, separator, radix, end)) { *result_is_junk = false; return SignedZero(sign); } } - int64_t number = 0; - int exponent = 0; - const int radix = (1 << radix_log_2); - - do { + while (true) { int digit; if (IsDecimalDigitForRadix(**current, radix)) { digit = static_cast(**current) - '0'; + if (post_decimal) exponent -= radix_log_2; } else if (IsCharacterDigitForRadix(**current, radix, 'a')) { digit = static_cast(**current) - 'a' + 10; + if (post_decimal) exponent -= radix_log_2; } else if (IsCharacterDigitForRadix(**current, radix, 'A')) { digit = static_cast(**current) - 'A' + 10; + if (post_decimal) exponent -= radix_log_2; + } else if (parse_as_hex_float && **current == '.') { + post_decimal = true; + Advance(current, separator, radix, end); + DOUBLE_CONVERSION_ASSERT(*current != end); + continue; + } else if (parse_as_hex_float && (**current == 'p' || **current == 'P')) { + break; } else { if (allow_trailing_junk || !AdvanceToNonspace(current, end)) { break; @@ -577,17 +315,26 @@ static double RadixStringToIeee(Iterator* current, int dropped_bits_mask = ((1 << overflow_bits_count) - 1); int dropped_bits = static_cast(number) & dropped_bits_mask; number >>= overflow_bits_count; - exponent = overflow_bits_count; + exponent += overflow_bits_count; bool zero_tail = true; for (;;) { - ++(*current); - if (*current == end || !isDigit(**current, radix)) break; + if (Advance(current, separator, radix, end)) break; + if (parse_as_hex_float && **current == '.') { + // Just run over the '.'. We are just trying to see whether there is + // a non-zero digit somewhere. + Advance(current, separator, radix, end); + DOUBLE_CONVERSION_ASSERT(*current != end); + post_decimal = true; + } + if (!isDigit(**current, radix)) break; zero_tail = zero_tail && **current == '0'; - exponent += radix_log_2; + if (!post_decimal) exponent += radix_log_2; } - if (!allow_trailing_junk && AdvanceToNonspace(current, end)) { + if (!parse_as_hex_float && + !allow_trailing_junk && + AdvanceToNonspace(current, end)) { return junk_string_value; } @@ -609,15 +356,41 @@ static double RadixStringToIeee(Iterator* current, } break; } - ++(*current); - } while (*current != end); + if (Advance(current, separator, radix, end)) break; + } - ASSERT(number < ((int64_t)1 << kSignificandSize)); - ASSERT(static_cast(static_cast(number)) == number); + DOUBLE_CONVERSION_ASSERT(number < ((int64_t)1 << kSignificandSize)); + DOUBLE_CONVERSION_ASSERT(static_cast(static_cast(number)) == number); *result_is_junk = false; - if (exponent == 0) { + if (parse_as_hex_float) { + DOUBLE_CONVERSION_ASSERT(**current == 'p' || **current == 'P'); + Advance(current, separator, radix, end); + DOUBLE_CONVERSION_ASSERT(*current != end); + bool is_negative = false; + if (**current == '+') { + Advance(current, separator, radix, end); + DOUBLE_CONVERSION_ASSERT(*current != end); + } else if (**current == '-') { + is_negative = true; + Advance(current, separator, radix, end); + DOUBLE_CONVERSION_ASSERT(*current != end); + } + int written_exponent = 0; + while (IsDecimalDigitForRadix(**current, 10)) { + // No need to read exponents if they are too big. That could potentially overflow + // the `written_exponent` variable. + if (abs(written_exponent) <= 100 * Double::kMaxExponent) { + written_exponent = 10 * written_exponent + **current - '0'; + } + if (Advance(current, separator, radix, end)) break; + } + if (is_negative) written_exponent = -written_exponent; + exponent += written_exponent; + } + + if (exponent == 0 || number == 0) { if (sign) { if (number == 0) return -0.0; number = -number; @@ -625,11 +398,11 @@ static double RadixStringToIeee(Iterator* current, return static_cast(number); } - ASSERT(number != 0); - return Double(DiyFp(number, exponent)).value(); + DOUBLE_CONVERSION_ASSERT(number != 0); + double result = Double(DiyFp(number, exponent)).value(); + return sign ? -result : result; } - template double StringToDoubleConverter::StringToIeee( Iterator input, @@ -645,6 +418,7 @@ double StringToDoubleConverter::StringToIeee( const bool allow_leading_spaces = (flags_ & ALLOW_LEADING_SPACES) != 0; const bool allow_trailing_spaces = (flags_ & ALLOW_TRAILING_SPACES) != 0; const bool allow_spaces_after_sign = (flags_ & ALLOW_SPACES_AFTER_SIGN) != 0; + const bool allow_case_insensitivity = (flags_ & ALLOW_CASE_INSENSITIVITY) != 0; // To make sure that iterator dereferencing is valid the following // convention is used: @@ -694,8 +468,8 @@ double StringToDoubleConverter::StringToIeee( } if (infinity_symbol_ != NULL) { - if (*current == infinity_symbol_[0]) { - if (!ConsumeSubString(¤t, end, infinity_symbol_)) { + if (ConsumeFirstCharacter(*current, infinity_symbol_, allow_case_insensitivity)) { + if (!ConsumeSubString(¤t, end, infinity_symbol_, allow_case_insensitivity)) { return junk_string_value_; } @@ -706,15 +480,15 @@ double StringToDoubleConverter::StringToIeee( return junk_string_value_; } - ASSERT(buffer_pos == 0); + DOUBLE_CONVERSION_ASSERT(buffer_pos == 0); *processed_characters_count = static_cast(current - input); return sign ? -Double::Infinity() : Double::Infinity(); } } if (nan_symbol_ != NULL) { - if (*current == nan_symbol_[0]) { - if (!ConsumeSubString(¤t, end, nan_symbol_)) { + if (ConsumeFirstCharacter(*current, nan_symbol_, allow_case_insensitivity)) { + if (!ConsumeSubString(¤t, end, nan_symbol_, allow_case_insensitivity)) { return junk_string_value_; } @@ -725,7 +499,7 @@ double StringToDoubleConverter::StringToIeee( return junk_string_value_; } - ASSERT(buffer_pos == 0); + DOUBLE_CONVERSION_ASSERT(buffer_pos == 0); *processed_characters_count = static_cast(current - input); return sign ? -Double::NaN() : Double::NaN(); } @@ -733,8 +507,7 @@ double StringToDoubleConverter::StringToIeee( bool leading_zero = false; if (*current == '0') { - ++current; - if (current == end) { + if (Advance(¤t, separator_, 10, end)) { *processed_characters_count = static_cast(current - input); return SignedZero(sign); } @@ -742,16 +515,25 @@ double StringToDoubleConverter::StringToIeee( leading_zero = true; // It could be hexadecimal value. - if ((flags_ & ALLOW_HEX) && (*current == 'x' || *current == 'X')) { + if (((flags_ & ALLOW_HEX) || (flags_ & ALLOW_HEX_FLOATS)) && + (*current == 'x' || *current == 'X')) { ++current; - if (current == end || !isDigit(*current, 16)) { - return junk_string_value_; // "0x". + + if (current == end) return junk_string_value_; // "0x" + + bool parse_as_hex_float = (flags_ & ALLOW_HEX_FLOATS) && + IsHexFloatString(current, end, separator_, allow_trailing_junk); + + if (!parse_as_hex_float && !isDigit(*current, 16)) { + return junk_string_value_; } bool result_is_junk; double result = RadixStringToIeee<4>(¤t, end, sign, + separator_, + parse_as_hex_float, allow_trailing_junk, junk_string_value_, read_as_double, @@ -765,8 +547,7 @@ double StringToDoubleConverter::StringToIeee( // Ignore leading zeros in the integer part. while (*current == '0') { - ++current; - if (current == end) { + if (Advance(¤t, separator_, 10, end)) { *processed_characters_count = static_cast(current - input); return SignedZero(sign); } @@ -778,7 +559,7 @@ double StringToDoubleConverter::StringToIeee( // Copy significant digits of the integer part (if any) to the buffer. while (*current >= '0' && *current <= '9') { if (significant_digits < kMaxSignificantDigits) { - ASSERT(buffer_pos < kBufferSize); + DOUBLE_CONVERSION_ASSERT(buffer_pos < kBufferSize); buffer[buffer_pos++] = static_cast(*current); significant_digits++; // Will later check if it's an octal in the buffer. @@ -787,8 +568,7 @@ double StringToDoubleConverter::StringToIeee( nonzero_digit_dropped = nonzero_digit_dropped || *current != '0'; } octal = octal && *current < '8'; - ++current; - if (current == end) goto parsing_done; + if (Advance(¤t, separator_, 10, end)) goto parsing_done; } if (significant_digits == 0) { @@ -799,8 +579,7 @@ double StringToDoubleConverter::StringToIeee( if (octal && !allow_trailing_junk) return junk_string_value_; if (octal) goto parsing_done; - ++current; - if (current == end) { + if (Advance(¤t, separator_, 10, end)) { if (significant_digits == 0 && !leading_zero) { return junk_string_value_; } else { @@ -813,8 +592,7 @@ double StringToDoubleConverter::StringToIeee( // Integer part consists of 0 or is absent. Significant digits start after // leading zeros (if any). while (*current == '0') { - ++current; - if (current == end) { + if (Advance(¤t, separator_, 10, end)) { *processed_characters_count = static_cast(current - input); return SignedZero(sign); } @@ -826,7 +604,7 @@ double StringToDoubleConverter::StringToIeee( // We don't emit a '.', but adjust the exponent instead. while (*current >= '0' && *current <= '9') { if (significant_digits < kMaxSignificantDigits) { - ASSERT(buffer_pos < kBufferSize); + DOUBLE_CONVERSION_ASSERT(buffer_pos < kBufferSize); buffer[buffer_pos++] = static_cast(*current); significant_digits++; exponent--; @@ -834,8 +612,7 @@ double StringToDoubleConverter::StringToIeee( // Ignore insignificant digits in the fractional part. nonzero_digit_dropped = nonzero_digit_dropped || *current != '0'; } - ++current; - if (current == end) goto parsing_done; + if (Advance(¤t, separator_, 10, end)) goto parsing_done; } } @@ -851,9 +628,11 @@ double StringToDoubleConverter::StringToIeee( if (*current == 'e' || *current == 'E') { if (octal && !allow_trailing_junk) return junk_string_value_; if (octal) goto parsing_done; + Iterator junk_begin = current; ++current; if (current == end) { if (allow_trailing_junk) { + current = junk_begin; goto parsing_done; } else { return junk_string_value_; @@ -865,6 +644,7 @@ double StringToDoubleConverter::StringToIeee( ++current; if (current == end) { if (allow_trailing_junk) { + current = junk_begin; goto parsing_done; } else { return junk_string_value_; @@ -874,6 +654,7 @@ double StringToDoubleConverter::StringToIeee( if (current == end || *current < '0' || *current > '9') { if (allow_trailing_junk) { + current = junk_begin; goto parsing_done; } else { return junk_string_value_; @@ -881,7 +662,7 @@ double StringToDoubleConverter::StringToIeee( } const int max_exponent = INT_MAX / 2; - ASSERT(-max_exponent / 2 <= exponent && exponent <= max_exponent / 2); + DOUBLE_CONVERSION_ASSERT(-max_exponent / 2 <= exponent && exponent <= max_exponent / 2); int num = 0; do { // Check overflow. @@ -918,11 +699,13 @@ double StringToDoubleConverter::StringToIeee( result = RadixStringToIeee<3>(&start, buffer + buffer_pos, sign, + separator_, + false, // Don't parse as hex_float. allow_trailing_junk, junk_string_value_, read_as_double, &result_is_junk); - ASSERT(!result_is_junk); + DOUBLE_CONVERSION_ASSERT(!result_is_junk); *processed_characters_count = static_cast(current - input); return result; } @@ -932,7 +715,7 @@ double StringToDoubleConverter::StringToIeee( exponent--; } - ASSERT(buffer_pos < kBufferSize); + DOUBLE_CONVERSION_ASSERT(buffer_pos < kBufferSize); buffer[buffer_pos] = '\0'; double converted; diff --git a/double-conversion/string-to-double.h b/double-conversion/string-to-double.h new file mode 100644 index 00000000..ecd6c761 --- /dev/null +++ b/double-conversion/string-to-double.h @@ -0,0 +1,226 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_STRING_TO_DOUBLE_H_ +#define DOUBLE_CONVERSION_STRING_TO_DOUBLE_H_ + +#include "utils.h" + +namespace double_conversion { + +class StringToDoubleConverter { + public: + // Enumeration for allowing octals and ignoring junk when converting + // strings to numbers. + enum Flags { + NO_FLAGS = 0, + ALLOW_HEX = 1, + ALLOW_OCTALS = 2, + ALLOW_TRAILING_JUNK = 4, + ALLOW_LEADING_SPACES = 8, + ALLOW_TRAILING_SPACES = 16, + ALLOW_SPACES_AFTER_SIGN = 32, + ALLOW_CASE_INSENSITIVITY = 64, + ALLOW_CASE_INSENSIBILITY = 64, // Deprecated + ALLOW_HEX_FLOATS = 128, + }; + + static const uc16 kNoSeparator = '\0'; + + // Flags should be a bit-or combination of the possible Flags-enum. + // - NO_FLAGS: no special flags. + // - ALLOW_HEX: recognizes the prefix "0x". Hex numbers may only be integers. + // Ex: StringToDouble("0x1234") -> 4660.0 + // In StringToDouble("0x1234.56") the characters ".56" are trailing + // junk. The result of the call is hence dependent on + // the ALLOW_TRAILING_JUNK flag and/or the junk value. + // With this flag "0x" is a junk-string. Even with ALLOW_TRAILING_JUNK, + // the string will not be parsed as "0" followed by junk. + // + // - ALLOW_OCTALS: recognizes the prefix "0" for octals: + // If a sequence of octal digits starts with '0', then the number is + // read as octal integer. Octal numbers may only be integers. + // Ex: StringToDouble("01234") -> 668.0 + // StringToDouble("012349") -> 12349.0 // Not a sequence of octal + // // digits. + // In StringToDouble("01234.56") the characters ".56" are trailing + // junk. The result of the call is hence dependent on + // the ALLOW_TRAILING_JUNK flag and/or the junk value. + // In StringToDouble("01234e56") the characters "e56" are trailing + // junk, too. + // - ALLOW_TRAILING_JUNK: ignore trailing characters that are not part of + // a double literal. + // - ALLOW_LEADING_SPACES: skip over leading whitespace, including spaces, + // new-lines, and tabs. + // - ALLOW_TRAILING_SPACES: ignore trailing whitespace. + // - ALLOW_SPACES_AFTER_SIGN: ignore whitespace after the sign. + // Ex: StringToDouble("- 123.2") -> -123.2. + // StringToDouble("+ 123.2") -> 123.2 + // - ALLOW_CASE_INSENSITIVITY: ignore case of characters for special values: + // infinity and nan. + // - ALLOW_HEX_FLOATS: allows hexadecimal float literals. + // This *must* start with "0x" and separate the exponent with "p". + // Examples: 0x1.2p3 == 9.0 + // 0x10.1p0 == 16.0625 + // ALLOW_HEX and ALLOW_HEX_FLOATS are indendent. + // + // empty_string_value is returned when an empty string is given as input. + // If ALLOW_LEADING_SPACES or ALLOW_TRAILING_SPACES are set, then a string + // containing only spaces is converted to the 'empty_string_value', too. + // + // junk_string_value is returned when + // a) ALLOW_TRAILING_JUNK is not set, and a junk character (a character not + // part of a double-literal) is found. + // b) ALLOW_TRAILING_JUNK is set, but the string does not start with a + // double literal. + // + // infinity_symbol and nan_symbol are strings that are used to detect + // inputs that represent infinity and NaN. They can be null, in which case + // they are ignored. + // The conversion routine first reads any possible signs. Then it compares the + // following character of the input-string with the first character of + // the infinity, and nan-symbol. If either matches, the function assumes, that + // a match has been found, and expects the following input characters to match + // the remaining characters of the special-value symbol. + // This means that the following restrictions apply to special-value symbols: + // - they must not start with signs ('+', or '-'), + // - they must not have the same first character. + // - they must not start with digits. + // + // If the separator character is not kNoSeparator, then that specific + // character is ignored when in between two valid digits of the significant. + // It is not allowed to appear in the exponent. + // It is not allowed to lead or trail the number. + // It is not allowed to appear twice next to each other. + // + // Examples: + // flags = ALLOW_HEX | ALLOW_TRAILING_JUNK, + // empty_string_value = 0.0, + // junk_string_value = NaN, + // infinity_symbol = "infinity", + // nan_symbol = "nan": + // StringToDouble("0x1234") -> 4660.0. + // StringToDouble("0x1234K") -> 4660.0. + // StringToDouble("") -> 0.0 // empty_string_value. + // StringToDouble(" ") -> NaN // junk_string_value. + // StringToDouble(" 1") -> NaN // junk_string_value. + // StringToDouble("0x") -> NaN // junk_string_value. + // StringToDouble("-123.45") -> -123.45. + // StringToDouble("--123.45") -> NaN // junk_string_value. + // StringToDouble("123e45") -> 123e45. + // StringToDouble("123E45") -> 123e45. + // StringToDouble("123e+45") -> 123e45. + // StringToDouble("123E-45") -> 123e-45. + // StringToDouble("123e") -> 123.0 // trailing junk ignored. + // StringToDouble("123e-") -> 123.0 // trailing junk ignored. + // StringToDouble("+NaN") -> NaN // NaN string literal. + // StringToDouble("-infinity") -> -inf. // infinity literal. + // StringToDouble("Infinity") -> NaN // junk_string_value. + // + // flags = ALLOW_OCTAL | ALLOW_LEADING_SPACES, + // empty_string_value = 0.0, + // junk_string_value = NaN, + // infinity_symbol = NULL, + // nan_symbol = NULL: + // StringToDouble("0x1234") -> NaN // junk_string_value. + // StringToDouble("01234") -> 668.0. + // StringToDouble("") -> 0.0 // empty_string_value. + // StringToDouble(" ") -> 0.0 // empty_string_value. + // StringToDouble(" 1") -> 1.0 + // StringToDouble("0x") -> NaN // junk_string_value. + // StringToDouble("0123e45") -> NaN // junk_string_value. + // StringToDouble("01239E45") -> 1239e45. + // StringToDouble("-infinity") -> NaN // junk_string_value. + // StringToDouble("NaN") -> NaN // junk_string_value. + // + // flags = NO_FLAGS, + // separator = ' ': + // StringToDouble("1 2 3 4") -> 1234.0 + // StringToDouble("1 2") -> NaN // junk_string_value + // StringToDouble("1 000 000.0") -> 1000000.0 + // StringToDouble("1.000 000") -> 1.0 + // StringToDouble("1.0e1 000") -> NaN // junk_string_value + StringToDoubleConverter(int flags, + double empty_string_value, + double junk_string_value, + const char* infinity_symbol, + const char* nan_symbol, + uc16 separator = kNoSeparator) + : flags_(flags), + empty_string_value_(empty_string_value), + junk_string_value_(junk_string_value), + infinity_symbol_(infinity_symbol), + nan_symbol_(nan_symbol), + separator_(separator) { + } + + // Performs the conversion. + // The output parameter 'processed_characters_count' is set to the number + // of characters that have been processed to read the number. + // Spaces than are processed with ALLOW_{LEADING|TRAILING}_SPACES are included + // in the 'processed_characters_count'. Trailing junk is never included. + double StringToDouble(const char* buffer, + int length, + int* processed_characters_count) const; + + // Same as StringToDouble above but for 16 bit characters. + double StringToDouble(const uc16* buffer, + int length, + int* processed_characters_count) const; + + // Same as StringToDouble but reads a float. + // Note that this is not equivalent to static_cast(StringToDouble(...)) + // due to potential double-rounding. + float StringToFloat(const char* buffer, + int length, + int* processed_characters_count) const; + + // Same as StringToFloat above but for 16 bit characters. + float StringToFloat(const uc16* buffer, + int length, + int* processed_characters_count) const; + + private: + const int flags_; + const double empty_string_value_; + const double junk_string_value_; + const char* const infinity_symbol_; + const char* const nan_symbol_; + const uc16 separator_; + + template + double StringToIeee(Iterator start_pointer, + int length, + bool read_as_double, + int* processed_characters_count) const; + + DOUBLE_CONVERSION_DISALLOW_IMPLICIT_CONSTRUCTORS(StringToDoubleConverter); +}; + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_STRING_TO_DOUBLE_H_ diff --git a/double-conversion/strtod.cc b/double-conversion/strtod.cc index 17abcbb2..d0bb7f70 100644 --- a/double-conversion/strtod.cc +++ b/double-conversion/strtod.cc @@ -25,13 +25,13 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include -#include +#include +#include -#include "strtod.h" #include "bignum.h" #include "cached-powers.h" #include "ieee.h" +#include "strtod.h" namespace double_conversion { @@ -52,7 +52,7 @@ static const int kMaxDecimalPower = 309; static const int kMinDecimalPower = -324; // 2^64 = 18446744073709551616 -static const uint64_t kMaxUint64 = UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF); +static const uint64_t kMaxUint64 = DOUBLE_CONVERSION_UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF); static const double exact_powers_of_ten[] = { @@ -81,7 +81,7 @@ static const double exact_powers_of_ten[] = { // 10^22 = 0x21e19e0c9bab2400000 = 0x878678326eac9 * 2^22 10000000000000000000000.0 }; -static const int kExactPowersOfTenSize = ARRAY_SIZE(exact_powers_of_ten); +static const int kExactPowersOfTenSize = DOUBLE_CONVERSION_ARRAY_SIZE(exact_powers_of_ten); // Maximum number of significant digits in the decimal representation. // In fact the value is 772 (see conversions.cc), but to give us some margin @@ -117,7 +117,7 @@ static void CutToMaxSignificantDigits(Vector buffer, } // The input buffer has been trimmed. Therefore the last digit must be // different from '0'. - ASSERT(buffer[buffer.length() - 1] != '0'); + DOUBLE_CONVERSION_ASSERT(buffer[buffer.length() - 1] != '0'); // Set the last digit to be non-zero. This is sufficient to guarantee // correct rounding. significant_buffer[kMaxSignificantDecimalDigits - 1] = '1'; @@ -138,7 +138,7 @@ static void TrimAndCut(Vector buffer, int exponent, exponent += left_trimmed.length() - right_trimmed.length(); if (right_trimmed.length() > kMaxSignificantDecimalDigits) { (void) space_size; // Mark variable as used. - ASSERT(space_size >= kMaxSignificantDecimalDigits); + DOUBLE_CONVERSION_ASSERT(space_size >= kMaxSignificantDecimalDigits); CutToMaxSignificantDigits(right_trimmed, exponent, buffer_copy_space, updated_exponent); *trimmed = Vector(buffer_copy_space, @@ -161,7 +161,7 @@ static uint64_t ReadUint64(Vector buffer, int i = 0; while (i < buffer.length() && result <= (kMaxUint64 / 10 - 1)) { int digit = buffer[i++] - '0'; - ASSERT(0 <= digit && digit <= 9); + DOUBLE_CONVERSION_ASSERT(0 <= digit && digit <= 9); result = 10 * result + digit; } *number_of_read_digits = i; @@ -205,7 +205,7 @@ static bool DoubleStrtod(Vector trimmed, // Note that the ARM simulator is compiled for 32bits. It therefore exhibits // the same problem. return false; -#endif +#else if (trimmed.length() <= kMaxExactDoubleIntegerDecimalDigits) { int read_digits; // The trimmed input fits into a double. @@ -217,14 +217,14 @@ static bool DoubleStrtod(Vector trimmed, if (exponent < 0 && -exponent < kExactPowersOfTenSize) { // 10^-exponent fits into a double. *result = static_cast(ReadUint64(trimmed, &read_digits)); - ASSERT(read_digits == trimmed.length()); + DOUBLE_CONVERSION_ASSERT(read_digits == trimmed.length()); *result /= exact_powers_of_ten[-exponent]; return true; } if (0 <= exponent && exponent < kExactPowersOfTenSize) { // 10^exponent fits into a double. *result = static_cast(ReadUint64(trimmed, &read_digits)); - ASSERT(read_digits == trimmed.length()); + DOUBLE_CONVERSION_ASSERT(read_digits == trimmed.length()); *result *= exact_powers_of_ten[exponent]; return true; } @@ -236,34 +236,35 @@ static bool DoubleStrtod(Vector trimmed, // 10^remaining_digits. As a result the remaining exponent now fits // into a double too. *result = static_cast(ReadUint64(trimmed, &read_digits)); - ASSERT(read_digits == trimmed.length()); + DOUBLE_CONVERSION_ASSERT(read_digits == trimmed.length()); *result *= exact_powers_of_ten[remaining_digits]; *result *= exact_powers_of_ten[exponent - remaining_digits]; return true; } } return false; +#endif } // Returns 10^exponent as an exact DiyFp. // The given exponent must be in the range [1; kDecimalExponentDistance[. static DiyFp AdjustmentPowerOfTen(int exponent) { - ASSERT(0 < exponent); - ASSERT(exponent < PowersOfTenCache::kDecimalExponentDistance); + DOUBLE_CONVERSION_ASSERT(0 < exponent); + DOUBLE_CONVERSION_ASSERT(exponent < PowersOfTenCache::kDecimalExponentDistance); // Simply hardcode the remaining powers for the given decimal exponent // distance. - ASSERT(PowersOfTenCache::kDecimalExponentDistance == 8); + DOUBLE_CONVERSION_ASSERT(PowersOfTenCache::kDecimalExponentDistance == 8); switch (exponent) { - case 1: return DiyFp(UINT64_2PART_C(0xa0000000, 00000000), -60); - case 2: return DiyFp(UINT64_2PART_C(0xc8000000, 00000000), -57); - case 3: return DiyFp(UINT64_2PART_C(0xfa000000, 00000000), -54); - case 4: return DiyFp(UINT64_2PART_C(0x9c400000, 00000000), -50); - case 5: return DiyFp(UINT64_2PART_C(0xc3500000, 00000000), -47); - case 6: return DiyFp(UINT64_2PART_C(0xf4240000, 00000000), -44); - case 7: return DiyFp(UINT64_2PART_C(0x98968000, 00000000), -40); + case 1: return DiyFp(DOUBLE_CONVERSION_UINT64_2PART_C(0xa0000000, 00000000), -60); + case 2: return DiyFp(DOUBLE_CONVERSION_UINT64_2PART_C(0xc8000000, 00000000), -57); + case 3: return DiyFp(DOUBLE_CONVERSION_UINT64_2PART_C(0xfa000000, 00000000), -54); + case 4: return DiyFp(DOUBLE_CONVERSION_UINT64_2PART_C(0x9c400000, 00000000), -50); + case 5: return DiyFp(DOUBLE_CONVERSION_UINT64_2PART_C(0xc3500000, 00000000), -47); + case 6: return DiyFp(DOUBLE_CONVERSION_UINT64_2PART_C(0xf4240000, 00000000), -44); + case 7: return DiyFp(DOUBLE_CONVERSION_UINT64_2PART_C(0x98968000, 00000000), -40); default: - UNREACHABLE(); + DOUBLE_CONVERSION_UNREACHABLE(); } } @@ -292,7 +293,7 @@ static bool DiyFpStrtod(Vector buffer, input.Normalize(); error <<= old_e - input.e(); - ASSERT(exponent <= PowersOfTenCache::kMaxDecimalExponent); + DOUBLE_CONVERSION_ASSERT(exponent <= PowersOfTenCache::kMaxDecimalExponent); if (exponent < PowersOfTenCache::kMinDecimalExponent) { *result = 0.0; return true; @@ -310,7 +311,7 @@ static bool DiyFpStrtod(Vector buffer, if (kMaxUint64DecimalDigits - buffer.length() >= adjustment_exponent) { // The product of input with the adjustment power fits into a 64 bit // integer. - ASSERT(DiyFp::kSignificandSize == 64); + DOUBLE_CONVERSION_ASSERT(DiyFp::kSignificandSize == 64); } else { // The adjustment power is exact. There is hence only an error of 0.5. error += kDenominator / 2; @@ -352,8 +353,8 @@ static bool DiyFpStrtod(Vector buffer, precision_digits_count -= shift_amount; } // We use uint64_ts now. This only works if the DiyFp uses uint64_ts too. - ASSERT(DiyFp::kSignificandSize == 64); - ASSERT(precision_digits_count < 64); + DOUBLE_CONVERSION_ASSERT(DiyFp::kSignificandSize == 64); + DOUBLE_CONVERSION_ASSERT(precision_digits_count < 64); uint64_t one64 = 1; uint64_t precision_bits_mask = (one64 << precision_digits_count) - 1; uint64_t precision_bits = input.f() & precision_bits_mask; @@ -392,14 +393,14 @@ static bool DiyFpStrtod(Vector buffer, static int CompareBufferWithDiyFp(Vector buffer, int exponent, DiyFp diy_fp) { - ASSERT(buffer.length() + exponent <= kMaxDecimalPower + 1); - ASSERT(buffer.length() + exponent > kMinDecimalPower); - ASSERT(buffer.length() <= kMaxSignificantDecimalDigits); + DOUBLE_CONVERSION_ASSERT(buffer.length() + exponent <= kMaxDecimalPower + 1); + DOUBLE_CONVERSION_ASSERT(buffer.length() + exponent > kMinDecimalPower); + DOUBLE_CONVERSION_ASSERT(buffer.length() <= kMaxSignificantDecimalDigits); // Make sure that the Bignum will be able to hold all our numbers. // Our Bignum implementation has a separate field for exponents. Shifts will // consume at most one bigit (< 64 bits). // ln(10) == 3.3219... - ASSERT(((kMaxDecimalPower + 1) * 333 / 100) < Bignum::kMaxSignificantBits); + DOUBLE_CONVERSION_ASSERT(((kMaxDecimalPower + 1) * 333 / 100) < Bignum::kMaxSignificantBits); Bignum buffer_bignum; Bignum diy_fp_bignum; buffer_bignum.AssignDecimalString(buffer); @@ -445,18 +446,31 @@ static bool ComputeGuess(Vector trimmed, int exponent, return false; } -double Strtod(Vector buffer, int exponent) { - char copy_buffer[kMaxSignificantDecimalDigits]; - Vector trimmed; - int updated_exponent; - TrimAndCut(buffer, exponent, copy_buffer, kMaxSignificantDecimalDigits, - &trimmed, &updated_exponent); - exponent = updated_exponent; +static bool IsDigit(const char d) { + return ('0' <= d) && (d <= '9'); +} - double guess; - bool is_correct = ComputeGuess(trimmed, exponent, &guess); - if (is_correct) return guess; +static bool IsNonZeroDigit(const char d) { + return ('1' <= d) && (d <= '9'); +} + +static bool AssertTrimmedDigits(const Vector& buffer) { + for(int i = 0; i < buffer.length(); ++i) { + if(!IsDigit(buffer[i])) { + return false; + } + } + return (buffer.length() == 0) || (IsNonZeroDigit(buffer[0]) && IsNonZeroDigit(buffer[buffer.length()-1])); +} +double StrtodTrimmed(Vector trimmed, int exponent) { + DOUBLE_CONVERSION_ASSERT(trimmed.length() <= kMaxSignificantDecimalDigits); + DOUBLE_CONVERSION_ASSERT(AssertTrimmedDigits(trimmed)); + double guess; + const bool is_correct = ComputeGuess(trimmed, exponent, &guess); + if (is_correct) { + return guess; + } DiyFp upper_boundary = Double(guess).UpperBoundary(); int comparison = CompareBufferWithDiyFp(trimmed, exponent, upper_boundary); if (comparison < 0) { @@ -471,6 +485,39 @@ double Strtod(Vector buffer, int exponent) { } } +double Strtod(Vector buffer, int exponent) { + char copy_buffer[kMaxSignificantDecimalDigits]; + Vector trimmed; + int updated_exponent; + TrimAndCut(buffer, exponent, copy_buffer, kMaxSignificantDecimalDigits, + &trimmed, &updated_exponent); + return StrtodTrimmed(trimmed, updated_exponent); +} + +static float SanitizedDoubletof(double d) { + DOUBLE_CONVERSION_ASSERT(d >= 0.0); + // ASAN has a sanitize check that disallows casting doubles to floats if + // they are too big. + // https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html#available-checks + // The behavior should be covered by IEEE 754, but some projects use this + // flag, so work around it. + float max_finite = 3.4028234663852885981170418348451692544e+38; + // The half-way point between the max-finite and infinity value. + // Since infinity has an even significand everything equal or greater than + // this value should become infinity. + double half_max_finite_infinity = + 3.40282356779733661637539395458142568448e+38; + if (d >= max_finite) { + if (d >= half_max_finite_infinity) { + return Single::Infinity(); + } else { + return max_finite; + } + } else { + return static_cast(d); + } +} + float Strtof(Vector buffer, int exponent) { char copy_buffer[kMaxSignificantDecimalDigits]; Vector trimmed; @@ -482,7 +529,7 @@ float Strtof(Vector buffer, int exponent) { double double_guess; bool is_correct = ComputeGuess(trimmed, exponent, &double_guess); - float float_guess = static_cast(double_guess); + float float_guess = SanitizedDoubletof(double_guess); if (float_guess == double_guess) { // This shortcut triggers for integer values. return float_guess; @@ -505,18 +552,18 @@ float Strtof(Vector buffer, int exponent) { double double_next = Double(double_guess).NextDouble(); double double_previous = Double(double_guess).PreviousDouble(); - float f1 = static_cast(double_previous); + float f1 = SanitizedDoubletof(double_previous); float f2 = float_guess; - float f3 = static_cast(double_next); + float f3 = SanitizedDoubletof(double_next); float f4; if (is_correct) { f4 = f3; } else { double double_next2 = Double(double_next).NextDouble(); - f4 = static_cast(double_next2); + f4 = SanitizedDoubletof(double_next2); } (void) f2; // Mark variable as used. - ASSERT(f1 <= f2 && f2 <= f3 && f3 <= f4); + DOUBLE_CONVERSION_ASSERT(f1 <= f2 && f2 <= f3 && f3 <= f4); // If the guess doesn't lie near a single-precision boundary we can simply // return its float-value. @@ -524,11 +571,11 @@ float Strtof(Vector buffer, int exponent) { return float_guess; } - ASSERT((f1 != f2 && f2 == f3 && f3 == f4) || + DOUBLE_CONVERSION_ASSERT((f1 != f2 && f2 == f3 && f3 == f4) || (f1 == f2 && f2 != f3 && f3 == f4) || (f1 == f2 && f2 == f3 && f3 != f4)); - // guess and next are the two possible canditates (in the same way that + // guess and next are the two possible candidates (in the same way that // double_guess was the lower candidate for a double-precision guess). float guess = f1; float next = f4; diff --git a/double-conversion/strtod.h b/double-conversion/strtod.h index ed0293b8..ff0ee470 100644 --- a/double-conversion/strtod.h +++ b/double-conversion/strtod.h @@ -40,6 +40,11 @@ double Strtod(Vector buffer, int exponent); // contain a dot or a sign. It must not start with '0', and must not be empty. float Strtof(Vector buffer, int exponent); +// For special use cases, the heart of the Strtod() function is also available +// separately, it assumes that 'trimmed' is as produced by TrimAndCut(), i.e. +// no leading or trailing zeros, also no lone zero, and not 'too many' digits. +double StrtodTrimmed(Vector trimmed, int exponent); + } // namespace double_conversion #endif // DOUBLE_CONVERSION_STRTOD_H_ diff --git a/double-conversion/utils.h b/double-conversion/utils.h index 51d5e61e..221467ff 100644 --- a/double-conversion/utils.h +++ b/double-conversion/utils.h @@ -28,16 +28,16 @@ #ifndef DOUBLE_CONVERSION_UTILS_H_ #define DOUBLE_CONVERSION_UTILS_H_ -#include -#include +#include +#include -#include -#ifndef ASSERT -#define ASSERT(condition) \ +#include +#ifndef DOUBLE_CONVERSION_ASSERT +#define DOUBLE_CONVERSION_ASSERT(condition) \ assert(condition); #endif -#ifndef UNIMPLEMENTED -#define UNIMPLEMENTED() (abort()) +#ifndef DOUBLE_CONVERSION_UNIMPLEMENTED +#define DOUBLE_CONVERSION_UNIMPLEMENTED() (abort()) #endif #ifndef DOUBLE_CONVERSION_NO_RETURN #ifdef _MSC_VER @@ -46,16 +46,23 @@ #define DOUBLE_CONVERSION_NO_RETURN __attribute__((noreturn)) #endif #endif -#ifndef UNREACHABLE +#ifndef DOUBLE_CONVERSION_UNREACHABLE #ifdef _MSC_VER void DOUBLE_CONVERSION_NO_RETURN abort_noreturn(); inline void abort_noreturn() { abort(); } -#define UNREACHABLE() (abort_noreturn()) +#define DOUBLE_CONVERSION_UNREACHABLE() (abort_noreturn()) #else -#define UNREACHABLE() (abort()) +#define DOUBLE_CONVERSION_UNREACHABLE() (abort()) #endif #endif +#ifndef DOUBLE_CONVERSION_UNUSED +#ifdef __GNUC__ +#define DOUBLE_CONVERSION_UNUSED __attribute__((unused)) +#else +#define DOUBLE_CONVERSION_UNUSED +#endif +#endif // Double operations detection based on target architecture. // Linux uses a 80bit wide floating point stack on x86. This induces double @@ -67,18 +74,39 @@ inline void abort_noreturn() { abort(); } // the output of the division with the expected result. (Inlining must be // disabled.) // On Linux,x86 89255e-22 != Div_double(89255.0/1e22) +// +// For example: +/* +// -- in div.c +double Div_double(double x, double y) { return x / y; } + +// -- in main.c +double Div_double(double x, double y); // Forward declaration. + +int main(int argc, char** argv) { + return Div_double(89255.0, 1e22) == 89255e-22; +} +*/ +// Run as follows ./main || echo "correct" +// +// If it prints "correct" then the architecture should be here, in the "correct" section. #if defined(_M_X64) || defined(__x86_64__) || \ - defined(__ARMEL__) || defined(__avr32__) || \ + defined(__ARMEL__) || defined(__avr32__) || defined(_M_ARM) || defined(_M_ARM64) || \ defined(__hppa__) || defined(__ia64__) || \ defined(__mips__) || \ defined(__powerpc__) || defined(__ppc__) || defined(__ppc64__) || \ defined(_POWER) || defined(_ARCH_PPC) || defined(_ARCH_PPC64) || \ defined(__sparc__) || defined(__sparc) || defined(__s390__) || \ defined(__SH4__) || defined(__alpha__) || \ - defined(_MIPS_ARCH_MIPS32R2) || \ - defined(__AARCH64EL__) || defined(__aarch64__) + defined(_MIPS_ARCH_MIPS32R2) || defined(__ARMEB__) ||\ + defined(__AARCH64EL__) || defined(__aarch64__) || defined(__AARCH64EB__) || \ + defined(__riscv) || defined(__e2k__) || \ + defined(__or1k__) || defined(__arc__) || \ + defined(__microblaze__) || \ + defined(__EMSCRIPTEN__) #define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1 -#elif defined(__mc68000__) +#elif defined(__mc68000__) || \ + defined(__pnacl__) || defined(__native_client__) #undef DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS #elif defined(_M_IX86) || defined(__i386__) || defined(__i386) #if defined(_WIN32) @@ -91,12 +119,6 @@ inline void abort_noreturn() { abort(); } #error Target architecture was not detected as supported by Double-Conversion. #endif -#if defined(__GNUC__) -#define DOUBLE_CONVERSION_UNUSED __attribute__((unused)) -#else -#define DOUBLE_CONVERSION_UNUSED -#endif - #if defined(_WIN32) && !defined(__MINGW32__) typedef signed char int8_t; @@ -119,24 +141,24 @@ typedef uint16_t uc16; // The following macro works on both 32 and 64-bit platforms. // Usage: instead of writing 0x1234567890123456 -// write UINT64_2PART_C(0x12345678,90123456); -#define UINT64_2PART_C(a, b) (((static_cast(a) << 32) + 0x##b##u)) +// write DOUBLE_CONVERSION_UINT64_2PART_C(0x12345678,90123456); +#define DOUBLE_CONVERSION_UINT64_2PART_C(a, b) (((static_cast(a) << 32) + 0x##b##u)) -// The expression ARRAY_SIZE(a) is a compile-time constant of type +// The expression DOUBLE_CONVERSION_ARRAY_SIZE(a) is a compile-time constant of type // size_t which represents the number of elements of the given -// array. You should only use ARRAY_SIZE on statically allocated +// array. You should only use DOUBLE_CONVERSION_ARRAY_SIZE on statically allocated // arrays. -#ifndef ARRAY_SIZE -#define ARRAY_SIZE(a) \ +#ifndef DOUBLE_CONVERSION_ARRAY_SIZE +#define DOUBLE_CONVERSION_ARRAY_SIZE(a) \ ((sizeof(a) / sizeof(*(a))) / \ static_cast(!(sizeof(a) % sizeof(*(a))))) #endif // A macro to disallow the evil copy constructor and operator= functions // This should be used in the private: declarations for a class -#ifndef DISALLOW_COPY_AND_ASSIGN -#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ +#ifndef DOUBLE_CONVERSION_DISALLOW_COPY_AND_ASSIGN +#define DOUBLE_CONVERSION_DISALLOW_COPY_AND_ASSIGN(TypeName) \ TypeName(const TypeName&); \ void operator=(const TypeName&) #endif @@ -147,33 +169,17 @@ typedef uint16_t uc16; // This should be used in the private: declarations for a class // that wants to prevent anyone from instantiating it. This is // especially useful for classes containing only static methods. -#ifndef DISALLOW_IMPLICIT_CONSTRUCTORS -#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ +#ifndef DOUBLE_CONVERSION_DISALLOW_IMPLICIT_CONSTRUCTORS +#define DOUBLE_CONVERSION_DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ TypeName(); \ - DISALLOW_COPY_AND_ASSIGN(TypeName) + DOUBLE_CONVERSION_DISALLOW_COPY_AND_ASSIGN(TypeName) #endif namespace double_conversion { -static const int kCharSize = sizeof(char); - -// Returns the maximum of the two parameters. -template -static T Max(T a, T b) { - return a < b ? b : a; -} - - -// Returns the minimum of the two parameters. -template -static T Min(T a, T b) { - return a < b ? a : b; -} - - inline int StrLength(const char* string) { size_t length = strlen(string); - ASSERT(length == static_cast(static_cast(length))); + DOUBLE_CONVERSION_ASSERT(length == static_cast(static_cast(length))); return static_cast(length); } @@ -183,15 +189,15 @@ class Vector { public: Vector() : start_(NULL), length_(0) {} Vector(T* data, int len) : start_(data), length_(len) { - ASSERT(len == 0 || (len > 0 && data != NULL)); + DOUBLE_CONVERSION_ASSERT(len == 0 || (len > 0 && data != NULL)); } // Returns a vector using the same backing storage as this one, // spanning from and including 'from', to but not including 'to'. Vector SubVector(int from, int to) { - ASSERT(to <= length_); - ASSERT(from < to); - ASSERT(0 <= from); + DOUBLE_CONVERSION_ASSERT(to <= length_); + DOUBLE_CONVERSION_ASSERT(from < to); + DOUBLE_CONVERSION_ASSERT(0 <= from); return Vector(start() + from, to - from); } @@ -206,7 +212,7 @@ class Vector { // Access individual vector elements - checks bounds in debug mode. T& operator[](int index) const { - ASSERT(0 <= index && index < length_); + DOUBLE_CONVERSION_ASSERT(0 <= index && index < length_); return start_[index]; } @@ -214,6 +220,11 @@ class Vector { T& last() { return start_[length_ - 1]; } + void pop_back() { + DOUBLE_CONVERSION_ASSERT(!is_empty()); + --length_; + } + private: T* start_; int length_; @@ -234,7 +245,7 @@ class StringBuilder { // Get the current position in the builder. int position() const { - ASSERT(!is_finalized()); + DOUBLE_CONVERSION_ASSERT(!is_finalized()); return position_; } @@ -245,8 +256,8 @@ class StringBuilder { // 0-characters; use the Finalize() method to terminate the string // instead. void AddCharacter(char c) { - ASSERT(c != '\0'); - ASSERT(!is_finalized() && position_ < buffer_.length()); + DOUBLE_CONVERSION_ASSERT(c != '\0'); + DOUBLE_CONVERSION_ASSERT(!is_finalized() && position_ < buffer_.length()); buffer_[position_++] = c; } @@ -259,9 +270,9 @@ class StringBuilder { // Add the first 'n' characters of the given string 's' to the // builder. The input string must have enough characters. void AddSubstring(const char* s, int n) { - ASSERT(!is_finalized() && position_ + n < buffer_.length()); - ASSERT(static_cast(n) <= strlen(s)); - memmove(&buffer_[position_], s, n * kCharSize); + DOUBLE_CONVERSION_ASSERT(!is_finalized() && position_ + n < buffer_.length()); + DOUBLE_CONVERSION_ASSERT(static_cast(n) <= strlen(s)); + memmove(&buffer_[position_], s, n); position_ += n; } @@ -276,13 +287,13 @@ class StringBuilder { // Finalize the string by 0-terminating it and returning the buffer. char* Finalize() { - ASSERT(!is_finalized() && position_ < buffer_.length()); + DOUBLE_CONVERSION_ASSERT(!is_finalized() && position_ < buffer_.length()); buffer_[position_] = '\0'; // Make sure nobody managed to add a 0-character to the // buffer while building the string. - ASSERT(strlen(buffer_.start()) == static_cast(position_)); + DOUBLE_CONVERSION_ASSERT(strlen(buffer_.start()) == static_cast(position_)); position_ = -1; - ASSERT(is_finalized()); + DOUBLE_CONVERSION_ASSERT(is_finalized()); return buffer_.start(); } @@ -292,7 +303,7 @@ class StringBuilder { bool is_finalized() const { return position_ < 0; } - DISALLOW_IMPLICIT_CONSTRUCTORS(StringBuilder); + DOUBLE_CONVERSION_DISALLOW_IMPLICIT_CONSTRUCTORS(StringBuilder); }; // The type-based aliasing rule allows the compiler to assume that pointers of @@ -320,11 +331,16 @@ class StringBuilder { // enough that it can no longer see that you have cast one pointer type to // another thus avoiding the warning. template -inline Dest BitCast(const Source& source) { +Dest BitCast(const Source& source) { // Compile time assertion: sizeof(Dest) == sizeof(Source) // A compile error here means your Dest and Source have different sizes. +#if __cplusplus >= 201103L + static_assert(sizeof(Dest) == sizeof(Source), + "source and destination size mismatch"); +#else DOUBLE_CONVERSION_UNUSED - typedef char VerifySizesAreEqual[sizeof(Dest) == sizeof(Source) ? 1 : -1]; + typedef char VerifySizesAreEqual[sizeof(Dest) == sizeof(Source) ? 1 : -1]; +#endif Dest dest; memmove(&dest, &source, sizeof(dest)); @@ -332,7 +348,7 @@ inline Dest BitCast(const Source& source) { } template -inline Dest BitCast(Source* source) { +Dest BitCast(Source* source) { return BitCast(reinterpret_cast(source)); } diff --git a/double-conversionBuildTreeSettings.cmake.in b/double-conversionBuildTreeSettings.cmake.in deleted file mode 100644 index f46705d6..00000000 --- a/double-conversionBuildTreeSettings.cmake.in +++ /dev/null @@ -1,2 +0,0 @@ -set(double-conversion_INCLUDE_DIRS - "@PROJECT_SOURCE_DIR@/src") diff --git a/double-conversionConfig.cmake.in b/double-conversionConfig.cmake.in deleted file mode 100644 index 110df446..00000000 --- a/double-conversionConfig.cmake.in +++ /dev/null @@ -1,16 +0,0 @@ -# - Config file for the double-conversion package -# It defines the following variables -# double-conversion_INCLUDE_DIRS -# double-conversion_LIBRARIES - -get_filename_component(double-conversion_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) - -if(EXISTS "${double-conversion_CMAKE_DIR}/CMakeCache.txt") - include("${double-conversion_CMAKE_DIR}/double-conversionBuildTreeSettings.cmake") -else() - set(double-conversion_INCLUDE_DIRS "@CMAKE_INSTALL_FULL_INCLUDEDIR@/double-conversion") -endif() - -include("@CMAKE_INSTALL_FULL_LIBDIR@/cmake/double-conversion/double-conversionLibraryDepends.cmake") - -set(double-conversion_LIBRARIES double-conversion) diff --git a/double-conversionConfigVersion.cmake.in b/double-conversionConfigVersion.cmake.in deleted file mode 100644 index 0f2295d2..00000000 --- a/double-conversionConfigVersion.cmake.in +++ /dev/null @@ -1,11 +0,0 @@ -set(PACKAGE_VERSION "@double-conversion_VERSION@") - -# Check whether the requested PACKAGE_FIND_VERSION is compatible -if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}") - set(PACKAGE_VERSION_COMPATIBLE FALSE) -else() - set(PACKAGE_VERSION_COMPATIBLE TRUE) - if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}") - set(PACKAGE_VERSION_EXACT TRUE) - endif() -endif() diff --git a/test/cctest/cctest.cc b/test/cctest/cctest.cc index 6d85bfdf..b6e82b4a 100644 --- a/test/cctest/cctest.cc +++ b/test/cctest/cctest.cc @@ -115,7 +115,7 @@ int main(int argc, char* argv[]) { test = test->prev(); } } - delete[] arg_copy; + free(arg_copy); } } if (print_run_count && tests_run != 1) diff --git a/test/cctest/cctest.h b/test/cctest/cctest.h index cb87076f..bd157a6d 100644 --- a/test/cctest/cctest.h +++ b/test/cctest/cctest.h @@ -75,8 +75,11 @@ static inline void CheckEqualsHelper(const char* file, int line, const char* value_source, const char* value) { if ((expected == NULL && value != NULL) || - (expected != NULL && value == NULL) || - (expected != NULL && value != NULL && strcmp(expected, value) != 0)) { + (expected != NULL && value == NULL)) { + abort(); + } + + if ((expected != NULL && value != NULL && strcmp(expected, value) != 0)) { printf("%s:%d:\n CHECK_EQ(%s, %s) failed\n" "# Expected: %s\n" "# Found: %s\n", @@ -124,10 +127,10 @@ class CcTest { static int test_count(); static CcTest* last() { return last_; } CcTest* prev() { return prev_; } - const char* file() { return file_; } - const char* name() { return name_; } - const char* dependency() { return dependency_; } - bool enabled() { return enabled_; } + const char* file() const { return file_; } + const char* name() const { return name_; } + const char* dependency() const { return dependency_; } + bool enabled() const { return enabled_; } private: TestFunction* callback_; const char* file_; diff --git a/test/cctest/checks.h b/test/cctest/checks.h index 5ea59920..4b0a7032 100644 --- a/test/cctest/checks.h +++ b/test/cctest/checks.h @@ -35,21 +35,21 @@ extern "C" void V8_Fatal(const char* file, int line, const char* format, ...); void API_Fatal(const char* location, const char* format, ...); -// The FATAL, UNREACHABLE and UNIMPLEMENTED macros are useful during +// The FATAL, DOUBLE_CONVERSION_UNREACHABLE and DOUBLE_CONVERSION_UNIMPLEMENTED macros are useful during // development, but they should not be relied on in the final product. #ifdef DEBUG #define FATAL(msg) \ V8_Fatal(__FILE__, __LINE__, "%s", (msg)) -#define UNIMPLEMENTED() \ +#define DOUBLE_CONVERSION_UNIMPLEMENTED() \ V8_Fatal(__FILE__, __LINE__, "unimplemented code") -#define UNREACHABLE() \ +#define DOUBLE_CONVERSION_UNREACHABLE() \ V8_Fatal(__FILE__, __LINE__, "unreachable code") #else #define FATAL(msg) \ V8_Fatal("", 0, "%s", (msg)) -#define UNIMPLEMENTED() \ +#define DOUBLE_CONVERSION_UNIMPLEMENTED() \ V8_Fatal("", 0, "unimplemented code") -#define UNREACHABLE() ((void) 0) +#define DOUBLE_CONVERSION_UNREACHABLE() ((void) 0) #endif @@ -279,36 +279,36 @@ template class StaticAssertionHelper { }; SEMI_STATIC_JOIN(__StaticAssertTypedef__, __LINE__) -// The ASSERT macro is equivalent to CHECK except that it only +// The DOUBLE_CONVERSION_ASSERT macro is equivalent to CHECK except that it only // generates code in debug builds. #ifdef DEBUG -#define ASSERT_RESULT(expr) CHECK(expr) -#define ASSERT(condition) CHECK(condition) -#define ASSERT_EQ(v1, v2) CHECK_EQ(v1, v2) -#define ASSERT_NE(v1, v2) CHECK_NE(v1, v2) -#define ASSERT_GE(v1, v2) CHECK_GE(v1, v2) -#define SLOW_ASSERT(condition) if (FLAG_enable_slow_asserts) CHECK(condition) +#define DOUBLE_CONVERSION_ASSERT_RESULT(expr) CHECK(expr) +#define DOUBLE_CONVERSION_ASSERT(condition) CHECK(condition) +#define DOUBLE_CONVERSION_ASSERT_EQ(v1, v2) CHECK_EQ(v1, v2) +#define DOUBLE_CONVERSION_ASSERT_NE(v1, v2) CHECK_NE(v1, v2) +#define DOUBLE_CONVERSION_ASSERT_GE(v1, v2) CHECK_GE(v1, v2) +#define SLOW_DOUBLE_CONVERSION_ASSERT(condition) if (FLAG_enable_slow_asserts) CHECK(condition) #else -#define ASSERT_RESULT(expr) (expr) -#define ASSERT(condition) ((void) 0) -#define ASSERT_EQ(v1, v2) ((void) 0) -#define ASSERT_NE(v1, v2) ((void) 0) -#define ASSERT_GE(v1, v2) ((void) 0) -#define SLOW_ASSERT(condition) ((void) 0) +#define DOUBLE_CONVERSION_ASSERT_RESULT(expr) (expr) +#define DOUBLE_CONVERSION_ASSERT(condition) ((void) 0) +#define DOUBLE_CONVERSION_ASSERT_EQ(v1, v2) ((void) 0) +#define DOUBLE_CONVERSION_ASSERT_NE(v1, v2) ((void) 0) +#define DOUBLE_CONVERSION_ASSERT_GE(v1, v2) ((void) 0) +#define SLOW_DOUBLE_CONVERSION_ASSERT(condition) ((void) 0) #endif // Static asserts has no impact on runtime performance, so they can be // safely enabled in release mode. Moreover, the ((void) 0) expression // obeys different syntax rules than typedef's, e.g. it can't appear // inside class declaration, this leads to inconsistency between debug // and release compilation modes behaviour. -#define STATIC_ASSERT(test) STATIC_CHECK(test) +#define STATIC_DOUBLE_CONVERSION_ASSERT(test) STATIC_CHECK(test) -#define ASSERT_TAG_ALIGNED(address) \ - ASSERT((reinterpret_cast(address) & kHeapObjectTagMask) == 0) +#define DOUBLE_CONVERSION_ASSERT_TAG_ALIGNED(address) \ + DOUBLE_CONVERSION_ASSERT((reinterpret_cast(address) & kHeapObjectTagMask) == 0) -#define ASSERT_SIZE_TAG_ALIGNED(size) ASSERT((size & kHeapObjectTagMask) == 0) +#define DOUBLE_CONVERSION_ASSERT_SIZE_TAG_ALIGNED(size) DOUBLE_CONVERSION_ASSERT((size & kHeapObjectTagMask) == 0) -#define ASSERT_NOT_NULL(p) ASSERT_NE(NULL, p) +#define DOUBLE_CONVERSION_ASSERT_NOT_NULL(p) DOUBLE_CONVERSION_ASSERT_NE(NULL, p) #endif // V8_CHECKS_H_ diff --git a/test/cctest/test-bignum-dtoa.cc b/test/cctest/test-bignum-dtoa.cc index 86494647..e2367d94 100644 --- a/test/cctest/test-bignum-dtoa.cc +++ b/test/cctest/test-bignum-dtoa.cc @@ -191,7 +191,12 @@ TEST(BignumDtoaVariousDoubles) { CHECK_EQ("35844466", buffer.start()); CHECK_EQ(299, point); - uint64_t smallest_normal64 = UINT64_2PART_C(0x00100000, 00000000); + BignumDtoa(1e-23, BIGNUM_DTOA_SHORTEST, 0, + buffer, &length, &point); + CHECK_EQ("1", buffer.start()); + CHECK_EQ(-22, point); + + uint64_t smallest_normal64 = DOUBLE_CONVERSION_UINT64_2PART_C(0x00100000, 00000000); double v = Double(smallest_normal64).value(); BignumDtoa(v, BIGNUM_DTOA_SHORTEST, 0, buffer, &length, &point); CHECK_EQ("22250738585072014", buffer.start()); @@ -203,7 +208,7 @@ TEST(BignumDtoaVariousDoubles) { CHECK_EQ("22250738585072013831", buffer.start()); CHECK_EQ(-307, point); - uint64_t largest_denormal64 = UINT64_2PART_C(0x000FFFFF, FFFFFFFF); + uint64_t largest_denormal64 = DOUBLE_CONVERSION_UINT64_2PART_C(0x000FFFFF, FFFFFFFF); v = Double(largest_denormal64).value(); BignumDtoa(v, BIGNUM_DTOA_SHORTEST, 0, buffer, &length, &point); CHECK_EQ("2225073858507201", buffer.start()); diff --git a/test/cctest/test-bignum.cc b/test/cctest/test-bignum.cc index 84c4a09f..1bf6a694 100644 --- a/test/cctest/test-bignum.cc +++ b/test/cctest/test-bignum.cc @@ -81,12 +81,12 @@ TEST(Assign) { CHECK(bignum.ToHexString(buffer, kBufferSize)); CHECK_EQ("12345678", buffer); - uint64_t big = UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF); + uint64_t big = DOUBLE_CONVERSION_UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF); bignum.AssignUInt64(big); CHECK(bignum.ToHexString(buffer, kBufferSize)); CHECK_EQ("FFFFFFFFFFFFFFFF", buffer); - big = UINT64_2PART_C(0x12345678, 9ABCDEF0); + big = DOUBLE_CONVERSION_UINT64_2PART_C(0x12345678, 9ABCDEF0); bignum.AssignUInt64(big); CHECK(bignum.ToHexString(buffer, kBufferSize)); CHECK_EQ("123456789ABCDEF0", buffer); @@ -206,49 +206,49 @@ TEST(AddUInt64) { CHECK_EQ("1000000000000000000000FFFF", buffer); AssignHexString(&bignum, "0"); - bignum.AddUInt64(UINT64_2PART_C(0xA, 00000000)); + bignum.AddUInt64(DOUBLE_CONVERSION_UINT64_2PART_C(0xA, 00000000)); CHECK(bignum.ToHexString(buffer, kBufferSize)); CHECK_EQ("A00000000", buffer); AssignHexString(&bignum, "1"); - bignum.AddUInt64(UINT64_2PART_C(0xA, 00000000)); + bignum.AddUInt64(DOUBLE_CONVERSION_UINT64_2PART_C(0xA, 00000000)); CHECK(bignum.ToHexString(buffer, kBufferSize)); CHECK_EQ("A00000001", buffer); AssignHexString(&bignum, "1"); - bignum.AddUInt64(UINT64_2PART_C(0x100, 00000000)); + bignum.AddUInt64(DOUBLE_CONVERSION_UINT64_2PART_C(0x100, 00000000)); CHECK(bignum.ToHexString(buffer, kBufferSize)); CHECK_EQ("10000000001", buffer); AssignHexString(&bignum, "1"); - bignum.AddUInt64(UINT64_2PART_C(0xFFFF, 00000000)); + bignum.AddUInt64(DOUBLE_CONVERSION_UINT64_2PART_C(0xFFFF, 00000000)); CHECK(bignum.ToHexString(buffer, kBufferSize)); CHECK_EQ("FFFF00000001", buffer); AssignHexString(&bignum, "FFFFFFF"); - bignum.AddUInt64(UINT64_2PART_C(0x1, 00000000)); + bignum.AddUInt64(DOUBLE_CONVERSION_UINT64_2PART_C(0x1, 00000000)); CHECK(bignum.ToHexString(buffer, kBufferSize)); CHECK_EQ("10FFFFFFF", buffer); AssignHexString(&bignum, "10000000000000000000000000000000000000000000"); - bignum.AddUInt64(UINT64_2PART_C(0xFFFF, 00000000)); + bignum.AddUInt64(DOUBLE_CONVERSION_UINT64_2PART_C(0xFFFF, 00000000)); CHECK(bignum.ToHexString(buffer, kBufferSize)); CHECK_EQ("10000000000000000000000000000000FFFF00000000", buffer); AssignHexString(&bignum, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"); - bignum.AddUInt64(UINT64_2PART_C(0x1, 00000000)); + bignum.AddUInt64(DOUBLE_CONVERSION_UINT64_2PART_C(0x1, 00000000)); CHECK(bignum.ToHexString(buffer, kBufferSize)); CHECK_EQ("1000000000000000000000000000000000000FFFFFFFF", buffer); bignum.AssignUInt16(0x1); bignum.ShiftLeft(100); - bignum.AddUInt64(UINT64_2PART_C(0x1, 00000000)); + bignum.AddUInt64(DOUBLE_CONVERSION_UINT64_2PART_C(0x1, 00000000)); CHECK(bignum.ToHexString(buffer, kBufferSize)); CHECK_EQ("10000000000000000100000000", buffer); bignum.AssignUInt16(0x1); bignum.ShiftLeft(100); - bignum.AddUInt64(UINT64_2PART_C(0xFFFF, 00000000)); + bignum.AddUInt64(DOUBLE_CONVERSION_UINT64_2PART_C(0xFFFF, 00000000)); CHECK(bignum.ToHexString(buffer, kBufferSize)); CHECK_EQ("10000000000000FFFF00000000", buffer); } @@ -314,9 +314,13 @@ TEST(AddBignum) { CHECK_EQ("10000000000001000000000000", buffer); other.ShiftLeft(64); - // other == "10000000000000000000000000000" + CHECK(other.ToHexString(buffer, kBufferSize)); + CHECK_EQ("10000000000000000000000000000", buffer); bignum.AssignUInt16(0x1); + CHECK(bignum.ToHexString(buffer, kBufferSize)); + CHECK_EQ("1", buffer); + bignum.AddBignum(other); CHECK(bignum.ToHexString(buffer, kBufferSize)); CHECK_EQ("10000000000000000000000000001", buffer); @@ -570,7 +574,7 @@ TEST(MultiplyUInt64) { CHECK_EQ("FFFF00000000000000", buffer); AssignHexString(&bignum, "100000000000000"); - bignum.MultiplyByUInt64(UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF)); + bignum.MultiplyByUInt64(DOUBLE_CONVERSION_UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF)); CHECK(bignum.ToHexString(buffer, kBufferSize)); CHECK_EQ("FFFFFFFFFFFFFFFF00000000000000", buffer); @@ -580,7 +584,7 @@ TEST(MultiplyUInt64) { CHECK_EQ("12333335552433", buffer); AssignHexString(&bignum, "1234567ABCD"); - bignum.MultiplyByUInt64(UINT64_2PART_C(0xFF, FFFFFFFF)); + bignum.MultiplyByUInt64(DOUBLE_CONVERSION_UINT64_2PART_C(0xFF, FFFFFFFF)); CHECK(bignum.ToHexString(buffer, kBufferSize)); CHECK_EQ("1234567ABCBDCBA985433", buffer); @@ -600,7 +604,7 @@ TEST(MultiplyUInt64) { CHECK_EQ("EFFFFFFFFFFFFFFF1", buffer); AssignHexString(&bignum, "FFFFFFFFFFFFFFFF"); - bignum.MultiplyByUInt64(UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF)); + bignum.MultiplyByUInt64(DOUBLE_CONVERSION_UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF)); CHECK(bignum.ToHexString(buffer, kBufferSize)); CHECK_EQ("FFFFFFFFFFFFFFFE0000000000000001", buffer); @@ -635,12 +639,12 @@ TEST(MultiplyUInt64) { bignum.AssignUInt16(0xFFFF); bignum.ShiftLeft(100); // "FFFF0 0000 0000 0000 0000 0000 0000" - bignum.MultiplyByUInt64(UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF)); + bignum.MultiplyByUInt64(DOUBLE_CONVERSION_UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF)); CHECK(bignum.ToHexString(buffer, kBufferSize)); CHECK_EQ("FFFEFFFFFFFFFFFF00010000000000000000000000000", buffer); AssignDecimalString(&bignum, "15611230384529777"); - bignum.MultiplyByUInt64(UINT64_2PART_C(0x8ac72304, 89e80000)); + bignum.MultiplyByUInt64(DOUBLE_CONVERSION_UINT64_2PART_C(0x8ac72304, 89e80000)); CHECK(bignum.ToHexString(buffer, kBufferSize)); CHECK_EQ("1E10EE4B11D15A7F3DE7F3C7680000", buffer); } diff --git a/test/cctest/test-conversions.cc b/test/cctest/test-conversions.cc index 42bca876..e723c95f 100644 --- a/test/cctest/test-conversions.cc +++ b/test/cctest/test-conversions.cc @@ -35,6 +35,10 @@ TEST(DoubleToShortest) { CHECK(dc.ToShortest(1e21, &builder)); CHECK_EQ("1e+21", builder.Finalize()); + builder.Reset(); + CHECK(dc.ToShortest(1e-23, &builder)); + CHECK_EQ("1e-23", builder.Finalize()); + builder.Reset(); CHECK(dc.ToShortest(1e20, &builder)); CHECK_EQ("100000000000000000000", builder.Finalize()); @@ -67,6 +71,90 @@ TEST(DoubleToShortest) { CHECK(dc.ToShortest(-0.0, &builder)); CHECK_EQ("0", builder.Finalize()); + // Test min_exponent_width + flags = DoubleToStringConverter::UNIQUE_ZERO | + DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN; + DoubleToStringConverter dcExpWidth2(flags, NULL, NULL, 'e', -4, 6, 0, 0, 2); + + builder.Reset(); + CHECK(dcExpWidth2.ToShortest(11111111111.0, &builder)); + CHECK_EQ("1.1111111111e+10", builder.Finalize()); + + builder.Reset(); + CHECK(dcExpWidth2.ToShortest(1111111111.0, &builder)); + CHECK_EQ("1.111111111e+09", builder.Finalize()); + + builder.Reset(); + CHECK(dcExpWidth2.ToShortest(1111111.0, &builder)); + CHECK_EQ("1.111111e+06", builder.Finalize()); + + builder.Reset(); + CHECK(dcExpWidth2.ToShortest(111111.0, &builder)); + CHECK_EQ("111111", builder.Finalize()); + + builder.Reset(); + CHECK(dcExpWidth2.ToShortest(10000000000.0, &builder)); + CHECK_EQ("1e+10", builder.Finalize()); + + builder.Reset(); + CHECK(dcExpWidth2.ToShortest(1000000000.0, &builder)); + CHECK_EQ("1e+09", builder.Finalize()); + + DoubleToStringConverter dcExpWidth0(flags, NULL, NULL, 'e', -4, 6, 0, 0, 0); + + builder.Reset(); + CHECK(dcExpWidth0.ToShortest(11111111111.0, &builder)); + CHECK_EQ("1.1111111111e+10", builder.Finalize()); + + builder.Reset(); + CHECK(dcExpWidth0.ToShortest(1111111111.0, &builder)); + CHECK_EQ("1.111111111e+9", builder.Finalize()); + + builder.Reset(); + CHECK(dcExpWidth0.ToShortest(1111111.0, &builder)); + CHECK_EQ("1.111111e+6", builder.Finalize()); + + builder.Reset(); + CHECK(dcExpWidth0.ToShortest(111111.0, &builder)); + CHECK_EQ("111111", builder.Finalize()); + + builder.Reset(); + CHECK(dcExpWidth0.ToShortest(10000000000.0, &builder)); + CHECK_EQ("1e+10", builder.Finalize()); + + builder.Reset(); + CHECK(dcExpWidth0.ToShortest(1000000000.0, &builder)); + CHECK_EQ("1e+9", builder.Finalize()); + + // Set min_exponent_width to 100 is equal to 5, + // as kMaxExponentLength is defined to 5 in double-to-string.cc + DoubleToStringConverter dcExpWidth100(flags, NULL, NULL, 'e', -4, 6, 0, 0, 100); + + builder.Reset(); + CHECK(dcExpWidth100.ToShortest(11111111111.0, &builder)); + CHECK_EQ("1.1111111111e+00010", builder.Finalize()); + + builder.Reset(); + CHECK(dcExpWidth100.ToShortest(1111111111.0, &builder)); + CHECK_EQ("1.111111111e+00009", builder.Finalize()); + + builder.Reset(); + CHECK(dcExpWidth100.ToShortest(1111111.0, &builder)); + CHECK_EQ("1.111111e+00006", builder.Finalize()); + + builder.Reset(); + CHECK(dcExpWidth100.ToShortest(111111.0, &builder)); + CHECK_EQ("111111", builder.Finalize()); + + builder.Reset(); + CHECK(dcExpWidth100.ToShortest(10000000000.0, &builder)); + CHECK_EQ("1e+00010", builder.Finalize()); + + builder.Reset(); + CHECK(dcExpWidth100.ToShortest(1000000000.0, &builder)); + CHECK_EQ("1e+00009", builder.Finalize()); + // End of min_exponent_width testing + flags = DoubleToStringConverter::NO_FLAGS; DoubleToStringConverter dc2(flags, NULL, NULL, 'e', -1, 1, 0, 0); builder.Reset(); @@ -348,8 +436,8 @@ TEST(DoubleToFixed) { CHECK(dc.ToFixed(-0.0, 1, &builder)); CHECK_EQ("0.0", builder.Finalize()); - ASSERT(DoubleToStringConverter::kMaxFixedDigitsBeforePoint == 60); - ASSERT(DoubleToStringConverter::kMaxFixedDigitsAfterPoint == 60); + DOUBLE_CONVERSION_ASSERT(DoubleToStringConverter::kMaxFixedDigitsBeforePoint == 60); + DOUBLE_CONVERSION_ASSERT(DoubleToStringConverter::kMaxFixedDigitsAfterPoint == 60); builder.Reset(); CHECK(dc.ToFixed( 0.0, DoubleToStringConverter::kMaxFixedDigitsAfterPoint, &builder)); @@ -634,7 +722,7 @@ TEST(DoubleToExponential) { CHECK(dc.ToExponential(-0.0, 2, &builder)); CHECK_EQ("0.00e+0", builder.Finalize()); - ASSERT(DoubleToStringConverter::kMaxExponentialDigits == 120); + DOUBLE_CONVERSION_ASSERT(DoubleToStringConverter::kMaxExponentialDigits == 120); builder.Reset(); CHECK(dc.ToExponential( 0.0, DoubleToStringConverter::kMaxExponentialDigits, &builder)); @@ -761,7 +849,7 @@ TEST(DoubleToPrecision) { 0, 0, // Padding zeroes for shortest mode. 6, 0); // Padding zeroes for precision mode. - ASSERT(DoubleToStringConverter::kMinPrecisionDigits == 1); + DOUBLE_CONVERSION_ASSERT(DoubleToStringConverter::kMinPrecisionDigits == 1); CHECK(dc.ToPrecision(0.0, 1, &builder)); CHECK_EQ("0", builder.Finalize()); @@ -777,7 +865,7 @@ TEST(DoubleToPrecision) { CHECK(dc.ToPrecision(-0.0, 2, &builder)); CHECK_EQ("0.0", builder.Finalize()); - ASSERT(DoubleToStringConverter::kMaxPrecisionDigits == 120); + DOUBLE_CONVERSION_ASSERT(DoubleToStringConverter::kMaxPrecisionDigits == 120); builder.Reset(); CHECK(dc.ToPrecision( 0.0, DoubleToStringConverter::kMaxPrecisionDigits, &builder)); @@ -1720,9 +1808,37 @@ TEST(DoubleToStringJavaScript) { static double StrToD16(const uc16* str16, int length, int flags, double empty_string_value, - int* processed_characters_count, bool* processed_all) { + int* processed_characters_count, bool* processed_all, + uc16 separator = StringToDoubleConverter::kNoSeparator) { StringToDoubleConverter converter(flags, empty_string_value, Double::NaN(), - NULL, NULL); + NULL, NULL, separator); + double result = + converter.StringToDouble(str16, length, processed_characters_count); + *processed_all = (length == *processed_characters_count); + return result; +} + + +static double StrToD16(const char* str, int flags, + double empty_string_value, + int* processed_characters_count, bool* processed_all, + char char_separator, uc16 separator) { + uc16 str16[256]; + int length = -1; + for (int i = 0;; i++) { + if (str[i] == char_separator) { + str16[i] = separator; + } else { + str16[i] = str[i]; + } + if (str[i] == '\0') { + length = i; + break; + } + } + DOUBLE_CONVERSION_ASSERT(length < 256); + StringToDoubleConverter converter(flags, empty_string_value, Double::NaN(), + NULL, NULL, separator); double result = converter.StringToDouble(str16, length, processed_characters_count); *processed_all = (length == *processed_characters_count); @@ -1731,16 +1847,17 @@ static double StrToD16(const uc16* str16, int length, int flags, static double StrToD(const char* str, int flags, double empty_string_value, - int* processed_characters_count, bool* processed_all) { + int* processed_characters_count, bool* processed_all, + uc16 separator = StringToDoubleConverter::kNoSeparator) { StringToDoubleConverter converter(flags, empty_string_value, Double::NaN(), - NULL, NULL); + NULL, NULL, separator); double result = converter.StringToDouble(str, strlen(str), processed_characters_count); *processed_all = ((strlen(str) == static_cast(*processed_characters_count))); uc16 buffer16[256]; - ASSERT(strlen(str) < ARRAY_SIZE(buffer16)); + DOUBLE_CONVERSION_ASSERT(strlen(str) < DOUBLE_CONVERSION_ARRAY_SIZE(buffer16)); int len = strlen(str); for (int i = 0; i < len; i++) { buffer16[i] = str[i]; @@ -1748,7 +1865,8 @@ static double StrToD(const char* str, int flags, double empty_string_value, int processed_characters_count16; bool processed_all16; double result16 = StrToD16(buffer16, len, flags, empty_string_value, - &processed_characters_count16, &processed_all16); + &processed_characters_count16, &processed_all16, + separator); CHECK_EQ(result, result16); CHECK_EQ(*processed_characters_count, processed_characters_count16); return result; @@ -1804,6 +1922,18 @@ TEST(StringToDoubleVarious) { CHECK_EQ(0, processed); + flags = StringToDoubleConverter::ALLOW_TRAILING_JUNK; + + CHECK_EQ(123.0, StrToD("123e", flags, 0.0, &processed, &all_used)); + CHECK_EQ(processed, 3); + + CHECK_EQ(123.0, StrToD("123e-", flags, 0.0, &processed, &all_used)); + CHECK_EQ(processed, 3); + + CHECK_EQ(123.0, StrToD("123e-a", flags, 0.0, &processed, &all_used)); + CHECK_EQ(processed, 3); + + flags = StringToDoubleConverter::ALLOW_LEADING_SPACES | StringToDoubleConverter::ALLOW_SPACES_AFTER_SIGN | StringToDoubleConverter::ALLOW_TRAILING_SPACES | @@ -2514,6 +2644,193 @@ TEST(StringToDoubleHexString) { CHECK_EQ(Double::NaN(), StrToD("x3", flags, 0.0, &processed, &all_used)); CHECK_EQ(0, processed); + + CHECK_EQ(-5.634002666912405e+27, StrToD("-0x123456789012345678901234", + flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(72057594037927940.0, StrToD("0x100000000000001", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(72057594037927940.0, StrToD("0x100000000000000", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(295147905179352830000.0, StrToD("0x100000000000000001", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(295147905179352830000.0, StrToD("0x100000000000000000", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(295147905179352900000.0, StrToD("0x100000000000008001", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(295147905179352830000.0, StrToD("0x100000000000008000", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(295147905179352960000.0, StrToD("0x100000000000018001", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(295147905179352960000.0, StrToD("0x100000000000018000", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + flags = StringToDoubleConverter::ALLOW_HEX_FLOATS; + + CHECK_EQ(3.0, StrToD("0x3p0", flags, 0.0, &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(0.0, StrToD("0x.0p0", flags, 0.0, &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(3.0, StrToD("0x3.0p0", flags, 0.0, &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(3.0, StrToD("0x3.p0", flags, 0.0, &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(-5.634002666912405e+27, StrToD("-0x123456789012345678901234p0", + flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(72057594037927940.0, StrToD("0x100000000000001p0", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(72057594037927940.0, StrToD("0x100000000000000p0", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(295147905179352830000.0, StrToD("0x100000000000000001p0", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(295147905179352830000.0, StrToD("0x100000000000000000p0", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(295147905179352900000.0, StrToD("0x100000000000008001p0", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(295147905179352830000.0, StrToD("0x100000000000008000p0", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(295147905179352960000.0, StrToD("0x100000000000018001p0", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(295147905179352960000.0, StrToD("0x100000000000018000p0", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(4.722366482869645e+21, StrToD("0x100000000000000001p4", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(4.722366482869645e+21, StrToD("0x100000000000000000p+4", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(4.722366482869646e+21, StrToD("0x100000000000008001p04", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(18446744073709552000.0, StrToD("0x100000000000008000p-4", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(18446744073709560000.0, StrToD("0x100000000000018001p-04", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(4.722366482869647e+21, StrToD("0x100000000000018000p4", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(Double::Infinity(), StrToD("0x1p2000", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(0.0, StrToD("0x1p-2000", flags, 0.0, &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(-0.0, StrToD("-0x1p-2000", flags, 0.0, &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(Double::NaN(), StrToD(" ", flags, Double::NaN(), + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("0x", flags, 0.0, + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD(" 0x ", flags, 0.0, + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD(" 0x 3", flags, 0.0, + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("0x3g", flags, 0.0, + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("x3", flags, 0.0, + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("0x3 foo", flags, 0.0, + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD(" 0x3 foo", flags, 0.0, + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("+ 0x3 foo", flags, 0.0, + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("+", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("-", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("- -0x5", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("- +0x5", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("+ +0x5", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("0xp1", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("0x.p1", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::Infinity(), StrToD("0x1.p10000000000000000", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(0.0, StrToD("0x1.p-10000000000000000", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); } @@ -3062,73 +3379,455 @@ TEST(StringToDoubleOctalString) { } -TEST(StringToDoubleSpecialValues) { +TEST(StringToDoubleSeparator) { + int flags; int processed; - int flags = StringToDoubleConverter::NO_FLAGS; + bool all_used; + uc16 separator; - { - // Use 1.0 as junk_string_value. - StringToDoubleConverter converter(flags, 0.0, 1.0, "infinity", "NaN"); + separator = '\''; + flags = StringToDoubleConverter::NO_FLAGS; - CHECK_EQ(Double::NaN(), converter.StringToDouble("+NaN", 4, &processed)); - CHECK_EQ(4, processed); + CHECK_EQ(1.0, StrToD("000'001.0'0", flags, 0.0, + &processed, &all_used, separator)); + CHECK(all_used); - CHECK_EQ(-Double::Infinity(), - converter.StringToDouble("-infinity", 9, &processed)); - CHECK_EQ(9, processed); + CHECK_EQ(1.0, StrToD("0'0'0'0'0'1.0'0", flags, 0.0, + &processed, &all_used, separator)); + CHECK(all_used); - CHECK_EQ(1.0, converter.StringToDouble("Infinity", 8, &processed)); - CHECK_EQ(0, processed); + CHECK_EQ(Double::NaN(), StrToD("'1.0", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); - CHECK_EQ(1.0, converter.StringToDouble("++NaN", 5, &processed)); - CHECK_EQ(0, processed); - } + CHECK_EQ(Double::NaN(), StrToD("1'.0", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); - { - // Use 1.0 as junk_string_value. - StringToDoubleConverter converter(flags, 0.0, 1.0, "+infinity", "1NaN"); + CHECK_EQ(Double::NaN(), StrToD("1.'0", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); - // The '+' is consumed before trying to match the infinity string. - CHECK_EQ(1.0, converter.StringToDouble("+infinity", 9, &processed)); - CHECK_EQ(0, processed); + CHECK_EQ(Double::NaN(), StrToD("0''1.0", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); - // The match for "1NaN" triggers, and doesn't let the 1234.0 complete. - CHECK_EQ(1.0, converter.StringToDouble("1234.0", 6, &processed)); - CHECK_EQ(0, processed); - } -} + CHECK_EQ(Double::NaN(), StrToD("1.0e1'0", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.0e1'", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + CHECK_EQ(Double::NaN(), StrToD("1.0e'1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); -TEST(StringToDoubleCommentExamples) { - // Make sure the examples in the comments are correct. - int flags; - int processed; - bool all_used; + CHECK_EQ(Double::NaN(), StrToD("1.0'e1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); - flags = StringToDoubleConverter::ALLOW_HEX; + CHECK_EQ(Double::NaN(), StrToD("+'1.0e1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); - CHECK_EQ(4660.0, StrToD("0x1234", flags, 0.0, &processed, &all_used)); - CHECK(all_used); + CHECK_EQ(Double::NaN(), StrToD("-'1.0e1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); - CHECK_EQ(Double::NaN(), - StrToD("0x1234.56", flags, 0.0, &processed, &all_used)); + CHECK_EQ(Double::NaN(), StrToD("1.0e+'1", flags, 0.0, + &processed, &all_used, separator)); CHECK_EQ(0, processed); - flags |= StringToDoubleConverter::ALLOW_TRAILING_JUNK; - CHECK_EQ(4660.0, - StrToD("0x1234.56", flags, 0.0, &processed, &all_used)); - CHECK_EQ(6, processed); + CHECK_EQ(Double::NaN(), StrToD("1.0e-'1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); - flags = StringToDoubleConverter::ALLOW_OCTALS; - CHECK_EQ(668.0, StrToD("01234", flags, 0.0, &processed, &all_used)); + CHECK_EQ(Double::NaN(), StrToD("1.0e'+1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.0e'-1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + separator = ' '; + flags = StringToDoubleConverter::NO_FLAGS; + + CHECK_EQ(1.0, StrToD("000 001.0 0", flags, 0.0, + &processed, &all_used, separator)); CHECK(all_used); - CHECK_EQ(12349.0, StrToD("012349", flags, 0.0, &processed, &all_used)); + CHECK_EQ(1.0, StrToD("0 0 0 0 0 1.0 0", flags, 0.0, + &processed, &all_used, separator)); CHECK(all_used); - CHECK_EQ(Double::NaN(), - StrToD("01234.56", flags, 0.0, &processed, &all_used)); - CHECK_EQ(processed, 0); + CHECK_EQ(Double::NaN(), StrToD(" 1.0", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1 .0", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1. 0", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("0 1.0", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.0e1 0", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.0e1 ", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.0e 1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.0 e1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("+ 1.0e1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("- 1.0e1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.0e+ 1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.0e- 1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.0e +1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.0e -1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + separator = ' '; + flags = StringToDoubleConverter::ALLOW_LEADING_SPACES | + StringToDoubleConverter::ALLOW_TRAILING_SPACES; + + CHECK_EQ(1.0, StrToD("000 001.0 0", flags, 0.0, + &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(1.0, StrToD("0 0 0 0 0 1.0 0", flags, 0.0, + &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(1.0, StrToD(" 000 001.0 0 ", flags, 0.0, + &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(1.0, StrToD(" 0 0 0 0 0 1.0 0 ", flags, 0.0, + &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(1.0, StrToD(" 1.0", flags, 0.0, + &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(Double::NaN(), StrToD("1 .0", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1. 0", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("0 1.0", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.0e1 0", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(10.0, StrToD("1.0e1 ", flags, 0.0, + &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(Double::NaN(), StrToD("1.0e 1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.0 e1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("+ 1.0e1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("- 1.0e1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.0e+ 1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.0e- 1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.0e +1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.0e -1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + separator = ' '; + flags = StringToDoubleConverter::ALLOW_HEX | + StringToDoubleConverter::ALLOW_HEX_FLOATS | + StringToDoubleConverter::ALLOW_LEADING_SPACES | + StringToDoubleConverter::ALLOW_TRAILING_SPACES; + + CHECK_EQ(18.0, StrToD("0x1 2", flags, 0.0, &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(0.0, StrToD("0x0 0", flags, 1.0, &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(static_cast(0x123456789), + StrToD("0x1 2 3 4 5 6 7 8 9", flags, Double::NaN(), + &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(18.0, StrToD(" 0x1 2 ", flags, 0.0, + &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(0.0, StrToD(" 0x0 ", flags, 1.0, + &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(static_cast(0x123456789), + StrToD(" 0x1 2 3 4 5 6 7 8 9 ", flags, Double::NaN(), + &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(static_cast(0xabcdef), + StrToD("0xa b c d e f", flags, 0.0, + &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(Double::NaN(), StrToD("0x 1 2", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("0 x0", flags, 1.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), + StrToD("0x1 2 3 4 5 6 7 8 9", flags, Double::NaN(), + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD(" 0 x1 2 ", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(3.0, + StrToD("0x0 3p0", flags, 0.0, &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(0.0, + StrToD("0x.0 0p0", flags, 0.0, &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(3.0, + StrToD("0x3.0 0p0", flags, 0.0, &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(3.0, + StrToD("0x0 3.p0", flags, 0.0, &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(Double::NaN(), + StrToD("0x 3p0", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), + StrToD("0x.0 p0", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), + StrToD("0x3.0p0 0", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), + StrToD("0x0 3.p 0", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), + StrToD("0x3p+ 0", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), + StrToD("0x.0p- 0", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), + StrToD("0x3.0p +0", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), + StrToD("0x0 3.p -0", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + separator = 0x202F; + char char_separator = '@'; + flags = StringToDoubleConverter::ALLOW_HEX | + StringToDoubleConverter::ALLOW_HEX_FLOATS | + StringToDoubleConverter::ALLOW_LEADING_SPACES | + StringToDoubleConverter::ALLOW_TRAILING_SPACES; + + CHECK_EQ(18.0, + StrToD16("0x1@2", flags, 0.0, &processed, &all_used, + char_separator, separator)); + CHECK(all_used); + + CHECK_EQ(0.0, StrToD16("0x0@0", flags, 1.0, &processed, &all_used, + char_separator, separator)); + CHECK(all_used); + + CHECK_EQ(static_cast(0x123456789), + StrToD16("0x1@2@3@4@5@6@7@8@9", flags, Double::NaN(), + &processed, &all_used, char_separator, separator)); + CHECK(all_used); + + CHECK_EQ(18.0, StrToD16(" 0x1@2 ", flags, 0.0, + &processed, &all_used, char_separator, separator)); + CHECK(all_used); + + CHECK_EQ(static_cast(0xabcdef), + StrToD16("0xa@b@c@d@e@f", flags, 0.0, + &processed, &all_used, char_separator, separator)); + CHECK(all_used); + + CHECK_EQ(Double::NaN(), + StrToD16("0x@1@2", flags, 0.0, + &processed, &all_used, char_separator, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), + StrToD16("0@x0", flags, 1.0, + &processed, &all_used, char_separator, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), + StrToD16("0x1@2@@3@4@5@6@7@8@9", flags, Double::NaN(), + &processed, &all_used, char_separator, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(3.0, + StrToD16("0x0@3p0", flags, 0.0, &processed, &all_used, + char_separator, separator)); + CHECK(all_used); + + CHECK_EQ(0.0, + StrToD16("0x.0@0p0", flags, 0.0, &processed, &all_used, + char_separator, separator)); + CHECK(all_used); + + CHECK_EQ(3.0, + StrToD16("0x3.0@0p0", flags, 0.0, &processed, &all_used, + char_separator, separator)); + CHECK(all_used); + + CHECK_EQ(3.0, + StrToD16("0x0@3.p0", flags, 0.0, &processed, &all_used, + char_separator, separator)); + CHECK(all_used); +} + +TEST(StringToDoubleSpecialValues) { + int processed; + int flags = StringToDoubleConverter::NO_FLAGS; + + { + // Use 1.0 as junk_string_value. + StringToDoubleConverter converter(flags, 0.0, 1.0, "infinity", "NaN"); + + CHECK_EQ(Double::NaN(), converter.StringToDouble("+NaN", 4, &processed)); + CHECK_EQ(4, processed); + + CHECK_EQ(-Double::Infinity(), + converter.StringToDouble("-infinity", 9, &processed)); + CHECK_EQ(9, processed); + + CHECK_EQ(1.0, converter.StringToDouble("Infinity", 8, &processed)); + CHECK_EQ(0, processed); + + CHECK_EQ(1.0, converter.StringToDouble("++NaN", 5, &processed)); + CHECK_EQ(0, processed); + } + + { + // Use 1.0 as junk_string_value. + StringToDoubleConverter converter(flags, 0.0, 1.0, "+infinity", "1NaN"); + + // The '+' is consumed before trying to match the infinity string. + CHECK_EQ(1.0, converter.StringToDouble("+infinity", 9, &processed)); + CHECK_EQ(0, processed); + + // The match for "1NaN" triggers, and doesn't let the 1234.0 complete. + CHECK_EQ(1.0, converter.StringToDouble("1234.0", 6, &processed)); + CHECK_EQ(0, processed); + } +} + + +TEST(StringToDoubleCommentExamples) { + // Make sure the examples in the comments are correct. + int flags; + int processed; + bool all_used; + + flags = StringToDoubleConverter::ALLOW_HEX; + + CHECK_EQ(4660.0, StrToD("0x1234", flags, 0.0, &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(Double::NaN(), + StrToD("0x1234.56", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + flags |= StringToDoubleConverter::ALLOW_TRAILING_JUNK; + CHECK_EQ(4660.0, + StrToD("0x1234.56", flags, 0.0, &processed, &all_used)); + CHECK_EQ(6, processed); + + flags = StringToDoubleConverter::ALLOW_OCTALS; + CHECK_EQ(668.0, StrToD("01234", flags, 0.0, &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(12349.0, StrToD("012349", flags, 0.0, &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(Double::NaN(), + StrToD("01234.56", flags, 0.0, &processed, &all_used)); + CHECK_EQ(processed, 0); flags |= StringToDoubleConverter::ALLOW_TRAILING_JUNK; CHECK_EQ(668.0, @@ -3184,10 +3883,10 @@ TEST(StringToDoubleCommentExamples) { CHECK(all_used); CHECK_EQ(123.0, StrToD("123e", flags, 0.0, &processed, &all_used)); - CHECK(all_used); + CHECK_EQ(processed, 3); CHECK_EQ(123.0, StrToD("123e-", flags, 0.0, &processed, &all_used)); - CHECK(all_used); + CHECK_EQ(processed, 3); { StringToDoubleConverter converter(flags, 0.0, 1.0, "infinity", "NaN"); @@ -3235,6 +3934,28 @@ TEST(StringToDoubleCommentExamples) { CHECK_EQ(Double::NaN(), StrToD("NaN", flags, 0.0, &processed, &all_used)); CHECK_EQ(0, processed); + + flags = StringToDoubleConverter::NO_FLAGS; + char separator = ' '; + CHECK_EQ(1234.0, + StrToD("1 2 3 4", flags, 0.0, &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(Double::NaN(), + StrToD("1 2", flags, 0.0, &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(1000000.0, + StrToD("1 000 000.0", flags, 0.0, &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(1.0, + StrToD("1.000 000", flags, 0.0, &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(Double::NaN(), + StrToD("1.0e1 000", flags, 0.0, &processed, &all_used, separator)); + CHECK_EQ(0, processed); } @@ -3261,7 +3982,7 @@ static double StrToF(const char* str, int flags, double empty_string_value, ((strlen(str) == static_cast(*processed_characters_count))); uc16 buffer16[256]; - ASSERT(strlen(str) < ARRAY_SIZE(buffer16)); + DOUBLE_CONVERSION_ASSERT(strlen(str) < DOUBLE_CONVERSION_ARRAY_SIZE(buffer16)); int len = strlen(str); for (int i = 0; i < len; i++) { buffer16[i] = str[i]; @@ -3740,13 +4461,30 @@ TEST(StringToFloatHexString) { CHECK_EQ(5.0f, StrToF(" + 0x5 ", flags, 0.0f, &processed, &all_used)); CHECK(all_used); - CHECK_EQ(Single::NaN(), StrToF("- -0x5", flags, 0.0f, &processed, &all_used)); + CHECK_EQ(Single::NaN(), StrToF("- -0x5", flags, 0.0f, + &processed, &all_used)); CHECK_EQ(0, processed); - CHECK_EQ(Single::NaN(), StrToF("- +0x5", flags, 0.0f, &processed, &all_used)); + CHECK_EQ(Single::NaN(), StrToF("- +0x5", flags, 0.0f, + &processed, &all_used)); CHECK_EQ(0, processed); - CHECK_EQ(Single::NaN(), StrToF("+ +0x5", flags, 0.0f, &processed, &all_used)); + CHECK_EQ(Single::NaN(), StrToF("+ +0x5", flags, 0.0f, + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::NaN(), StrToF("0x3p0", flags, 0.0f, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::NaN(), StrToF("0x.0p0", flags, 0.0f, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::NaN(), StrToF("0x3.0p0", flags, 0.0f, + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::NaN(), StrToF("0x3.p0", flags, 0.0f, + &processed, &all_used)); CHECK_EQ(0, processed); flags = StringToDoubleConverter::ALLOW_HEX; @@ -3843,6 +4581,20 @@ TEST(StringToFloatHexString) { CHECK_EQ(Single::NaN(), StrToF("+ +0x5", flags, 0.0f, &processed, &all_used)); CHECK_EQ(0, processed); + CHECK_EQ(Single::NaN(), StrToF("0x3p0", flags, 0.0f, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::NaN(), StrToF("0x.0p0", flags, 0.0f, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::NaN(), StrToF("0x3.0p0", flags, 0.0f, + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::NaN(), StrToF("0x3.p0", flags, 0.0f, + &processed, &all_used)); + CHECK_EQ(0, processed); + flags = StringToDoubleConverter::ALLOW_TRAILING_JUNK | StringToDoubleConverter::ALLOW_HEX; @@ -3965,6 +4717,19 @@ TEST(StringToFloatHexString) { CHECK_EQ(Single::NaN(), StrToF("+ +0x5", flags, 0.0f, &processed, &all_used)); CHECK_EQ(0, processed); + CHECK_EQ(3.0f, StrToF("0x3p0", flags, 0.0f, &processed, &all_used)); + CHECK_EQ(3, processed); + + CHECK_EQ(Single::NaN(), StrToF("0x.0p0", flags, 0.0f, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(3.0f, StrToF("0x3.0p0", flags, 0.0f, &processed, &all_used)); + CHECK_EQ(3, processed); + + CHECK_EQ(3.0f, StrToF("0x3.p0", flags, 0.0f, &processed, &all_used)); + CHECK_EQ(3, processed); + + flags = StringToDoubleConverter::ALLOW_TRAILING_JUNK | StringToDoubleConverter::ALLOW_LEADING_SPACES | StringToDoubleConverter::ALLOW_TRAILING_SPACES | @@ -4053,6 +4818,156 @@ TEST(StringToFloatHexString) { CHECK_EQ(Single::NaN(), StrToF("x3", flags, 0.0f, &processed, &all_used)); CHECK_EQ(0, processed); + + flags = StringToDoubleConverter::ALLOW_HEX_FLOATS; + + CHECK_EQ(3.0f, StrToF("0x3p0", flags, 0.0, &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(0.0f, StrToF("0x.0p0", flags, 0.0, &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(3.0f, StrToF("0x3.0p0", flags, 0.0, &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(3.0f, StrToF("0x3.p0", flags, 0.0, &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(-5634002804104940178441764864.0f, StrToF("-0x123456789012345678901234p0", + flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(134217728.0f, StrToF("0x8000001p0", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(134217728.0f, StrToF("0x8000000p0", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(549755813888.0f, StrToF("0x8000000001p0", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(549755813888.0f, StrToF("0x8000000000p0", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(549755879424.0f, StrToF("0x8000008001p0", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(549755813888.0f, StrToF("0x8000008000p0", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(549755944960.0f, StrToF("0x8000018001p0", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(549755944960.0f, StrToF("0x8000018000p0", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(8796093022208.0f, StrToF("0x8000000001p4", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(8796093022208.0f, StrToF("0x8000000000p+4", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(8796094070784.0f, StrToF("0x8000008001p04", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(34359738368.0f, StrToF("0x8000008000p-4", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(34359746560.0f, StrToF("0x8000018001p-04", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(8796095119360.0f, StrToF("0x8000018000p4", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(Single::Infinity(), StrToF("0x1p2000", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(0.0f, StrToF("0x1p-2000", flags, 0.0, &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(-0.0f, StrToF("-0x1p-2000", flags, 0.0, &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(Single::NaN(), StrToF(" ", flags, Single::NaN(), + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::NaN(), StrToF("0x", flags, 0.0, + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::NaN(), StrToF(" 0x ", flags, 0.0, + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::NaN(), StrToF(" 0x 3", flags, 0.0, + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::NaN(), StrToF("0x3g", flags, 0.0, + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::NaN(), StrToF("x3", flags, 0.0, + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::NaN(), StrToF("0x3 foo", flags, 0.0, + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::NaN(), StrToF(" 0x3 foo", flags, 0.0, + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::NaN(), StrToF("+ 0x3 foo", flags, 0.0, + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::NaN(), StrToF("+", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::NaN(), StrToF("-", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::NaN(), StrToF("- -0x5", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::NaN(), StrToF("- +0x5", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::NaN(), StrToF("+ +0x5", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::NaN(), StrToF("0xp1", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::NaN(), StrToF("0x.p1", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::Infinity(), StrToF("0x1.p10000000000000000", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(0.0f, StrToF("0x1.p-10000000000000000", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); } @@ -4700,7 +5615,7 @@ TEST(StringToDoubleFloatWhitespace) { kFigureSpace, kPunctuationSpace, kThinSpace, kHairSpace, kNarrowNoBreakSpace, kMediumMathematicalSpace, kIdeographicSpace, }; - const int kWhitespace16Length = ARRAY_SIZE(kWhitespace16); + const int kWhitespace16Length = DOUBLE_CONVERSION_ARRAY_SIZE(kWhitespace16); CHECK_EQ(-1.2, StrToD16(kWhitespace16, kWhitespace16Length, flags, Double::NaN(), &processed, &all_used)); @@ -4710,3 +5625,46 @@ TEST(StringToDoubleFloatWhitespace) { &processed, &all_used)); CHECK(all_used); } + + +TEST(StringToDoubleCaseInsensitiveSpecialValues) { + int processed = 0; + + int flags = StringToDoubleConverter::ALLOW_CASE_INSENSITIVITY | + StringToDoubleConverter::ALLOW_LEADING_SPACES | + StringToDoubleConverter::ALLOW_TRAILING_JUNK | + StringToDoubleConverter::ALLOW_TRAILING_SPACES; + + // Use 1.0 as junk_string_value. + StringToDoubleConverter converter(flags, 0.0, 1.0, "infinity", "nan"); + + CHECK_EQ(Double::NaN(), converter.StringToDouble("+nan", 4, &processed)); + CHECK_EQ(4, processed); + + CHECK_EQ(Double::NaN(), converter.StringToDouble("-nAN", 4, &processed)); + CHECK_EQ(4, processed); + + CHECK_EQ(Double::NaN(), converter.StringToDouble("nAN", 3, &processed)); + CHECK_EQ(3, processed); + + CHECK_EQ(Double::NaN(), converter.StringToDouble("nANabc", 6, &processed)); + CHECK_EQ(3, processed); + + CHECK_EQ(+Double::Infinity(), + converter.StringToDouble("+Infinity", 9, &processed)); + CHECK_EQ(9, processed); + + CHECK_EQ(-Double::Infinity(), + converter.StringToDouble("-INFinity", 9, &processed)); + CHECK_EQ(9, processed); + + CHECK_EQ(Double::Infinity(), + converter.StringToDouble("infINITY", 8, &processed)); + CHECK_EQ(8, processed); + + CHECK_EQ(1.0, converter.StringToDouble("INF", 3, &processed)); + CHECK_EQ(0, processed); + + CHECK_EQ(1.0, converter.StringToDouble("+inf", 4, &processed)); + CHECK_EQ(0, processed); +} diff --git a/test/cctest/test-diy-fp.cc b/test/cctest/test-diy-fp.cc index 26038b1f..16209a90 100644 --- a/test/cctest/test-diy-fp.cc +++ b/test/cctest/test-diy-fp.cc @@ -34,20 +34,20 @@ TEST(Multiply) { CHECK(0 == diy_fp1.f()); // NOLINT CHECK_EQ(64, diy_fp1.e()); - diy_fp1 = DiyFp(UINT64_2PART_C(0x80000000, 00000000), 11); + diy_fp1 = DiyFp(DOUBLE_CONVERSION_UINT64_2PART_C(0x80000000, 00000000), 11); diy_fp2 = DiyFp(2, 13); product = DiyFp::Times(diy_fp1, diy_fp2); CHECK(1 == product.f()); // NOLINT CHECK_EQ(11 + 13 + 64, product.e()); // Test rounding. - diy_fp1 = DiyFp(UINT64_2PART_C(0x80000000, 00000001), 11); + diy_fp1 = DiyFp(DOUBLE_CONVERSION_UINT64_2PART_C(0x80000000, 00000001), 11); diy_fp2 = DiyFp(1, 13); product = DiyFp::Times(diy_fp1, diy_fp2); CHECK(1 == product.f()); // NOLINT CHECK_EQ(11 + 13 + 64, product.e()); - diy_fp1 = DiyFp(UINT64_2PART_C(0x7fffffff, ffffffff), 11); + diy_fp1 = DiyFp(DOUBLE_CONVERSION_UINT64_2PART_C(0x7fffffff, ffffffff), 11); diy_fp2 = DiyFp(1, 13); product = DiyFp::Times(diy_fp1, diy_fp2); CHECK(0 == product.f()); // NOLINT @@ -56,10 +56,10 @@ TEST(Multiply) { // Halfway cases are allowed to round either way. So don't check for it. // Big numbers. - diy_fp1 = DiyFp(UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF), 11); - diy_fp2 = DiyFp(UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF), 13); + diy_fp1 = DiyFp(DOUBLE_CONVERSION_UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF), 11); + diy_fp2 = DiyFp(DOUBLE_CONVERSION_UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF), 13); // 128bit result: 0xfffffffffffffffe0000000000000001 product = DiyFp::Times(diy_fp1, diy_fp2); - CHECK(UINT64_2PART_C(0xFFFFFFFF, FFFFFFFe) == product.f()); + CHECK(DOUBLE_CONVERSION_UINT64_2PART_C(0xFFFFFFFF, FFFFFFFe) == product.f()); CHECK_EQ(11 + 13 + 64, product.e()); } diff --git a/test/cctest/test-dtoa.cc b/test/cctest/test-dtoa.cc index 03463366..494850e1 100644 --- a/test/cctest/test-dtoa.cc +++ b/test/cctest/test-dtoa.cc @@ -269,7 +269,7 @@ TEST(DtoaVariousDoubles) { CHECK_EQ("35844466", buffer.start()); CHECK_EQ(299, point); - uint64_t smallest_normal64 = UINT64_2PART_C(0x00100000, 00000000); + uint64_t smallest_normal64 = DOUBLE_CONVERSION_UINT64_2PART_C(0x00100000, 00000000); double v = Double(smallest_normal64).value(); DoubleToAscii(v, SHORTEST, 0, buffer, &sign, &length, &point); CHECK_EQ("22250738585072014", buffer.start()); @@ -287,7 +287,7 @@ TEST(DtoaVariousDoubles) { CHECK_EQ("22250738585072013831", buffer.start()); CHECK_EQ(-307, point); - uint64_t largest_denormal64 = UINT64_2PART_C(0x000FFFFF, FFFFFFFF); + uint64_t largest_denormal64 = DOUBLE_CONVERSION_UINT64_2PART_C(0x000FFFFF, FFFFFFFF); v = Double(largest_denormal64).value(); DoubleToAscii(v, SHORTEST, 0, buffer, &sign, &length, &point); CHECK_EQ("2225073858507201", buffer.start()); diff --git a/test/cctest/test-fast-dtoa.cc b/test/cctest/test-fast-dtoa.cc index 31d47846..6a523cf8 100644 --- a/test/cctest/test-fast-dtoa.cc +++ b/test/cctest/test-fast-dtoa.cc @@ -79,7 +79,7 @@ TEST(FastDtoaShortestVariousDoubles) { CHECK_EQ(299, point); } - uint64_t smallest_normal64 = UINT64_2PART_C(0x00100000, 00000000); + uint64_t smallest_normal64 = DOUBLE_CONVERSION_UINT64_2PART_C(0x00100000, 00000000); double v = Double(smallest_normal64).value(); status = FastDtoa(v, FAST_DTOA_SHORTEST, 0, buffer, &length, &point); if (status) { @@ -87,7 +87,7 @@ TEST(FastDtoaShortestVariousDoubles) { CHECK_EQ(-307, point); } - uint64_t largest_denormal64 = UINT64_2PART_C(0x000FFFFF, FFFFFFFF); + uint64_t largest_denormal64 = DOUBLE_CONVERSION_UINT64_2PART_C(0x000FFFFF, FFFFFFFF); v = Double(largest_denormal64).value(); status = FastDtoa(v, FAST_DTOA_SHORTEST, 0, buffer, &length, &point); if (status) { @@ -244,14 +244,14 @@ TEST(FastDtoaPrecisionVariousDoubles) { CHECK_EQ("35844466", buffer.start()); CHECK_EQ(299, point); - uint64_t smallest_normal64 = UINT64_2PART_C(0x00100000, 00000000); + uint64_t smallest_normal64 = DOUBLE_CONVERSION_UINT64_2PART_C(0x00100000, 00000000); double v = Double(smallest_normal64).value(); status = FastDtoa(v, FAST_DTOA_PRECISION, 17, buffer, &length, &point); CHECK(status); CHECK_EQ("22250738585072014", buffer.start()); CHECK_EQ(-307, point); - uint64_t largest_denormal64 = UINT64_2PART_C(0x000FFFFF, FFFFFFFF); + uint64_t largest_denormal64 = DOUBLE_CONVERSION_UINT64_2PART_C(0x000FFFFF, FFFFFFFF); v = Double(largest_denormal64).value(); status = FastDtoa(v, FAST_DTOA_PRECISION, 17, buffer, &length, &point); CHECK(status); diff --git a/test/cctest/test-fixed-dtoa.cc b/test/cctest/test-fixed-dtoa.cc index 5ffac581..e66f3890 100644 --- a/test/cctest/test-fixed-dtoa.cc +++ b/test/cctest/test-fixed-dtoa.cc @@ -485,6 +485,10 @@ TEST(FastFixedVariousDoubles) { buffer, &length, &point)); CHECK_EQ("1000000000000000128", buffer.start()); CHECK_EQ(19, point); + + CHECK(FastFixedDtoa(2.10861548515811875e+15, 17, buffer, &length, &point)); + CHECK_EQ("210861548515811875", buffer.start()); + CHECK_EQ(16, point); } diff --git a/test/cctest/test-ieee.cc b/test/cctest/test-ieee.cc index c57e8cea..f5285a34 100644 --- a/test/cctest/test-ieee.cc +++ b/test/cctest/test-ieee.cc @@ -13,13 +13,13 @@ using namespace double_conversion; TEST(Uint64Conversions) { // Start by checking the byte-order. - uint64_t ordered = UINT64_2PART_C(0x01234567, 89ABCDEF); + uint64_t ordered = DOUBLE_CONVERSION_UINT64_2PART_C(0x01234567, 89ABCDEF); CHECK_EQ(3512700564088504e-318, Double(ordered).value()); - uint64_t min_double64 = UINT64_2PART_C(0x00000000, 00000001); + uint64_t min_double64 = DOUBLE_CONVERSION_UINT64_2PART_C(0x00000000, 00000001); CHECK_EQ(5e-324, Double(min_double64).value()); - uint64_t max_double64 = UINT64_2PART_C(0x7fefffff, ffffffff); + uint64_t max_double64 = DOUBLE_CONVERSION_UINT64_2PART_C(0x7fefffff, ffffffff); CHECK_EQ(1.7976931348623157e308, Double(max_double64).value()); } @@ -38,22 +38,22 @@ TEST(Uint32Conversions) { TEST(Double_AsDiyFp) { - uint64_t ordered = UINT64_2PART_C(0x01234567, 89ABCDEF); + uint64_t ordered = DOUBLE_CONVERSION_UINT64_2PART_C(0x01234567, 89ABCDEF); DiyFp diy_fp = Double(ordered).AsDiyFp(); CHECK_EQ(0x12 - 0x3FF - 52, diy_fp.e()); // The 52 mantissa bits, plus the implicit 1 in bit 52 as a UINT64. - CHECK(UINT64_2PART_C(0x00134567, 89ABCDEF) == diy_fp.f()); // NOLINT + CHECK(DOUBLE_CONVERSION_UINT64_2PART_C(0x00134567, 89ABCDEF) == diy_fp.f()); // NOLINT - uint64_t min_double64 = UINT64_2PART_C(0x00000000, 00000001); + uint64_t min_double64 = DOUBLE_CONVERSION_UINT64_2PART_C(0x00000000, 00000001); diy_fp = Double(min_double64).AsDiyFp(); CHECK_EQ(-0x3FF - 52 + 1, diy_fp.e()); // This is a denormal; so no hidden bit. CHECK(1 == diy_fp.f()); // NOLINT - uint64_t max_double64 = UINT64_2PART_C(0x7fefffff, ffffffff); + uint64_t max_double64 = DOUBLE_CONVERSION_UINT64_2PART_C(0x7fefffff, ffffffff); diy_fp = Double(max_double64).AsDiyFp(); CHECK_EQ(0x7FE - 0x3FF - 52, diy_fp.e()); - CHECK(UINT64_2PART_C(0x001fffff, ffffffff) == diy_fp.f()); // NOLINT + CHECK(DOUBLE_CONVERSION_UINT64_2PART_C(0x001fffff, ffffffff) == diy_fp.f()); // NOLINT } @@ -78,32 +78,32 @@ TEST(Single_AsDiyFp) { TEST(AsNormalizedDiyFp) { - uint64_t ordered = UINT64_2PART_C(0x01234567, 89ABCDEF); + uint64_t ordered = DOUBLE_CONVERSION_UINT64_2PART_C(0x01234567, 89ABCDEF); DiyFp diy_fp = Double(ordered).AsNormalizedDiyFp(); CHECK_EQ(0x12 - 0x3FF - 52 - 11, diy_fp.e()); - CHECK((UINT64_2PART_C(0x00134567, 89ABCDEF) << 11) == + CHECK((DOUBLE_CONVERSION_UINT64_2PART_C(0x00134567, 89ABCDEF) << 11) == diy_fp.f()); // NOLINT - uint64_t min_double64 = UINT64_2PART_C(0x00000000, 00000001); + uint64_t min_double64 = DOUBLE_CONVERSION_UINT64_2PART_C(0x00000000, 00000001); diy_fp = Double(min_double64).AsNormalizedDiyFp(); CHECK_EQ(-0x3FF - 52 + 1 - 63, diy_fp.e()); // This is a denormal; so no hidden bit. - CHECK(UINT64_2PART_C(0x80000000, 00000000) == diy_fp.f()); // NOLINT + CHECK(DOUBLE_CONVERSION_UINT64_2PART_C(0x80000000, 00000000) == diy_fp.f()); // NOLINT - uint64_t max_double64 = UINT64_2PART_C(0x7fefffff, ffffffff); + uint64_t max_double64 = DOUBLE_CONVERSION_UINT64_2PART_C(0x7fefffff, ffffffff); diy_fp = Double(max_double64).AsNormalizedDiyFp(); CHECK_EQ(0x7FE - 0x3FF - 52 - 11, diy_fp.e()); - CHECK((UINT64_2PART_C(0x001fffff, ffffffff) << 11) == + CHECK((DOUBLE_CONVERSION_UINT64_2PART_C(0x001fffff, ffffffff) << 11) == diy_fp.f()); // NOLINT } TEST(Double_IsDenormal) { - uint64_t min_double64 = UINT64_2PART_C(0x00000000, 00000001); + uint64_t min_double64 = DOUBLE_CONVERSION_UINT64_2PART_C(0x00000000, 00000001); CHECK(Double(min_double64).IsDenormal()); - uint64_t bits = UINT64_2PART_C(0x000FFFFF, FFFFFFFF); + uint64_t bits = DOUBLE_CONVERSION_UINT64_2PART_C(0x000FFFFF, FFFFFFFF); CHECK(Double(bits).IsDenormal()); - bits = UINT64_2PART_C(0x00100000, 00000000); + bits = DOUBLE_CONVERSION_UINT64_2PART_C(0x00100000, 00000000); CHECK(!Double(bits).IsDenormal()); } @@ -122,7 +122,7 @@ TEST(Double_IsSpecial) { CHECK(Double(Double::Infinity()).IsSpecial()); CHECK(Double(-Double::Infinity()).IsSpecial()); CHECK(Double(Double::NaN()).IsSpecial()); - uint64_t bits = UINT64_2PART_C(0xFFF12345, 00000000); + uint64_t bits = DOUBLE_CONVERSION_UINT64_2PART_C(0xFFF12345, 00000000); CHECK(Double(bits).IsSpecial()); // Denormals are not special: CHECK(!Double(5e-324).IsSpecial()); @@ -172,7 +172,7 @@ TEST(Double_IsInfinite) { CHECK(!Double(-0.0).IsInfinite()); CHECK(!Double(1.0).IsInfinite()); CHECK(!Double(-1.0).IsInfinite()); - uint64_t min_double64 = UINT64_2PART_C(0x00000000, 00000001); + uint64_t min_double64 = DOUBLE_CONVERSION_UINT64_2PART_C(0x00000000, 00000001); CHECK(!Double(min_double64).IsInfinite()); } @@ -192,7 +192,7 @@ TEST(Single_IsInfinite) { TEST(Double_IsNan) { CHECK(Double(Double::NaN()).IsNan()); - uint64_t other_nan = UINT64_2PART_C(0xFFFFFFFF, 00000001); + uint64_t other_nan = DOUBLE_CONVERSION_UINT64_2PART_C(0xFFFFFFFF, 00000001); CHECK(Double(other_nan).IsNan()); CHECK(!Double(Double::Infinity()).IsNan()); CHECK(!Double(-Double::Infinity()).IsNan()); @@ -200,7 +200,7 @@ TEST(Double_IsNan) { CHECK(!Double(-0.0).IsNan()); CHECK(!Double(1.0).IsNan()); CHECK(!Double(-1.0).IsNan()); - uint64_t min_double64 = UINT64_2PART_C(0x00000000, 00000001); + uint64_t min_double64 = DOUBLE_CONVERSION_UINT64_2PART_C(0x00000000, 00000001); CHECK(!Double(min_double64).IsNan()); } @@ -226,7 +226,7 @@ TEST(Double_Sign) { CHECK_EQ(-1, Double(-Double::Infinity()).Sign()); CHECK_EQ(1, Double(0.0).Sign()); CHECK_EQ(-1, Double(-0.0).Sign()); - uint64_t min_double64 = UINT64_2PART_C(0x00000000, 00000001); + uint64_t min_double64 = DOUBLE_CONVERSION_UINT64_2PART_C(0x00000000, 00000001); CHECK_EQ(1, Double(min_double64).Sign()); } @@ -264,7 +264,7 @@ TEST(Double_NormalizedBoundaries) { CHECK((1 << 9) == diy_fp.f() - boundary_minus.f()); // NOLINT CHECK((1 << 10) == boundary_plus.f() - diy_fp.f()); // NOLINT - uint64_t min_double64 = UINT64_2PART_C(0x00000000, 00000001); + uint64_t min_double64 = DOUBLE_CONVERSION_UINT64_2PART_C(0x00000000, 00000001); diy_fp = Double(min_double64).AsNormalizedDiyFp(); Double(min_double64).NormalizedBoundaries(&boundary_minus, &boundary_plus); CHECK_EQ(diy_fp.e(), boundary_minus.e()); @@ -276,7 +276,7 @@ TEST(Double_NormalizedBoundaries) { CHECK((static_cast(1) << 62) == diy_fp.f() - boundary_minus.f()); // NOLINT - uint64_t smallest_normal64 = UINT64_2PART_C(0x00100000, 00000000); + uint64_t smallest_normal64 = DOUBLE_CONVERSION_UINT64_2PART_C(0x00100000, 00000000); diy_fp = Double(smallest_normal64).AsNormalizedDiyFp(); Double(smallest_normal64).NormalizedBoundaries(&boundary_minus, &boundary_plus); @@ -287,7 +287,7 @@ TEST(Double_NormalizedBoundaries) { CHECK(diy_fp.f() - boundary_minus.f() == boundary_plus.f() - diy_fp.f()); CHECK((1 << 10) == diy_fp.f() - boundary_minus.f()); // NOLINT - uint64_t largest_denormal64 = UINT64_2PART_C(0x000FFFFF, FFFFFFFF); + uint64_t largest_denormal64 = DOUBLE_CONVERSION_UINT64_2PART_C(0x000FFFFF, FFFFFFFF); diy_fp = Double(largest_denormal64).AsNormalizedDiyFp(); Double(largest_denormal64).NormalizedBoundaries(&boundary_minus, &boundary_plus); @@ -296,7 +296,7 @@ TEST(Double_NormalizedBoundaries) { CHECK(diy_fp.f() - boundary_minus.f() == boundary_plus.f() - diy_fp.f()); CHECK((1 << 11) == diy_fp.f() - boundary_minus.f()); // NOLINT - uint64_t max_double64 = UINT64_2PART_C(0x7fefffff, ffffffff); + uint64_t max_double64 = DOUBLE_CONVERSION_UINT64_2PART_C(0x7fefffff, ffffffff); diy_fp = Double(max_double64).AsNormalizedDiyFp(); Double(max_double64).NormalizedBoundaries(&boundary_minus, &boundary_plus); CHECK_EQ(diy_fp.e(), boundary_minus.e()); @@ -398,7 +398,7 @@ TEST(NextDouble) { CHECK_EQ(4e-324, d2.NextDouble()); CHECK_EQ(-1.7976931348623157e308, Double(-Double::Infinity()).NextDouble()); CHECK_EQ(Double::Infinity(), - Double(UINT64_2PART_C(0x7fefffff, ffffffff)).NextDouble()); + Double(DOUBLE_CONVERSION_UINT64_2PART_C(0x7fefffff, ffffffff)).NextDouble()); } @@ -417,5 +417,5 @@ TEST(PreviousDouble) { CHECK_EQ(-4e-324, d2.PreviousDouble()); CHECK_EQ(1.7976931348623157e308, Double(Double::Infinity()).PreviousDouble()); CHECK_EQ(-Double::Infinity(), - Double(UINT64_2PART_C(0xffefffff, ffffffff)).PreviousDouble()); + Double(DOUBLE_CONVERSION_UINT64_2PART_C(0xffefffff, ffffffff)).PreviousDouble()); } diff --git a/test/cctest/test-strtod.cc b/test/cctest/test-strtod.cc index 1a6ac3b9..62badbd2 100644 --- a/test/cctest/test-strtod.cc +++ b/test/cctest/test-strtod.cc @@ -21,6 +21,11 @@ static double StrtodChar(const char* str, int exponent) { } +static double StrtodTrimmedChar(const char* str, int exponent) { + return StrtodTrimmed(StringToVector(str), exponent); +} + + static float StrtofChar(const char* str, int exponent) { return Strtof(StringToVector(str), exponent); } @@ -350,6 +355,276 @@ TEST(Strtod) { } +TEST(StrtodTrimmed) { + Vector vector; + + vector = StringToVector("1"); + CHECK_EQ(1.0, StrtodTrimmed(vector, 0)); + CHECK_EQ(10.0, StrtodTrimmed(vector, 1)); + CHECK_EQ(100.0, StrtodTrimmed(vector, 2)); + CHECK_EQ(1e20, StrtodTrimmed(vector, 20)); + CHECK_EQ(1e22, StrtodTrimmed(vector, 22)); + CHECK_EQ(1e23, StrtodTrimmed(vector, 23)); + CHECK_EQ(1e35, StrtodTrimmed(vector, 35)); + CHECK_EQ(1e36, StrtodTrimmed(vector, 36)); + CHECK_EQ(1e37, StrtodTrimmed(vector, 37)); + CHECK_EQ(1e-1, StrtodTrimmed(vector, -1)); + CHECK_EQ(1e-2, StrtodTrimmed(vector, -2)); + CHECK_EQ(1e-5, StrtodTrimmed(vector, -5)); + CHECK_EQ(1e-20, StrtodTrimmed(vector, -20)); + CHECK_EQ(1e-22, StrtodTrimmed(vector, -22)); + CHECK_EQ(1e-23, StrtodTrimmed(vector, -23)); + CHECK_EQ(1e-25, StrtodTrimmed(vector, -25)); + CHECK_EQ(1e-39, StrtodTrimmed(vector, -39)); + + vector = StringToVector("2"); + CHECK_EQ(2.0, StrtodTrimmed(vector, 0)); + CHECK_EQ(20.0, StrtodTrimmed(vector, 1)); + CHECK_EQ(200.0, StrtodTrimmed(vector, 2)); + CHECK_EQ(2e20, StrtodTrimmed(vector, 20)); + CHECK_EQ(2e22, StrtodTrimmed(vector, 22)); + CHECK_EQ(2e23, StrtodTrimmed(vector, 23)); + CHECK_EQ(2e35, StrtodTrimmed(vector, 35)); + CHECK_EQ(2e36, StrtodTrimmed(vector, 36)); + CHECK_EQ(2e37, StrtodTrimmed(vector, 37)); + CHECK_EQ(2e-1, StrtodTrimmed(vector, -1)); + CHECK_EQ(2e-2, StrtodTrimmed(vector, -2)); + CHECK_EQ(2e-5, StrtodTrimmed(vector, -5)); + CHECK_EQ(2e-20, StrtodTrimmed(vector, -20)); + CHECK_EQ(2e-22, StrtodTrimmed(vector, -22)); + CHECK_EQ(2e-23, StrtodTrimmed(vector, -23)); + CHECK_EQ(2e-25, StrtodTrimmed(vector, -25)); + CHECK_EQ(2e-39, StrtodTrimmed(vector, -39)); + + vector = StringToVector("9"); + CHECK_EQ(9.0, StrtodTrimmed(vector, 0)); + CHECK_EQ(90.0, StrtodTrimmed(vector, 1)); + CHECK_EQ(900.0, StrtodTrimmed(vector, 2)); + CHECK_EQ(9e20, StrtodTrimmed(vector, 20)); + CHECK_EQ(9e22, StrtodTrimmed(vector, 22)); + CHECK_EQ(9e23, StrtodTrimmed(vector, 23)); + CHECK_EQ(9e35, StrtodTrimmed(vector, 35)); + CHECK_EQ(9e36, StrtodTrimmed(vector, 36)); + CHECK_EQ(9e37, StrtodTrimmed(vector, 37)); + CHECK_EQ(9e-1, StrtodTrimmed(vector, -1)); + CHECK_EQ(9e-2, StrtodTrimmed(vector, -2)); + CHECK_EQ(9e-5, StrtodTrimmed(vector, -5)); + CHECK_EQ(9e-20, StrtodTrimmed(vector, -20)); + CHECK_EQ(9e-22, StrtodTrimmed(vector, -22)); + CHECK_EQ(9e-23, StrtodTrimmed(vector, -23)); + CHECK_EQ(9e-25, StrtodTrimmed(vector, -25)); + CHECK_EQ(9e-39, StrtodTrimmed(vector, -39)); + + vector = StringToVector("12345"); + CHECK_EQ(12345.0, StrtodTrimmed(vector, 0)); + CHECK_EQ(123450.0, StrtodTrimmed(vector, 1)); + CHECK_EQ(1234500.0, StrtodTrimmed(vector, 2)); + CHECK_EQ(12345e20, StrtodTrimmed(vector, 20)); + CHECK_EQ(12345e22, StrtodTrimmed(vector, 22)); + CHECK_EQ(12345e23, StrtodTrimmed(vector, 23)); + CHECK_EQ(12345e30, StrtodTrimmed(vector, 30)); + CHECK_EQ(12345e31, StrtodTrimmed(vector, 31)); + CHECK_EQ(12345e32, StrtodTrimmed(vector, 32)); + CHECK_EQ(12345e35, StrtodTrimmed(vector, 35)); + CHECK_EQ(12345e36, StrtodTrimmed(vector, 36)); + CHECK_EQ(12345e37, StrtodTrimmed(vector, 37)); + CHECK_EQ(12345e-1, StrtodTrimmed(vector, -1)); + CHECK_EQ(12345e-2, StrtodTrimmed(vector, -2)); + CHECK_EQ(12345e-5, StrtodTrimmed(vector, -5)); + CHECK_EQ(12345e-20, StrtodTrimmed(vector, -20)); + CHECK_EQ(12345e-22, StrtodTrimmed(vector, -22)); + CHECK_EQ(12345e-23, StrtodTrimmed(vector, -23)); + CHECK_EQ(12345e-25, StrtodTrimmed(vector, -25)); + CHECK_EQ(12345e-39, StrtodTrimmed(vector, -39)); + + vector = StringToVector("12345678901234"); + CHECK_EQ(12345678901234.0, StrtodTrimmed(vector, 0)); + CHECK_EQ(123456789012340.0, StrtodTrimmed(vector, 1)); + CHECK_EQ(1234567890123400.0, StrtodTrimmed(vector, 2)); + CHECK_EQ(12345678901234e20, StrtodTrimmed(vector, 20)); + CHECK_EQ(12345678901234e22, StrtodTrimmed(vector, 22)); + CHECK_EQ(12345678901234e23, StrtodTrimmed(vector, 23)); + CHECK_EQ(12345678901234e30, StrtodTrimmed(vector, 30)); + CHECK_EQ(12345678901234e31, StrtodTrimmed(vector, 31)); + CHECK_EQ(12345678901234e32, StrtodTrimmed(vector, 32)); + CHECK_EQ(12345678901234e35, StrtodTrimmed(vector, 35)); + CHECK_EQ(12345678901234e36, StrtodTrimmed(vector, 36)); + CHECK_EQ(12345678901234e37, StrtodTrimmed(vector, 37)); + CHECK_EQ(12345678901234e-1, StrtodTrimmed(vector, -1)); + CHECK_EQ(12345678901234e-2, StrtodTrimmed(vector, -2)); + CHECK_EQ(12345678901234e-5, StrtodTrimmed(vector, -5)); + CHECK_EQ(12345678901234e-20, StrtodTrimmed(vector, -20)); + CHECK_EQ(12345678901234e-22, StrtodTrimmed(vector, -22)); + CHECK_EQ(12345678901234e-23, StrtodTrimmed(vector, -23)); + CHECK_EQ(12345678901234e-25, StrtodTrimmed(vector, -25)); + CHECK_EQ(12345678901234e-39, StrtodTrimmed(vector, -39)); + + vector = StringToVector("123456789012345"); + CHECK_EQ(123456789012345.0, StrtodTrimmed(vector, 0)); + CHECK_EQ(1234567890123450.0, StrtodTrimmed(vector, 1)); + CHECK_EQ(12345678901234500.0, StrtodTrimmed(vector, 2)); + CHECK_EQ(123456789012345e20, StrtodTrimmed(vector, 20)); + CHECK_EQ(123456789012345e22, StrtodTrimmed(vector, 22)); + CHECK_EQ(123456789012345e23, StrtodTrimmed(vector, 23)); + CHECK_EQ(123456789012345e35, StrtodTrimmed(vector, 35)); + CHECK_EQ(123456789012345e36, StrtodTrimmed(vector, 36)); + CHECK_EQ(123456789012345e37, StrtodTrimmed(vector, 37)); + CHECK_EQ(123456789012345e39, StrtodTrimmed(vector, 39)); + CHECK_EQ(123456789012345e-1, StrtodTrimmed(vector, -1)); + CHECK_EQ(123456789012345e-2, StrtodTrimmed(vector, -2)); + CHECK_EQ(123456789012345e-5, StrtodTrimmed(vector, -5)); + CHECK_EQ(123456789012345e-20, StrtodTrimmed(vector, -20)); + CHECK_EQ(123456789012345e-22, StrtodTrimmed(vector, -22)); + CHECK_EQ(123456789012345e-23, StrtodTrimmed(vector, -23)); + CHECK_EQ(123456789012345e-25, StrtodTrimmed(vector, -25)); + CHECK_EQ(123456789012345e-39, StrtodTrimmed(vector, -39)); + + CHECK_EQ(0.0, StrtodTrimmedChar("", 1324)); + CHECK_EQ(0.0, StrtodTrimmedChar("2", -324)); + CHECK_EQ(4e-324, StrtodTrimmedChar("3", -324)); + // It would be more readable to put non-zero literals on the left side (i.e. + // CHECK_EQ(1e-325, StrtodChar("1", -325))), but then Gcc complains that + // they are truncated to zero. + CHECK_EQ(0.0, StrtodTrimmedChar("1", -325)); + CHECK_EQ(0.0, StrtodTrimmedChar("1", -325)); + + // It would be more readable to put the literals (and not Double::Infinity()) + // on the left side (i.e. CHECK_EQ(1e309, StrtodChar("1", 309))), but then Gcc + // complains that the floating constant exceeds range of 'double'. + CHECK_EQ(Double::Infinity(), StrtodTrimmedChar("1", 309)); + CHECK_EQ(1e308, StrtodTrimmedChar("1", 308)); + CHECK_EQ(1234e305, StrtodTrimmedChar("1234", 305)); + CHECK_EQ(1234e304, StrtodTrimmedChar("1234", 304)); + CHECK_EQ(Double::Infinity(), StrtodTrimmedChar("18", 307)); + CHECK_EQ(17e307, StrtodTrimmedChar("17", 307)); + + CHECK_EQ(1.7976931348623157E+308, StrtodTrimmedChar("17976931348623157", 292)); + CHECK_EQ(1.7976931348623158E+308, StrtodTrimmedChar("17976931348623158", 292)); + CHECK_EQ(Double::Infinity(), StrtodTrimmedChar("17976931348623159", 292)); + + // The following number is the result of 89255.0/1e-22. Both floating-point + // numbers can be accurately represented with doubles. However on Linux,x86 + // the floating-point stack is set to 80bits and the double-rounding + // introduces an error. + CHECK_EQ(89255e-22, StrtodTrimmedChar("89255", -22)); + + // Some random values. + CHECK_EQ(358416272e-33, StrtodTrimmedChar("358416272", -33)); + CHECK_EQ(104110013277974872254e-225, + StrtodTrimmedChar("104110013277974872254", -225)); + + CHECK_EQ(123456789e108, StrtodTrimmedChar("123456789", 108)); + CHECK_EQ(123456789e109, StrtodTrimmedChar("123456789", 109)); + CHECK_EQ(123456789e110, StrtodTrimmedChar("123456789", 110)); + CHECK_EQ(123456789e111, StrtodTrimmedChar("123456789", 111)); + CHECK_EQ(123456789e112, StrtodTrimmedChar("123456789", 112)); + CHECK_EQ(123456789e113, StrtodTrimmedChar("123456789", 113)); + CHECK_EQ(123456789e114, StrtodTrimmedChar("123456789", 114)); + CHECK_EQ(123456789e115, StrtodTrimmedChar("123456789", 115)); + CHECK_EQ(1234567890123456789012345e108, + StrtodTrimmedChar("1234567890123456789012345", 108)); + CHECK_EQ(1234567890123456789012345e109, + StrtodTrimmedChar("1234567890123456789012345", 109)); + CHECK_EQ(1234567890123456789012345e110, + StrtodTrimmedChar("1234567890123456789012345", 110)); + CHECK_EQ(1234567890123456789012345e111, + StrtodTrimmedChar("1234567890123456789012345", 111)); + CHECK_EQ(1234567890123456789012345e112, + StrtodTrimmedChar("1234567890123456789012345", 112)); + CHECK_EQ(1234567890123456789012345e113, + StrtodTrimmedChar("1234567890123456789012345", 113)); + CHECK_EQ(1234567890123456789012345e114, + StrtodTrimmedChar("1234567890123456789012345", 114)); + CHECK_EQ(1234567890123456789012345e115, + StrtodTrimmedChar("1234567890123456789012345", 115)); + + CHECK_EQ(1234567890123456789052345e108, + StrtodTrimmedChar("1234567890123456789052345", 108)); + CHECK_EQ(1234567890123456789052345e109, + StrtodTrimmedChar("1234567890123456789052345", 109)); + CHECK_EQ(1234567890123456789052345e110, + StrtodTrimmedChar("1234567890123456789052345", 110)); + CHECK_EQ(1234567890123456789052345e111, + StrtodTrimmedChar("1234567890123456789052345", 111)); + CHECK_EQ(1234567890123456789052345e112, + StrtodTrimmedChar("1234567890123456789052345", 112)); + CHECK_EQ(1234567890123456789052345e113, + StrtodTrimmedChar("1234567890123456789052345", 113)); + CHECK_EQ(1234567890123456789052345e114, + StrtodTrimmedChar("1234567890123456789052345", 114)); + CHECK_EQ(1234567890123456789052345e115, + StrtodTrimmedChar("1234567890123456789052345", 115)); + + // Boundary cases. Boundaries themselves should round to even. + // + // 0x1FFFFFFFFFFFF * 2^3 = 72057594037927928 + // next: 72057594037927936 + // boundary: 72057594037927932 should round up. + CHECK_EQ(72057594037927928.0, StrtodTrimmedChar("72057594037927928", 0)); + CHECK_EQ(72057594037927936.0, StrtodTrimmedChar("72057594037927936", 0)); + CHECK_EQ(72057594037927936.0, StrtodTrimmedChar("72057594037927932", 0)); + CHECK_EQ(72057594037927928.0, StrtodTrimmedChar("7205759403792793199999", -5)); + CHECK_EQ(72057594037927936.0, StrtodTrimmedChar("7205759403792793200001", -5)); + + // 0x1FFFFFFFFFFFF * 2^10 = 9223372036854774784 + // next: 9223372036854775808 + // boundary: 9223372036854775296 should round up. + CHECK_EQ(9223372036854774784.0, StrtodTrimmedChar("9223372036854774784", 0)); + CHECK_EQ(9223372036854775808.0, StrtodTrimmedChar("9223372036854775808", 0)); + CHECK_EQ(9223372036854775808.0, StrtodTrimmedChar("9223372036854775296", 0)); + CHECK_EQ(9223372036854774784.0, StrtodTrimmedChar("922337203685477529599999", -5)); + CHECK_EQ(9223372036854775808.0, StrtodTrimmedChar("922337203685477529600001", -5)); + + // 0x1FFFFFFFFFFFF * 2^50 = 10141204801825834086073718800384 + // next: 10141204801825835211973625643008 + // boundary: 10141204801825834649023672221696 should round up. + CHECK_EQ(10141204801825834086073718800384.0, + StrtodTrimmedChar("10141204801825834086073718800384", 0)); + CHECK_EQ(10141204801825835211973625643008.0, + StrtodTrimmedChar("10141204801825835211973625643008", 0)); + CHECK_EQ(10141204801825835211973625643008.0, + StrtodTrimmedChar("10141204801825834649023672221696", 0)); + CHECK_EQ(10141204801825834086073718800384.0, + StrtodTrimmedChar("1014120480182583464902367222169599999", -5)); + CHECK_EQ(10141204801825835211973625643008.0, + StrtodTrimmedChar("1014120480182583464902367222169600001", -5)); + + // 0x1FFFFFFFFFFFF * 2^99 = 5708990770823838890407843763683279797179383808 + // next: 5708990770823839524233143877797980545530986496 + // boundary: 5708990770823839207320493820740630171355185152 + // The boundary should round up. + CHECK_EQ(5708990770823838890407843763683279797179383808.0, + StrtodTrimmedChar("5708990770823838890407843763683279797179383808", 0)); + CHECK_EQ(5708990770823839524233143877797980545530986496.0, + StrtodTrimmedChar("5708990770823839524233143877797980545530986496", 0)); + CHECK_EQ(5708990770823839524233143877797980545530986496.0, + StrtodTrimmedChar("5708990770823839207320493820740630171355185152", 0)); + CHECK_EQ(5708990770823838890407843763683279797179383808.0, + StrtodTrimmedChar("5708990770823839207320493820740630171355185151999", -3)); + CHECK_EQ(5708990770823839524233143877797980545530986496.0, + StrtodTrimmedChar("5708990770823839207320493820740630171355185152001", -3)); + + // The following test-cases got some public attention in early 2011 when they + // sent Java and PHP into an infinite loop. + CHECK_EQ(2.225073858507201e-308, StrtodTrimmedChar("22250738585072011", -324)); + CHECK_EQ(2.22507385850720138309e-308, + StrtodTrimmedChar("22250738585072011360574097967091319759348195463516456480" + "23426109724822222021076945516529523908135087914149158913" + "03962110687008643869459464552765720740782062174337998814" + "10632673292535522868813721490129811224514518898490572223" + "07285255133155755015914397476397983411801999323962548289" + "01710708185069063066665599493827577257201576306269066333" + "26475653000092458883164330377797918696120494973903778297" + "04905051080609940730262937128958950003583799967207254304" + "36028407889577179615094551674824347103070260914462157228" + "98802581825451803257070188608721131280795122334262883686" + "22321503775666622503982534335974568884423900265498198385" + "48794829220689472168983109969836584681402285424333066033" + "98508864458040010349339704275671864433837704860378616227" + "71738545623065874679014086723327636718751", -1076)); +} + + TEST(Strtof) { Vector vector;