* Copyright (c) 2024-2025 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.
*/
#ifndef ES2PANDA_UTIL_INCLUDE_DTOA_HELPER_H
#define ES2PANDA_UTIL_INCLUDE_DTOA_HELPER_H
#include <cstdint>
#include <cstddef>
#include <array>
#include "libarkbase/globals.h"
#include "util/es2pandaMacros.h"
namespace ark::es2panda::util {
constexpr int DOUBLE_MAX_PRECISION = 17;
constexpr int FLOAT_MAX_PRECISION = 9;
constexpr int DOUBLE_EXPONENT_BIAS = 0x3FF;
constexpr size_t DOUBLE_SIGNIFICAND_SIZE = 52;
constexpr uint64_t DOUBLE_SIGN_MASK = 0x8000000000000000ULL;
constexpr size_t DOUBLE_EXPONENT_SIZE = 11;
constexpr int DOUBLE_EXPONENT_MAX = 0x7FFULL;
constexpr uint64_t DOUBLE_EXPONENT_MASK = 0x7FFULL << DOUBLE_SIGNIFICAND_SIZE;
constexpr uint64_t DOUBLE_SIGNIFICAND_MASK = 0x000FFFFFFFFFFFFFULL;
constexpr uint64_t DOUBLE_HIDDEN_BIT = 1ULL << DOUBLE_SIGNIFICAND_SIZE;
class DtoaHelper {
public:
explicit DtoaHelper(char *buffer) : buffer_(buffer) {}
void Dtoa(double value);
int GetPoint()
{
return point_;
}
int GetDigits()
{
return length_;
}
private:
static constexpr int CACHED_POWERS_OFFSET = 348;
static constexpr double D_1_LOG2_10 = 0x1.34413509f79ffp-2;
static constexpr int Q = -61;
static constexpr int INDEX = 20;
static constexpr int MIN_DECIMAL_EXPONENT = -348;
static constexpr auto POW10 = std::array {1ULL,
10ULL,
100ULL,
1000ULL,
10000ULL,
100000ULL,
1000000ULL,
10000000ULL,
100000000ULL,
1000000000ULL,
10000000000ULL,
100000000000ULL,
1000000000000ULL,
10000000000000ULL,
100000000000000ULL,
1000000000000000ULL,
10000000000000000ULL,
100000000000000000ULL,
1000000000000000000ULL,
10000000000000000000ULL};
static constexpr uint32_t TEN = 10;
static constexpr uint32_t TEN2POW = 100;
static constexpr uint32_t TEN3POW = 1000;
static constexpr uint32_t TEN4POW = 10000;
static constexpr uint32_t TEN5POW = 100000;
static constexpr uint32_t TEN6POW = 1000000;
static constexpr uint32_t TEN7POW = 10000000;
static constexpr uint32_t TEN8POW = 100000000;
class DiyFp {
public:
DiyFp() : f_(), e_() {}
DiyFp(uint64_t fp, int exp) : f_(fp), e_(exp) {}
explicit DiyFp(double d)
{
union {
double d;
uint64_t u64;
} u = {d};
int biasedE =
static_cast<int>((u.u64 & DOUBLE_EXPONENT_MASK) >> DOUBLE_SIGNIFICAND_SIZE);
uint64_t significand = (u.u64 & DOUBLE_SIGNIFICAND_MASK);
if (biasedE != 0) {
f_ = significand + DOUBLE_HIDDEN_BIT;
e_ = biasedE - DP_EXPONENT_BIAS;
} else {
f_ = significand;
e_ = DP_MIN_EXPONENT + 1;
}
}
DiyFp operator-(const DiyFp &rhs) const
{
return DiyFp(f_ - rhs.f_, e_);
}
DiyFp operator*(const DiyFp &rhs) const
{
constexpr uint64_t M32 = UINT32_MAX;
const uint64_t a = f_ >> BITS_PER_UINT32;
const uint64_t b = f_ & M32;
const uint64_t c = rhs.f_ >> BITS_PER_UINT32;
const uint64_t d = rhs.f_ & M32;
const uint64_t ac = a * c;
const uint64_t bc = b * c;
const uint64_t ad = a * d;
const uint64_t bd = b * d;
uint64_t tmp = (bd >> BITS_PER_UINT32) + (ad & M32) + (bc & M32);
tmp += 1U << ROUND_BITS;
return DiyFp(ac + (ad >> BITS_PER_UINT32) + (bc >> BITS_PER_UINT32) + (tmp >> BITS_PER_UINT32),
e_ + rhs.e_ + BITS_PER_UINT64);
}
DiyFp Normalize() const
{
DiyFp res = *this;
while ((res.f_ & DOUBLE_HIDDEN_BIT) == 0) {
res.f_ <<= 1U;
res.e_--;
}
res.f_ <<= (DIY_SIGNIFICAND_SIZE - DOUBLE_SIGNIFICAND_SIZE - 1);
res.e_ = res.e_ - (DIY_SIGNIFICAND_SIZE - DOUBLE_SIGNIFICAND_SIZE - 1);
return res;
}
DiyFp NormalizeBoundary() const
{
DiyFp res = *this;
while ((res.f_ & (DOUBLE_HIDDEN_BIT << 1U)) == 0) {
res.f_ <<= 1U;
res.e_--;
}
res.f_ <<= (DIY_SIGNIFICAND_SIZE - DOUBLE_SIGNIFICAND_SIZE - 2);
res.e_ = res.e_ - (DIY_SIGNIFICAND_SIZE - DOUBLE_SIGNIFICAND_SIZE - 2);
return res;
}
void NormalizedBoundaries(DiyFp *minus, DiyFp *plus) const
{
DiyFp pl = DiyFp((f_ << 1U) + 1, e_ - 1).NormalizeBoundary();
DiyFp mi = (f_ == DOUBLE_HIDDEN_BIT) ? DiyFp((f_ << 2U) - 1, e_ - 2)
: DiyFp((f_ << 1U) - 1, e_ - 1);
ES2PANDA_ASSERT(mi.e_ >= pl.e_);
mi.f_ <<= static_cast<uint32_t>(mi.e_ - pl.e_);
mi.e_ = pl.e_;
*plus = pl;
*minus = mi;
}
static constexpr uint32_t ROUND_BITS = BITS_PER_UINT32 - 1;
static constexpr uint32_t DIY_SIGNIFICAND_SIZE = BITS_PER_UINT64;
static constexpr int32_t DP_EXPONENT_BIAS = DOUBLE_EXPONENT_BIAS + DOUBLE_SIGNIFICAND_SIZE;
static constexpr int32_t DP_MAX_EXPONENT = DOUBLE_EXPONENT_MAX - DP_EXPONENT_BIAS;
static constexpr int32_t DP_MIN_EXPONENT = -DP_EXPONENT_BIAS;
static constexpr int32_t DP_DENORMAL_EXPONENT = -DP_EXPONENT_BIAS + 1;
private:
uint64_t f_;
int e_;
friend class DtoaHelper;
};
DiyFp GetCachedPower(int e);
static DiyFp GetCachedPowerByIndex(std::size_t index);
void GrisuRound(uint64_t delta, uint64_t rest, uint64_t tenKappa, uint64_t distance);
static int CountDecimalDigit32(uint32_t n);
static uint32_t PopDigit(int kappa, uint32_t &p1);
void DigitGen(const DiyFp &w, const DiyFp &mp, uint64_t delta);
void Grisu(double value);
private:
char *buffer_;
int length_ {};
int point_ {};
int k_ {};
};
}
#endif