mirror of
https://github.com/openharmony/ark_js_runtime.git
synced 2026-07-21 23:25:24 -04:00
7ffcc4b20f
Signed-off-by: zhangyukun <zhangyukun8@huawei.com>
686 lines
22 KiB
C++
686 lines
22 KiB
C++
/*
|
||
* Copyright (c) 2021 Huawei Device Co., Ltd.
|
||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
* you may not use this file except in compliance with the License.
|
||
* You may obtain a copy of the License at
|
||
*
|
||
* http://www.apache.org/licenses/LICENSE-2.0
|
||
*
|
||
* Unless required by applicable law or agreed to in writing, software
|
||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
* See the License for the specific language governing permissions and
|
||
* limitations under the License.
|
||
*/
|
||
|
||
#include "ecmascript/base/number_helper.h"
|
||
#include <cmath>
|
||
#include <cstddef>
|
||
#include <cstdint>
|
||
#include <iomanip>
|
||
#include <sstream>
|
||
#include "ecmascript/base/builtins_base.h"
|
||
#include "ecmascript/js_tagged_value-inl.h"
|
||
#include "ecmascript/object_factory.h"
|
||
|
||
namespace panda::ecmascript::base {
|
||
enum class Sign { NONE, NEG, POS };
|
||
|
||
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
||
#define RETURN_IF_CONVERSION_END(p, end, result) \
|
||
if ((p) == (end)) { \
|
||
return (result); \
|
||
}
|
||
|
||
constexpr char CHARS[] = "0123456789abcdefghijklmnopqrstuvwxyz"; // NOLINT (modernize-avoid-c-arrays)
|
||
constexpr uint64_t MAX_MANTISSA = 0x1ULL << 52U;
|
||
|
||
static inline uint8_t ToDigit(uint8_t c)
|
||
{
|
||
if (c >= '0' && c <= '9') {
|
||
return c - '0';
|
||
}
|
||
if (c >= 'A' && c <= 'Z') {
|
||
return c - 'A' + DECIMAL;
|
||
}
|
||
if (c >= 'a' && c <= 'z') {
|
||
return c - 'a' + DECIMAL;
|
||
}
|
||
return '$';
|
||
}
|
||
|
||
bool NumberHelper::IsNonspace(uint16_t c)
|
||
{
|
||
int i;
|
||
int len = sizeof(SPACE_OR_LINE_TERMINAL) / sizeof(SPACE_OR_LINE_TERMINAL[0]);
|
||
for (i = 0; i < len; i++) {
|
||
if (c == SPACE_OR_LINE_TERMINAL[i]) {
|
||
return false;
|
||
}
|
||
if (c < SPACE_OR_LINE_TERMINAL[i]) {
|
||
return true;
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
|
||
bool NumberHelper::GotoNonspace(uint8_t **ptr, const uint8_t *end)
|
||
{
|
||
while (*ptr < end) {
|
||
uint16_t c = **ptr;
|
||
size_t size = 1;
|
||
if (**ptr > INT8_MAX) {
|
||
size = 0;
|
||
uint16_t utf8Bit = INT8_MAX + 1; // equal 0b1000'0000
|
||
while (utf8Bit > 0 && (c & utf8Bit) == utf8Bit) {
|
||
++size;
|
||
utf8Bit >>= 1UL;
|
||
}
|
||
if (base::utf_helper::ConvertRegionUtf8ToUtf16(*ptr, &c, 1, 0) <= 0) {
|
||
return true;
|
||
}
|
||
}
|
||
if (IsNonspace(c)) {
|
||
return true;
|
||
}
|
||
*ptr += size; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||
}
|
||
return false;
|
||
}
|
||
|
||
static inline double SignedZero(Sign sign)
|
||
{
|
||
return sign == Sign::NEG ? -0.0 : 0.0;
|
||
}
|
||
|
||
bool NumberHelper::IsEmptyString(const uint8_t *start, const uint8_t *end)
|
||
{
|
||
auto p = const_cast<uint8_t *>(start);
|
||
return !NumberHelper::GotoNonspace(&p, end);
|
||
}
|
||
|
||
JSTaggedValue NumberHelper::DoubleToString(JSThread *thread, double number, int radix)
|
||
{
|
||
bool negative = false;
|
||
if (number < 0.0) {
|
||
negative = true;
|
||
number = -number;
|
||
}
|
||
|
||
double numberInteger = std::floor(number);
|
||
double numberFraction = number - numberInteger;
|
||
|
||
auto value = bit_cast<uint64_t>(number);
|
||
value += 1;
|
||
double delta = HALF * (bit_cast<double>(value) - number);
|
||
|
||
CString result;
|
||
if (numberFraction != 0 && numberFraction >= delta) {
|
||
result += ".";
|
||
result += DecimalsToString(&numberInteger, numberFraction, radix, delta);
|
||
}
|
||
|
||
result = IntergerToString(numberInteger, radix) + result;
|
||
|
||
if (negative) {
|
||
result = "-" + result;
|
||
}
|
||
|
||
return BuiltinsBase::GetTaggedString(thread, result.c_str());
|
||
}
|
||
|
||
JSTaggedValue NumberHelper::DoubleToExponential(JSThread *thread, double number, int digit)
|
||
{
|
||
CStringStream ss;
|
||
if (digit < 0) {
|
||
ss << std::setiosflags(std::ios::scientific) << std::setprecision(base::MAX_PRECISION) << number;
|
||
} else {
|
||
ss << std::setiosflags(std::ios::scientific) << std::setprecision(digit) << number;
|
||
}
|
||
CString result = ss.str();
|
||
size_t found = result.find_last_of('e');
|
||
if (found != CString::npos && found < result.size() - 2 && result[found + 2] == '0') {
|
||
result.erase(found + 2, 1); // 2:offset of e
|
||
}
|
||
if (digit < 0) {
|
||
size_t end = found;
|
||
while (--found > 0) {
|
||
if (result[found] != '0') {
|
||
break;
|
||
}
|
||
}
|
||
if (result[found] == '.') {
|
||
found--;
|
||
}
|
||
if (found < end - 1) {
|
||
result.erase(found + 1, end - found - 1);
|
||
}
|
||
}
|
||
return BuiltinsBase::GetTaggedString(thread, result.c_str());
|
||
}
|
||
|
||
JSTaggedValue NumberHelper::DoubleToFixed(JSThread *thread, double number, int digit)
|
||
{
|
||
CStringStream ss;
|
||
ss << std::setiosflags(std::ios::fixed) << std::setprecision(digit) << number;
|
||
return BuiltinsBase::GetTaggedString(thread, ss.str().c_str());
|
||
}
|
||
|
||
JSTaggedValue NumberHelper::DoubleToPrecision(JSThread *thread, double number, int digit)
|
||
{
|
||
if (number == 0.0) {
|
||
return DoubleToFixed(thread, number, digit - 1);
|
||
}
|
||
CStringStream ss;
|
||
double positiveNumber = number > 0 ? number : -number;
|
||
int logDigit = std::floor(log10(positiveNumber));
|
||
int radixDigit = digit - logDigit - 1;
|
||
const int MAX_EXPONENT_DIGIT = 6;
|
||
if ((logDigit >= 0 && radixDigit >= 0) || (logDigit < 0 && radixDigit <= MAX_EXPONENT_DIGIT)) {
|
||
return DoubleToFixed(thread, number, std::abs(radixDigit));
|
||
}
|
||
return DoubleToExponential(thread, number, digit - 1);
|
||
}
|
||
|
||
JSTaggedValue NumberHelper::StringToDoubleWithRadix(const uint8_t *start, const uint8_t *end, int radix)
|
||
{
|
||
auto p = const_cast<uint8_t *>(start);
|
||
JSTaggedValue nanResult = BuiltinsBase::GetTaggedDouble(NAN_VALUE);
|
||
// 1. skip space and line terminal
|
||
if (!NumberHelper::GotoNonspace(&p, end)) {
|
||
return nanResult;
|
||
}
|
||
|
||
// 2. sign bit
|
||
bool negative = false;
|
||
if (*p == '-') {
|
||
negative = true;
|
||
RETURN_IF_CONVERSION_END(++p, end, nanResult);
|
||
} else if (*p == '+') {
|
||
RETURN_IF_CONVERSION_END(++p, end, nanResult);
|
||
}
|
||
// 3. 0x or 0X
|
||
bool stripPrefix = true;
|
||
// 4. If R 0, then
|
||
// a. If R < 2 or R > 36, return NaN.
|
||
// b. If R 16, let stripPrefix be false.
|
||
if (radix != 0) {
|
||
if (radix < MIN_RADIX || radix > MAX_RADIX) {
|
||
return nanResult;
|
||
}
|
||
if (radix != HEXADECIMAL) {
|
||
stripPrefix = false;
|
||
}
|
||
} else {
|
||
radix = DECIMAL;
|
||
}
|
||
int size = 0;
|
||
if (stripPrefix) {
|
||
if (*p == '0') {
|
||
size++;
|
||
if (++p != end && (*p == 'x' || *p == 'X')) {
|
||
RETURN_IF_CONVERSION_END(++p, end, nanResult);
|
||
radix = HEXADECIMAL;
|
||
}
|
||
}
|
||
}
|
||
|
||
double result = 0;
|
||
bool isDone = false;
|
||
do {
|
||
double part = 0;
|
||
uint32_t multiplier = 1;
|
||
for (; p != end; ++p) {
|
||
// The maximum value to ensure that uint32_t will not overflow
|
||
const uint32_t MAX_MULTIPER = 0xffffffffU / 36;
|
||
uint32_t m = multiplier * radix;
|
||
if (m > MAX_MULTIPER) {
|
||
break;
|
||
}
|
||
|
||
int currentBit = ToDigit(*p);
|
||
if (currentBit >= radix) {
|
||
isDone = true;
|
||
break;
|
||
}
|
||
size++;
|
||
part = part * radix + currentBit;
|
||
multiplier = m;
|
||
}
|
||
result = result * multiplier + part;
|
||
if (isDone) {
|
||
break;
|
||
}
|
||
} while (p != end);
|
||
|
||
if (size == 0) {
|
||
return nanResult;
|
||
}
|
||
|
||
if (negative) {
|
||
result = -result;
|
||
}
|
||
return BuiltinsBase::GetTaggedDouble(result);
|
||
}
|
||
|
||
char NumberHelper::Carry(char current, int radix)
|
||
{
|
||
int digit = (current > '9') ? (current - 'a' + DECIMAL) : (current - '0');
|
||
digit = (digit == (radix - 1)) ? 0 : digit + 1;
|
||
return CHARS[digit];
|
||
}
|
||
|
||
CString NumberHelper::IntergerToString(double number, int radix)
|
||
{
|
||
ASSERT(radix >= MIN_RADIX && radix <= MAX_RADIX);
|
||
CString result;
|
||
while (number / radix > MAX_MANTISSA) {
|
||
number /= radix;
|
||
result = CString("0").append(result);
|
||
}
|
||
do {
|
||
double remainder = std::fmod(number, radix);
|
||
result = CHARS[static_cast<int>(remainder)] + result;
|
||
number = (number - remainder) / radix;
|
||
} while (number > 0);
|
||
return result;
|
||
}
|
||
|
||
CString NumberHelper::DecimalsToString(double *numberInteger, double fraction, int radix, double delta)
|
||
{
|
||
CString result;
|
||
while (fraction >= delta) {
|
||
fraction *= radix;
|
||
delta *= radix;
|
||
int64_t integer = std::floor(fraction);
|
||
fraction -= integer;
|
||
result += CHARS[integer];
|
||
if (fraction > HALF && fraction + delta > 1) {
|
||
size_t fractionEnd = result.size() - 1;
|
||
result[fractionEnd] = Carry(*result.rbegin(), radix);
|
||
for (; fractionEnd > 0; fractionEnd--) {
|
||
if (result[fractionEnd] == '0') {
|
||
result[fractionEnd - 1] = Carry(result[fractionEnd - 1], radix);
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
if (fractionEnd == 0) {
|
||
(*numberInteger)++;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
// delete 0 in the end
|
||
size_t found = result.find_last_not_of('0');
|
||
if (found != CString::npos) {
|
||
result.erase(found + 1);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
CString NumberHelper::IntToString(int number)
|
||
{
|
||
return ToCString(number);
|
||
}
|
||
|
||
// 7.1.12.1 ToString Applied to the Number Type
|
||
JSHandle<EcmaString> NumberHelper::NumberToString(const JSThread *thread, JSTaggedValue number)
|
||
{
|
||
ASSERT(number.IsNumber());
|
||
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
|
||
if (number.IsInt()) {
|
||
return factory->NewFromCanBeCompressString(IntToString(number.GetInt()));
|
||
}
|
||
|
||
double d = number.GetDouble();
|
||
if (std::isnan(d)) {
|
||
return factory->NewFromCanBeCompressString("NaN");
|
||
}
|
||
if (d == 0.0) {
|
||
return factory->NewFromCanBeCompressString("0");
|
||
}
|
||
if (d >= INT32_MIN + 1 && d <= INT32_MAX && d == static_cast<double>(static_cast<int32_t>(d))) {
|
||
return factory->NewFromCanBeCompressString(IntToString(static_cast<int32_t>(d)));
|
||
}
|
||
|
||
std::string result;
|
||
if (d < 0) {
|
||
result += "-";
|
||
d = -d;
|
||
}
|
||
|
||
if (std::isinf(d)) {
|
||
result += "Infinity";
|
||
return factory->NewFromStdStringUnCheck(result, true);
|
||
}
|
||
|
||
ASSERT(d > 0);
|
||
|
||
// 5. Otherwise, let n, k, and s be integers such that k ≥ 1, 10k−1 ≤ s < 10k, the Number value for s × 10n−k is m,
|
||
// and k is as small as possible. If there are multiple possibilities for s, choose the value of s for which s ×
|
||
// 10n−k is closest in value to m. If there are two such possible values of s, choose the one that is even. Note
|
||
// that k is the number of digits in the decimal representation of s and that s is not divisible by 10.
|
||
CStringStream str;
|
||
str << std::scientific << std::showpoint << std::setprecision(DOUBLE_MAX_PRECISION) << d;
|
||
if (strtod(str.str().c_str(), nullptr) != d) {
|
||
str.clear();
|
||
str.str("");
|
||
str << std::scientific << std::showpoint << std::setprecision(DOUBLE_MAX_PRECISION + 1) << d;
|
||
}
|
||
std::string scientificStr(str.str());
|
||
|
||
auto indexOfE = scientificStr.find_last_of('e');
|
||
ASSERT(indexOfE != std::string::npos);
|
||
std::string base = scientificStr.substr(0, indexOfE);
|
||
// skip trailing zeros, and base must not be empty.
|
||
base = base.substr(0, base.find_last_not_of('0') + 1);
|
||
int k = static_cast<int>(base.size()) - 1;
|
||
int n = std::stoi(scientificStr.substr(indexOfE + 1)) + 1;
|
||
if (n > 0 && n <= 21) { // NOLINT(readability-magic-numbers)
|
||
base.erase(1, 1);
|
||
if (k <= n) {
|
||
// 6. If k ≤ n ≤ 21, return the String consisting of the code units of the k digits of the decimal
|
||
// representation of s (in order, with no leading zeroes), followed by n−k occurrences of the code unit
|
||
// 0x0030 (DIGIT ZERO).
|
||
base += std::string(n - k, '0');
|
||
} else {
|
||
// 7. If 0 < n ≤ 21, return the String consisting of the code units of the most significant n digits of the
|
||
// decimal representation of s, followed by the code unit 0x002E (FULL STOP), followed by the code units of
|
||
// the remaining k−n digits of the decimal representation of s.
|
||
base.insert(n, 1, '.');
|
||
}
|
||
} else if (-6 < n && n <= 0) { // NOLINT(readability-magic-numbers)
|
||
// 8. If −6 < n ≤ 0, return the String consisting of the code unit 0x0030 (DIGIT ZERO), followed by the code
|
||
// unit 0x002E (FULL STOP), followed by −n occurrences of the code unit 0x0030 (DIGIT ZERO), followed by the
|
||
// code units of the k digits of the decimal representation of s.
|
||
base.erase(1, 1);
|
||
base = std::string("0.") + std::string(-n, '0') + base;
|
||
} else {
|
||
if (k == 1) {
|
||
// 9. Otherwise, if k = 1, return the String consisting of the code unit of the single digit of s
|
||
base.erase(1, 1);
|
||
}
|
||
// followed by code unit 0x0065 (LATIN SMALL LETTER E), followed by the code unit 0x002B (PLUS SIGN) or the code
|
||
// unit 0x002D (HYPHEN-MINUS) according to whether n−1 is positive or negative, followed by the code units of
|
||
// the decimal representation of the integer abs(n−1) (with no leading zeroes).
|
||
base += "e" + (n >= 1 ? std::string("+") : "") + std::to_string(n - 1);
|
||
}
|
||
result += base;
|
||
return factory->NewFromStdStringUnCheck(result, true);
|
||
}
|
||
|
||
double NumberHelper::TruncateDouble(double d)
|
||
{
|
||
if (std::isnan(d)) {
|
||
return 0;
|
||
}
|
||
if (!std::isfinite(d)) {
|
||
return d;
|
||
}
|
||
// -0 to +0
|
||
if (d == 0.0) {
|
||
return 0;
|
||
}
|
||
return (d >= 0) ? std::floor(d) : std::ceil(d);
|
||
}
|
||
|
||
double NumberHelper::StringToDouble(const uint8_t *start, const uint8_t *end, uint8_t radix, uint32_t flags)
|
||
{
|
||
auto p = const_cast<uint8_t *>(start);
|
||
// 1. skip space and line terminal
|
||
if (!NumberHelper::GotoNonspace(&p, end)) {
|
||
return 0.0;
|
||
}
|
||
|
||
// 2. get number sign
|
||
Sign sign = Sign::NONE;
|
||
if (*p == '+') {
|
||
RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
|
||
sign = Sign::POS;
|
||
} else if (*p == '-') {
|
||
RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
|
||
sign = Sign::NEG;
|
||
}
|
||
bool ignoreTrailing = (flags & IGNORE_TRAILING) != 0;
|
||
|
||
// 3. judge Infinity
|
||
static const char INF[] = "Infinity"; // NOLINT(modernize-avoid-c-arrays)
|
||
if (*p == INF[0]) {
|
||
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||
for (const char *i = &INF[1]; *i != '\0'; ++i) {
|
||
if (++p == end || *p != *i) {
|
||
return NAN_VALUE;
|
||
}
|
||
}
|
||
++p;
|
||
if (!ignoreTrailing && NumberHelper::GotoNonspace(&p, end)) {
|
||
return NAN_VALUE;
|
||
}
|
||
return sign == Sign::NEG ? -POSITIVE_INFINITY : POSITIVE_INFINITY;
|
||
}
|
||
|
||
// 4. get number radix
|
||
bool leadingZero = false;
|
||
bool prefixRadix = false;
|
||
if (*p == '0' && radix == 0) {
|
||
RETURN_IF_CONVERSION_END(++p, end, SignedZero(sign));
|
||
if (*p == 'x' || *p == 'X') {
|
||
if ((flags & ALLOW_HEX) == 0) {
|
||
return ignoreTrailing ? SignedZero(sign) : NAN_VALUE;
|
||
}
|
||
RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
|
||
if (sign != Sign::NONE) {
|
||
return NAN_VALUE;
|
||
}
|
||
prefixRadix = true;
|
||
radix = HEXADECIMAL;
|
||
} else if (*p == 'o' || *p == 'O') {
|
||
if ((flags & ALLOW_OCTAL) == 0) {
|
||
return ignoreTrailing ? SignedZero(sign) : NAN_VALUE;
|
||
}
|
||
RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
|
||
if (sign != Sign::NONE) {
|
||
return NAN_VALUE;
|
||
}
|
||
prefixRadix = true;
|
||
radix = OCTAL;
|
||
} else if (*p == 'b' || *p == 'B') {
|
||
if ((flags & ALLOW_BINARY) == 0) {
|
||
return ignoreTrailing ? SignedZero(sign) : NAN_VALUE;
|
||
}
|
||
RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
|
||
if (sign != Sign::NONE) {
|
||
return NAN_VALUE;
|
||
}
|
||
prefixRadix = true;
|
||
radix = BINARY;
|
||
} else {
|
||
leadingZero = true;
|
||
}
|
||
}
|
||
|
||
if (radix == 0) {
|
||
radix = DECIMAL;
|
||
}
|
||
auto pStart = p;
|
||
// 5. skip leading '0'
|
||
while (*p == '0') {
|
||
RETURN_IF_CONVERSION_END(++p, end, SignedZero(sign));
|
||
leadingZero = true;
|
||
}
|
||
// 6. parse to number
|
||
uint64_t intNumber = 0;
|
||
uint64_t numberMax = (UINT64_MAX - (radix - 1)) / radix;
|
||
int digits = 0;
|
||
int exponent = 0;
|
||
do {
|
||
uint8_t c = ToDigit(*p);
|
||
if (c >= radix) {
|
||
if (!prefixRadix || ignoreTrailing || (pStart != p && !NumberHelper::GotoNonspace(&p, end))) {
|
||
break;
|
||
}
|
||
// "0b" "0x1.2" "0b1e2" ...
|
||
return NAN_VALUE;
|
||
}
|
||
++digits;
|
||
if (intNumber < numberMax) {
|
||
intNumber = intNumber * radix + c;
|
||
} else {
|
||
++exponent;
|
||
}
|
||
} while (++p != end);
|
||
|
||
auto number = static_cast<double>(intNumber);
|
||
if (sign == Sign::NEG) {
|
||
if (number == 0) {
|
||
number = -0.0;
|
||
} else {
|
||
number = -number;
|
||
}
|
||
}
|
||
|
||
// 7. deal with other radix except DECIMAL
|
||
if (p == end || radix != DECIMAL) {
|
||
if ((digits == 0 && !leadingZero) || (p != end && !ignoreTrailing && NumberHelper::GotoNonspace(&p, end))) {
|
||
// no digits there, like "0x", "0xh", or error trailing of "0x3q"
|
||
return NAN_VALUE;
|
||
}
|
||
return number * std::pow(radix, exponent);
|
||
}
|
||
|
||
// 8. parse '.'
|
||
if (radix == DECIMAL && *p == '.') {
|
||
RETURN_IF_CONVERSION_END(++p, end, (digits > 0) ? (number * std::pow(radix, exponent)) : NAN_VALUE);
|
||
while (ToDigit(*p) < radix) {
|
||
--exponent;
|
||
++digits;
|
||
if (++p == end) {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
if (digits == 0 && !leadingZero) {
|
||
// no digits there, like ".", "sss", or ".e1"
|
||
return NAN_VALUE;
|
||
}
|
||
auto pEnd = p;
|
||
|
||
// 9. parse 'e/E' with '+/-'
|
||
char exponentSign = '+';
|
||
int additionalExponent = 0;
|
||
constexpr int MAX_EXPONENT = INT32_MAX / 2;
|
||
if (radix == DECIMAL && (p != end && (*p == 'e' || *p == 'E'))) {
|
||
RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
|
||
|
||
// 10. parse exponent number
|
||
if (*p == '+' || *p == '-') {
|
||
exponentSign = static_cast<char>(*p);
|
||
RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
|
||
}
|
||
uint8_t digit;
|
||
while ((digit = ToDigit(*p)) < radix) {
|
||
if (additionalExponent > MAX_EXPONENT / radix) {
|
||
additionalExponent = MAX_EXPONENT;
|
||
} else {
|
||
additionalExponent = additionalExponent * radix + digit;
|
||
}
|
||
if (++p == end) {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
exponent += (exponentSign == '-' ? -additionalExponent : additionalExponent);
|
||
if (!ignoreTrailing && NumberHelper::GotoNonspace(&p, end)) {
|
||
return NAN_VALUE;
|
||
}
|
||
|
||
// 10. build StringNumericLiteral string
|
||
CString buffer;
|
||
if (sign == Sign::NEG) {
|
||
buffer += "-";
|
||
}
|
||
for (uint8_t *i = pStart; i < pEnd; ++i) { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||
if (*i != static_cast<uint8_t>('.')) {
|
||
buffer += *i;
|
||
}
|
||
}
|
||
|
||
// 11. convert none-prefix radix string
|
||
return Strtod(buffer.c_str(), exponent, radix);
|
||
}
|
||
|
||
double NumberHelper::Strtod(const char *str, int exponent, uint8_t radix)
|
||
{
|
||
ASSERT(str != nullptr);
|
||
ASSERT(radix >= base::MIN_RADIX && radix <= base::MAX_RADIX);
|
||
auto p = const_cast<char *>(str);
|
||
Sign sign = Sign::NONE;
|
||
uint64_t number = 0;
|
||
uint64_t numberMax = (UINT64_MAX - (radix - 1)) / radix;
|
||
double result = 0.0;
|
||
if (*p == '-') {
|
||
sign = Sign::NEG;
|
||
++p;
|
||
}
|
||
while (*p == '0') {
|
||
++p;
|
||
}
|
||
while (*p != '\0') {
|
||
uint8_t digit = ToDigit(static_cast<uint8_t>(*p));
|
||
if (digit >= radix) {
|
||
break;
|
||
}
|
||
if (number < numberMax) {
|
||
number = number * radix + digit;
|
||
} else {
|
||
++exponent;
|
||
}
|
||
++p;
|
||
}
|
||
if (exponent < 0) {
|
||
result = number / std::pow(radix, -exponent);
|
||
} else {
|
||
result = number * std::pow(radix, exponent);
|
||
}
|
||
return sign == Sign::NEG ? -result : result;
|
||
}
|
||
|
||
int32_t NumberHelper::DoubleToInt(double d, size_t bits)
|
||
{
|
||
int32_t ret = 0;
|
||
auto u64 = bit_cast<uint64_t>(d);
|
||
int exp = static_cast<int>((u64 & DOUBLE_EXPONENT_MASK) >> DOUBLE_SIGNIFICAND_SIZE) - DOUBLE_EXPONENT_BIAS;
|
||
if (exp < static_cast<int>(bits - 1)) {
|
||
// smaller than INT<bits>_MAX, fast conversion
|
||
ret = static_cast<int32_t>(d);
|
||
} else if (exp < static_cast<int>(bits + DOUBLE_SIGNIFICAND_SIZE)) {
|
||
// Still has significand bits after mod 2^<bits>
|
||
// Get low <bits> bits by shift left <64 - bits> and shift right <64 - bits>
|
||
uint64_t value = (((u64 & DOUBLE_SIGNIFICAND_MASK) | DOUBLE_HIDDEN_BIT)
|
||
<< (exp - DOUBLE_SIGNIFICAND_SIZE + INT64_BITS - bits)) >>
|
||
(INT64_BITS - bits);
|
||
ret = static_cast<int32_t>(value);
|
||
if ((u64 & DOUBLE_SIGN_MASK) == DOUBLE_SIGN_MASK && ret != INT32_MIN) {
|
||
ret = -ret;
|
||
}
|
||
} else {
|
||
// No significand bits after mod 2^<bits>, contains NaN and INF
|
||
ret = 0;
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
int32_t NumberHelper::DoubleInRangeInt32(double d)
|
||
{
|
||
if (d > INT_MAX) {
|
||
return INT_MAX;
|
||
}
|
||
if (d < INT_MIN) {
|
||
return INT_MIN;
|
||
}
|
||
return base::NumberHelper::DoubleToInt(d, base::INT32_BITS);
|
||
}
|
||
} // namespace panda::ecmascript::base
|