From ce596312fe6aaf4b59632046aed19e2602213e18 Mon Sep 17 00:00:00 2001 From: zhixinaa <1696730989@qq.com> Date: Wed, 13 Sep 2023 16:48:44 +0800 Subject: [PATCH] Optimize the DoubleToPrecision and DoubleToExponential. Change-Id: I9590c20153e17cfb9d6455bbfc5fa65e52dd0586 Signed-off-by: zhixinaa <1696730989@qq.com> --- ecmascript/base/number_helper.cpp | 199 ++++++++++++++++++++++++++++++ ecmascript/base/number_helper.h | 5 + 2 files changed, 204 insertions(+) diff --git a/ecmascript/base/number_helper.cpp b/ecmascript/base/number_helper.cpp index b1541be437..4c02bcf61c 100644 --- a/ecmascript/base/number_helper.cpp +++ b/ecmascript/base/number_helper.cpp @@ -164,8 +164,95 @@ JSTaggedValue NumberHelper::DoubleToString(JSThread *thread, double number, int return BuiltinsBase::GetTaggedString(thread, result.get()); } +JSTaggedValue NumberHelper::ICUDoubleToExponential(JSThread *thread, double number, int digit) +{ +#if !defined(PANDA_TARGET_WINDOWS) + bool negative = false; + if (number < 0) { + number = -number; + negative = true; + } + // The limit for the the precision for ToExponential, leaving room for a + // point and a null-terminator. + const int bufferCapacity = base::MAX_FRACTION + 2; + char decimalRep[bufferCapacity]; + // The input 'number' should be interpreted as decimalRep*10^(decimalPoint-decimalRepLength). + // eg. 123.46 = 12346*10^(3-5) + int decimalPoint; + int decimalRepLength; + if(number == 0) { + decimalRep[0] = '0'; + decimalRep[1] = '\0'; + decimalPoint = 1; + decimalRepLength = 1; + } else if(digit < 0) { + icu::double_conversion::FastDtoa(number,icu::double_conversion::FastDtoaMode::FAST_DTOA_SHORTEST, 0, + icu::double_conversion::Vector(decimalRep, bufferCapacity), &decimalRepLength, &decimalPoint); + digit = decimalRepLength - 1; + } else { + icu::double_conversion::FastDtoa(number,icu::double_conversion::FastDtoaMode::FAST_DTOA_PRECISION, digit + 1, + icu::double_conversion::Vector(decimalRep, bufferCapacity), &decimalRepLength, &decimalPoint); + } + decimalRep[decimalRepLength] = '\0'; + + int exponent = decimalPoint - 1; + // Adding 1 to it ensures that the total number of significant digits includes the + // digit before the decimal point as well. + int significantDigits = digit + 1; + CString builder; + // leave room for the minus, the period, the letter 'e', the sign of the exponent, + // and the three-digit exponent. + builder.reserve(significantDigits + 7); + + bool negativeExponent = false; + if (exponent < 0) { + negativeExponent = true; + exponent = -exponent; + } + + if (negative) { + builder.push_back('-'); + } + builder.push_back(decimalRep[0]); + // Append the decimal point and the rest of the decimal representation if necessary. + if (significantDigits != 1) { + builder.push_back('.'); + builder.append(decimalRep + 1); + builder.append(significantDigits - decimalRepLength, '0'); + } + builder.push_back('e'); + builder.push_back(negativeExponent ? '-' : '+'); + + // Calculate the number of digits in the exponent. + uint32_t uexp = static_cast(exponent); + if (exponent < 0) { + builder.push_back('-'); + uexp = static_cast(-exponent); + } + int digits = 1; + const int ten = 10; + for (uint32_t factor = ten; digits < ten; digits++, factor *= ten) { + if (factor > uexp) break; + } + builder.append(digits,'0'); + int position_ = builder.length(); + for (int i = 1; i <= digits; i++) { + builder[position_ - i] = '0' + static_cast(uexp % ten); + uexp /= ten; + } + auto res = JSHandle(thread,EcmaStringAccessor::CreateFromUtf8(thread->GetEcmaVM(), + reinterpret_cast(builder.c_str()), builder.length(), true, MemSpaceType::SEMI_SPACE)); + return res.GetTaggedValue(); +#else + return DoubleToExponential(thread, number, digit); +#endif +} + JSTaggedValue NumberHelper::DoubleToExponential(JSThread *thread, double number, int digit) { +#if !defined(PANDA_TARGET_WINDOWS) + return ICUDoubleToExponential(thread, number, digit); +#else CStringStream ss; if (digit < 0) { ss << std::setiosflags(std::ios::scientific) << std::setprecision(base::MAX_PRECISION) << number; @@ -192,6 +279,7 @@ JSTaggedValue NumberHelper::DoubleToExponential(JSThread *thread, double number, } } return BuiltinsBase::GetTaggedString(thread, result.c_str()); +#endif } JSTaggedValue NumberHelper::DoubleToFixed(JSThread *thread, double number, int digit) @@ -201,8 +289,118 @@ JSTaggedValue NumberHelper::DoubleToFixed(JSThread *thread, double number, int d return BuiltinsBase::GetTaggedString(thread, ss.str().c_str()); } +JSTaggedValue NumberHelper::ICUDoubleToPrecision(JSThread *thread, double number, int digit) +{ +#if !defined(PANDA_TARGET_WINDOWS) + bool negative = false; + if (number < 0) { + number = -number; + negative = true; + } + // The limit for the the precision, add one for the terminating null character. + const int bufferCapacity = base::MAX_FRACTION + 1; + char decimalRep[bufferCapacity]; + + // The input 'number' should be interpreted as decimalRep*10^(decimalPoint-decimalRepLength). + // eg. 123.46 = 12346*10^(3-5) + int decimalPoint = 0; + int decimalRepLength = 0; + if(number == 0) { + decimalRep[0] = '0'; + decimalPoint = 1; + decimalRepLength = 1; + } else if(digit != 0) { + icu::double_conversion::FastDtoa(number, icu::double_conversion::FastDtoaMode::FAST_DTOA_PRECISION, digit, + icu::double_conversion::Vector(decimalRep, bufferCapacity), + &decimalRepLength, &decimalPoint); + } + + decimalRep[decimalRepLength] = '\0'; + int exponent = decimalPoint - 1; + // Leave room for a minus, a period, and a zero. + unsigned resultSize = (decimalPoint <= 0) ? -decimalPoint + digit + 3 : digit + 2; + CString builder; + builder.reserve(resultSize + 1); + // Calculates the exponent of the number in the exponential representation. + if (exponent < -6 || exponent >= digit) { + bool negativeExponent = false; + if (exponent < 0) { + negativeExponent = true; + exponent = -exponent; + } + if (negative) { + builder.push_back('-'); + } + builder.push_back(decimalRep[0]); + if (digit != 1) { + builder.push_back('.'); + builder.append(decimalRep + 1); + builder.append(digit - decimalRepLength,'0'); + } + + builder.push_back('e'); + builder.push_back(negativeExponent ? '-' : '+'); + uint32_t uexp = static_cast(exponent); + if (exponent < 0) { + builder.push_back('-'); + uexp = static_cast(-exponent); + } + int digits = 1; + const int ten = 10; + for (uint32_t factor = ten; digits < ten; digits++, factor *= ten) { + if (factor > uexp) break; + } + builder.append(digits, '0'); + int position_ = builder.length(); + for (int i = 1; i <= digits; i++) { + builder[position_ - i] = '0' + static_cast(uexp % ten); + uexp /= ten; + } + } else { + if (negative) { + builder.push_back('-'); + } + if (decimalPoint <= 0) { + builder.append("0."); + builder.append(-decimalPoint, '0'); + builder.append(decimalRep); + if(digit - decimalRepLength > 0) { + builder.append(digit - decimalRepLength, '0'); + } + } else { + const int m = decimalRepLength < decimalPoint?decimalRepLength : decimalPoint; + builder.append(decimalRep, m); + if(decimalPoint - decimalRepLength > 0) { + builder.append(decimalPoint - decimalRepLength, '0'); + } + if (decimalPoint < digit) { + builder.push_back('.'); + const int extra = negative ? 2 : 1; + if (decimalRepLength > decimalPoint) { + const int len = decimalRepLength - decimalPoint; + const int ex = digit - (builder.length() - extra); + const int n = ex < len ? ex : len; + builder.append(decimalRep + decimalPoint, n); + } + if(extra + digit - builder.length() >0) { + builder.append(extra + digit - builder.length(), '0'); + } + } + } + } + auto result = JSHandle(thread,EcmaStringAccessor::CreateFromUtf8(thread->GetEcmaVM(), + reinterpret_cast(builder.c_str()), builder.length(), true, MemSpaceType::SEMI_SPACE)); + return result.GetTaggedValue(); +#else + return DoubleToPrecision(thread, number, digit); +#endif +} + JSTaggedValue NumberHelper::DoubleToPrecision(JSThread *thread, double number, int digit) { +#if !defined(PANDA_TARGET_WINDOWS) + return ICUDoubleToPrecision(thread, number, digit); +#else if (number == 0.0) { return DoubleToFixed(thread, number, digit - 1); } @@ -215,6 +413,7 @@ JSTaggedValue NumberHelper::DoubleToPrecision(JSThread *thread, double number, i return DoubleToFixed(thread, number, std::abs(radixDigit)); } return DoubleToExponential(thread, number, digit - 1); +#endif } JSTaggedValue NumberHelper::StringToDoubleWithRadix(const uint8_t *start, const uint8_t *end, int radix) diff --git a/ecmascript/base/number_helper.h b/ecmascript/base/number_helper.h index 3d44f03f5b..8931163f2d 100644 --- a/ecmascript/base/number_helper.h +++ b/ecmascript/base/number_helper.h @@ -20,6 +20,9 @@ #include "ecmascript/ecma_string.h" #include "ecmascript/js_tagged_value.h" +#if !defined(PANDA_TARGET_WINDOWS) +#include "double-conversion-fast-dtoa.h" +#endif namespace panda::ecmascript::base { constexpr double MIN_RADIX = 2; @@ -111,6 +114,8 @@ public: static CString IntToString(int number); static CString IntegerToString(double number, int radix); static JSTaggedValue StringToBigInt(JSThread *thread, JSHandle strVal); + static JSTaggedValue ICUDoubleToPrecision(JSThread *thread, double number, int digit); + static JSTaggedValue ICUDoubleToExponential(JSThread *thread, double number, int digit); private: static char Carry(char current, int radix); -- Gitee