Use dragonbox dtos() throughout codebase, remove dtoa_milo/floaxie

Replace all float/double-to-string conversions with dragonbox-based
dtos() from str-util.hh for shortest representation guarantee.

Changes:
- src/value-pprint.cc: Use dtos() for half and vector<double>
- src/usdMtlx.cc: Use dtos() in to_xml_string templates
- src/pprinter.cc: Remove dtoa_milo include
- src/json-writer.cc: Remove dtoa_milo include and local dtos()
- src/tydra/threejs-exporter.cc: Use dtos() for MaterialX export
- src/tydra/js-script.cc: Use dtos() for all float/double/half streaming

Removed external libraries (no longer needed):
- src/external/dtoa_milo.h and LICENSE
- src/external/floaxie/ (22 files)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Syoyo Fujita
2026-01-14 08:36:12 +09:00
parent a0ea7c21b2
commit 808c6a9131
31 changed files with 60 additions and 4792 deletions

View File

@@ -1,19 +0,0 @@
Copyright (C) 2014 Milo Yip
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -1,514 +0,0 @@
// SPDX-License-Identifier: MIT
// Copyright (C) 2014 Milo Yip
// https://github.com/miloyip/dtoa-benchmark
//
#pragma once
#include <cassert>
#include <cmath>
#include <limits>
#include <cstring>
//#if defined(_MSC_VER)
//#include "msinttypes/stdint.h"
//#include <intrin.h>
//#else
// cstdint should be available for VS2019 or later
#include <cstdint>
//#endif
// TINYUSDZ: TODO: Completely disable int128 feature for portablity?
#if defined(__GNUC__)
#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && \
defined(__x86_64__)
namespace gcc_ints
{
__extension__ typedef __int128 int128;
__extension__ typedef unsigned __int128 uint128;
} // namespace gcc_ints
#endif
#endif
#define UINT64_C2(h, l) ((static_cast<uint64_t>(h) << 32) | static_cast<uint64_t>(l))
class Double {
public:
Double() {}
Double(double d) : d_(d) {}
Double(uint64_t u) : u_(u) {}
double Value() const { return d_; }
uint64_t Uint64Value() const { return u_; }
double NextPositiveDouble() const {
//RAPIDJSON_ASSERT(!Sign());
return Double(u_ + 1).Value();
}
bool Sign() const { return (u_ & kSignMask) != 0; }
uint64_t Significand() const { return u_ & kSignificandMask; }
int Exponent() const { return static_cast<int>(((u_ & kExponentMask) >> kSignificandSize) - kExponentBias); }
bool IsNan() const { return (u_ & kExponentMask) == kExponentMask && Significand() != 0; }
bool IsInf() const { return (u_ & kExponentMask) == kExponentMask && Significand() == 0; }
bool IsNanOrInf() const { return (u_ & kExponentMask) == kExponentMask; }
bool IsNormal() const { return (u_ & kExponentMask) != 0 || Significand() == 0; }
bool IsZero() const { return (u_ & (kExponentMask | kSignificandMask)) == 0; }
uint64_t IntegerSignificand() const { return IsNormal() ? Significand() | kHiddenBit : Significand(); }
int IntegerExponent() const { return (IsNormal() ? Exponent() : kDenormalExponent) - kSignificandSize; }
uint64_t ToBias() const { return (u_ & kSignMask) ? ~u_ + 1 : u_ | kSignMask; }
static int EffectiveSignificandSize(int order) {
if (order >= -1021)
return 53;
else if (order <= -1074)
return 0;
else
return order + 1074;
}
private:
static const int kSignificandSize = 52;
static const int kExponentBias = 0x3FF;
static const int kDenormalExponent = 1 - kExponentBias;
static const uint64_t kSignMask = UINT64_C2(0x80000000, 0x00000000);
static const uint64_t kExponentMask = UINT64_C2(0x7FF00000, 0x00000000);
static const uint64_t kSignificandMask = UINT64_C2(0x000FFFFF, 0xFFFFFFFF);
static const uint64_t kHiddenBit = UINT64_C2(0x00100000, 0x00000000);
union {
double d_;
uint64_t u_;
};
};
struct DiyFp {
DiyFp() {}
DiyFp(uint64_t _f, int _e) : f(_f), e(_e) {}
DiyFp(double d) {
union {
double d;
uint64_t u64;
} u = { d };
int biased_e = (u.u64 & kDpExponentMask) >> kDpSignificandSize;
uint64_t significand = (u.u64 & kDpSignificandMask);
if (biased_e != 0) {
f = significand + kDpHiddenBit;
e = biased_e - kDpExponentBias;
}
else {
f = significand;
e = kDpMinExponent + 1;
}
}
DiyFp operator-(const DiyFp& rhs) const {
assert(e == rhs.e);
assert(f >= rhs.f);
return DiyFp(f - rhs.f, e);
}
DiyFp operator*(const DiyFp& rhs) const {
#if defined(_MSC_VER) && defined(_M_AMD64)
uint64_t h;
uint64_t l = _umul128(f, rhs.f, &h);
if (l & (uint64_t(1) << 63)) // rounding
h++;
return DiyFp(h, e + rhs.e + 64);
#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__)
gcc_ints::uint128 p = static_cast<gcc_ints::uint128>(f) * static_cast<gcc_ints::uint128>(rhs.f);
uint64_t h = p >> 64;
uint64_t l = static_cast<uint64_t>(p);
if (l & (uint64_t(1) << 63)) // rounding
h++;
return DiyFp(h, e + rhs.e + 64);
#else
const uint64_t M32 = 0xFFFFFFFF;
const uint64_t a = f >> 32;
const uint64_t b = f & M32;
const uint64_t c = rhs.f >> 32;
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 >> 32) + (ad & M32) + (bc & M32);
tmp += 1U << 31; /// mult_round
return DiyFp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32), e + rhs.e + 64);
#endif
}
DiyFp Normalize() const {
#if defined(_MSC_VER) && defined(_M_AMD64)
unsigned long index;
_BitScanReverse64(&index, f);
return DiyFp(f << (63 - int(index)), e - (63 - int(index)));
#elif defined(__GNUC__)
int s = __builtin_clzll(f);
return DiyFp(f << s, e - s);
#else
DiyFp res = *this;
while (!(res.f & kDpHiddenBit)) {
res.f <<= 1;
res.e--;
}
res.f <<= (kDiySignificandSize - kDpSignificandSize - 1);
res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 1);
return res;
#endif
}
DiyFp NormalizeBoundary() const {
#if defined(_MSC_VER) && defined(_M_AMD64)
unsigned long index;
_BitScanReverse64(&index, f);
return DiyFp (f << (63 - int(index)), e - (63 - int(index)));
#else
DiyFp res = *this;
while (!(res.f & (kDpHiddenBit << 1))) {
res.f <<= 1;
res.e--;
}
res.f <<= (kDiySignificandSize - kDpSignificandSize - 2);
res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 2);
return res;
#endif
}
void NormalizedBoundaries(DiyFp* minus, DiyFp* plus) const {
DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary();
DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2) : DiyFp((f << 1) - 1, e - 1);
mi.f <<= mi.e - pl.e;
mi.e = pl.e;
*plus = pl;
*minus = mi;
}
static const int kDiySignificandSize = 64;
static const int kDpSignificandSize = 52;
static const int kDpExponentBias = 0x3FF + kDpSignificandSize;
static const int kDpMinExponent = -kDpExponentBias;
static const uint64_t kDpExponentMask = UINT64_C2(0x7FF00000, 0x00000000);
static const uint64_t kDpSignificandMask = UINT64_C2(0x000FFFFF, 0xFFFFFFFF);
static const uint64_t kDpHiddenBit = UINT64_C2(0x00100000, 0x00000000);
uint64_t f;
int e;
};
inline DiyFp GetCachedPower(int e, int* K) {
// 10^-348, 10^-340, ..., 10^340
static const uint64_t kCachedPowers_F[] = {
UINT64_C2(0xfa8fd5a0, 0x081c0288), UINT64_C2(0xbaaee17f, 0xa23ebf76),
UINT64_C2(0x8b16fb20, 0x3055ac76), UINT64_C2(0xcf42894a, 0x5dce35ea),
UINT64_C2(0x9a6bb0aa, 0x55653b2d), UINT64_C2(0xe61acf03, 0x3d1a45df),
UINT64_C2(0xab70fe17, 0xc79ac6ca), UINT64_C2(0xff77b1fc, 0xbebcdc4f),
UINT64_C2(0xbe5691ef, 0x416bd60c), UINT64_C2(0x8dd01fad, 0x907ffc3c),
UINT64_C2(0xd3515c28, 0x31559a83), UINT64_C2(0x9d71ac8f, 0xada6c9b5),
UINT64_C2(0xea9c2277, 0x23ee8bcb), UINT64_C2(0xaecc4991, 0x4078536d),
UINT64_C2(0x823c1279, 0x5db6ce57), UINT64_C2(0xc2109436, 0x4dfb5637),
UINT64_C2(0x9096ea6f, 0x3848984f), UINT64_C2(0xd77485cb, 0x25823ac7),
UINT64_C2(0xa086cfcd, 0x97bf97f4), UINT64_C2(0xef340a98, 0x172aace5),
UINT64_C2(0xb23867fb, 0x2a35b28e), UINT64_C2(0x84c8d4df, 0xd2c63f3b),
UINT64_C2(0xc5dd4427, 0x1ad3cdba), UINT64_C2(0x936b9fce, 0xbb25c996),
UINT64_C2(0xdbac6c24, 0x7d62a584), UINT64_C2(0xa3ab6658, 0x0d5fdaf6),
UINT64_C2(0xf3e2f893, 0xdec3f126), UINT64_C2(0xb5b5ada8, 0xaaff80b8),
UINT64_C2(0x87625f05, 0x6c7c4a8b), UINT64_C2(0xc9bcff60, 0x34c13053),
UINT64_C2(0x964e858c, 0x91ba2655), UINT64_C2(0xdff97724, 0x70297ebd),
UINT64_C2(0xa6dfbd9f, 0xb8e5b88f), UINT64_C2(0xf8a95fcf, 0x88747d94),
UINT64_C2(0xb9447093, 0x8fa89bcf), UINT64_C2(0x8a08f0f8, 0xbf0f156b),
UINT64_C2(0xcdb02555, 0x653131b6), UINT64_C2(0x993fe2c6, 0xd07b7fac),
UINT64_C2(0xe45c10c4, 0x2a2b3b06), UINT64_C2(0xaa242499, 0x697392d3),
UINT64_C2(0xfd87b5f2, 0x8300ca0e), UINT64_C2(0xbce50864, 0x92111aeb),
UINT64_C2(0x8cbccc09, 0x6f5088cc), UINT64_C2(0xd1b71758, 0xe219652c),
UINT64_C2(0x9c400000, 0x00000000), UINT64_C2(0xe8d4a510, 0x00000000),
UINT64_C2(0xad78ebc5, 0xac620000), UINT64_C2(0x813f3978, 0xf8940984),
UINT64_C2(0xc097ce7b, 0xc90715b3), UINT64_C2(0x8f7e32ce, 0x7bea5c70),
UINT64_C2(0xd5d238a4, 0xabe98068), UINT64_C2(0x9f4f2726, 0x179a2245),
UINT64_C2(0xed63a231, 0xd4c4fb27), UINT64_C2(0xb0de6538, 0x8cc8ada8),
UINT64_C2(0x83c7088e, 0x1aab65db), UINT64_C2(0xc45d1df9, 0x42711d9a),
UINT64_C2(0x924d692c, 0xa61be758), UINT64_C2(0xda01ee64, 0x1a708dea),
UINT64_C2(0xa26da399, 0x9aef774a), UINT64_C2(0xf209787b, 0xb47d6b85),
UINT64_C2(0xb454e4a1, 0x79dd1877), UINT64_C2(0x865b8692, 0x5b9bc5c2),
UINT64_C2(0xc83553c5, 0xc8965d3d), UINT64_C2(0x952ab45c, 0xfa97a0b3),
UINT64_C2(0xde469fbd, 0x99a05fe3), UINT64_C2(0xa59bc234, 0xdb398c25),
UINT64_C2(0xf6c69a72, 0xa3989f5c), UINT64_C2(0xb7dcbf53, 0x54e9bece),
UINT64_C2(0x88fcf317, 0xf22241e2), UINT64_C2(0xcc20ce9b, 0xd35c78a5),
UINT64_C2(0x98165af3, 0x7b2153df), UINT64_C2(0xe2a0b5dc, 0x971f303a),
UINT64_C2(0xa8d9d153, 0x5ce3b396), UINT64_C2(0xfb9b7cd9, 0xa4a7443c),
UINT64_C2(0xbb764c4c, 0xa7a44410), UINT64_C2(0x8bab8eef, 0xb6409c1a),
UINT64_C2(0xd01fef10, 0xa657842c), UINT64_C2(0x9b10a4e5, 0xe9913129),
UINT64_C2(0xe7109bfb, 0xa19c0c9d), UINT64_C2(0xac2820d9, 0x623bf429),
UINT64_C2(0x80444b5e, 0x7aa7cf85), UINT64_C2(0xbf21e440, 0x03acdd2d),
UINT64_C2(0x8e679c2f, 0x5e44ff8f), UINT64_C2(0xd433179d, 0x9c8cb841),
UINT64_C2(0x9e19db92, 0xb4e31ba9), UINT64_C2(0xeb96bf6e, 0xbadf77d9),
UINT64_C2(0xaf87023b, 0x9bf0ee6b)
};
static const int16_t kCachedPowers_E[] = {
-1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980,
-954, -927, -901, -874, -847, -821, -794, -768, -741, -715,
-688, -661, -635, -608, -582, -555, -529, -502, -475, -449,
-422, -396, -369, -343, -316, -289, -263, -236, -210, -183,
-157, -130, -103, -77, -50, -24, 3, 30, 56, 83,
109, 136, 162, 189, 216, 242, 269, 295, 322, 348,
375, 402, 428, 455, 481, 508, 534, 561, 588, 614,
641, 667, 694, 720, 747, 774, 800, 827, 853, 880,
907, 933, 960, 986, 1013, 1039, 1066
};
//int k = static_cast<int>(ceil((-61 - e) * 0.30102999566398114)) + 374;
double dk = (-61 - e) * 0.30102999566398114 + 347; // dk must be positive, so can do ceiling in positive
int k = static_cast<int>(dk);
if (dk - k > 0.0)
k++;
unsigned index = static_cast<unsigned>((k >> 3) + 1);
*K = -(-348 + static_cast<int>(index << 3)); // decimal exponent no need lookup table
assert(index < sizeof(kCachedPowers_F) / sizeof(kCachedPowers_F[0]));
return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]);
}
inline const char* GetDigitsLut() {
static const char cDigitsLut[200] = {
'0', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '5', '0', '6', '0', '7', '0', '8', '0', '9',
'1', '0', '1', '1', '1', '2', '1', '3', '1', '4', '1', '5', '1', '6', '1', '7', '1', '8', '1', '9',
'2', '0', '2', '1', '2', '2', '2', '3', '2', '4', '2', '5', '2', '6', '2', '7', '2', '8', '2', '9',
'3', '0', '3', '1', '3', '2', '3', '3', '3', '4', '3', '5', '3', '6', '3', '7', '3', '8', '3', '9',
'4', '0', '4', '1', '4', '2', '4', '3', '4', '4', '4', '5', '4', '6', '4', '7', '4', '8', '4', '9',
'5', '0', '5', '1', '5', '2', '5', '3', '5', '4', '5', '5', '5', '6', '5', '7', '5', '8', '5', '9',
'6', '0', '6', '1', '6', '2', '6', '3', '6', '4', '6', '5', '6', '6', '6', '7', '6', '8', '6', '9',
'7', '0', '7', '1', '7', '2', '7', '3', '7', '4', '7', '5', '7', '6', '7', '7', '7', '8', '7', '9',
'8', '0', '8', '1', '8', '2', '8', '3', '8', '4', '8', '5', '8', '6', '8', '7', '8', '8', '8', '9',
'9', '0', '9', '1', '9', '2', '9', '3', '9', '4', '9', '5', '9', '6', '9', '7', '9', '8', '9', '9'
};
return cDigitsLut;
}
inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) {
while (rest < wp_w && delta - rest >= ten_kappa &&
(rest + ten_kappa < wp_w || /// closer
wp_w - rest > rest + ten_kappa - wp_w)) {
buffer[len - 1]--;
rest += ten_kappa;
}
}
inline int CountDecimalDigit32(uint32_t n) {
// Simple pure C++ implementation was faster than __builtin_clz version in this situation.
if (n < 10) return 1;
if (n < 100) return 2;
if (n < 1000) return 3;
if (n < 10000) return 4;
if (n < 100000) return 5;
if (n < 1000000) return 6;
if (n < 10000000) return 7;
if (n < 100000000) return 8;
// Will not reach 10 digits in DigitGen()
//if (n < 1000000000) return 9;
//return 10;
return 9;
}
inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) {
static const uint64_t kPow10[] = { 1ULL, 10ULL, 100ULL, 1000ULL, 10000ULL, 100000ULL, 1000000ULL, 10000000ULL, 100000000ULL,
1000000000ULL, 10000000000ULL, 100000000000ULL, 1000000000000ULL,
10000000000000ULL, 100000000000000ULL, 1000000000000000ULL,
10000000000000000ULL, 100000000000000000ULL, 1000000000000000000ULL,
10000000000000000000ULL };
const DiyFp one(uint64_t(1) << -Mp.e, Mp.e);
const DiyFp wp_w = Mp - W;
uint32_t p1 = static_cast<uint32_t>(Mp.f >> -one.e);
uint64_t p2 = Mp.f & (one.f - 1);
int kappa = CountDecimalDigit32(p1); // kappa in [0, 9]
*len = 0;
while (kappa > 0) {
uint32_t d = 0;
switch (kappa) {
case 9: d = p1 / 100000000; p1 %= 100000000; break;
case 8: d = p1 / 10000000; p1 %= 10000000; break;
case 7: d = p1 / 1000000; p1 %= 1000000; break;
case 6: d = p1 / 100000; p1 %= 100000; break;
case 5: d = p1 / 10000; p1 %= 10000; break;
case 4: d = p1 / 1000; p1 %= 1000; break;
case 3: d = p1 / 100; p1 %= 100; break;
case 2: d = p1 / 10; p1 %= 10; break;
case 1: d = p1; p1 = 0; break;
default:;
}
if (d || *len)
buffer[(*len)++] = static_cast<char>('0' + static_cast<char>(d));
kappa--;
uint64_t tmp = (static_cast<uint64_t>(p1) << -one.e) + p2;
if (tmp <= delta) {
*K += kappa;
GrisuRound(buffer, *len, delta, tmp, kPow10[kappa] << -one.e, wp_w.f);
return;
}
}
// kappa = 0
for (;;) {
p2 *= 10;
delta *= 10;
char d = static_cast<char>(p2 >> -one.e);
if (d || *len)
buffer[(*len)++] = static_cast<char>('0' + d);
p2 &= one.f - 1;
kappa--;
if (p2 < delta) {
*K += kappa;
int index = -kappa;
GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 20 ? kPow10[index] : 0));
return;
}
}
}
inline void Grisu2(double value, char* buffer, int* length, int* K) {
const DiyFp v(value);
DiyFp w_m, w_p;
v.NormalizedBoundaries(&w_m, &w_p);
const DiyFp c_mk = GetCachedPower(w_p.e, K);
const DiyFp W = v.Normalize() * c_mk;
DiyFp Wp = w_p * c_mk;
DiyFp Wm = w_m * c_mk;
Wm.f++;
Wp.f--;
DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K);
}
inline char* WriteExponent(int K, char* buffer) {
if (K < 0) {
*buffer++ = '-';
K = -K;
}
if (K >= 100) {
*buffer++ = static_cast<char>('0' + static_cast<char>(K / 100));
K %= 100;
const char* d = GetDigitsLut() + K * 2;
*buffer++ = d[0];
*buffer++ = d[1];
}
else if (K >= 10) {
const char* d = GetDigitsLut() + K * 2;
*buffer++ = d[0];
*buffer++ = d[1];
}
else
*buffer++ = static_cast<char>('0' + static_cast<char>(K));
return buffer;
}
inline char* Prettify(char* buffer, int length, int k, int maxDecimalPlaces) {
const int kk = length + k; // 10^(kk-1) <= v < 10^kk
if (0 <= k && kk <= 21) {
// 1234e7 -> 12340000000
for (int i = length; i < kk; i++)
buffer[i] = '0';
buffer[kk] = '.';
buffer[kk + 1] = '0';
return &buffer[kk + 2];
}
else if (0 < kk && kk <= 21) {
// 1234e-2 -> 12.34
std::memmove(&buffer[kk + 1], &buffer[kk], static_cast<size_t>(length - kk));
buffer[kk] = '.';
if (0 > k + maxDecimalPlaces) {
// When maxDecimalPlaces = 2, 1.2345 -> 1.23, 1.102 -> 1.1
// Remove extra trailing zeros (at least one) after truncation.
for (int i = kk + maxDecimalPlaces; i > kk + 1; i--)
if (buffer[i] != '0')
return &buffer[i + 1];
return &buffer[kk + 2]; // Reserve one zero
}
else
return &buffer[length + 1];
}
else if (-6 < kk && kk <= 0) {
// 1234e-6 -> 0.001234
const int offset = 2 - kk;
std::memmove(&buffer[offset], &buffer[0], static_cast<size_t>(length));
buffer[0] = '0';
buffer[1] = '.';
for (int i = 2; i < offset; i++)
buffer[i] = '0';
if (length - kk > maxDecimalPlaces) {
// When maxDecimalPlaces = 2, 0.123 -> 0.12, 0.102 -> 0.1
// Remove extra trailing zeros (at least one) after truncation.
for (int i = maxDecimalPlaces + 1; i > 2; i--)
if (buffer[i] != '0')
return &buffer[i + 1];
return &buffer[3]; // Reserve one zero
}
else
return &buffer[length + offset];
}
else if (kk < -maxDecimalPlaces) {
// Truncate to zero
buffer[0] = '0';
buffer[1] = '.';
buffer[2] = '0';
return &buffer[3];
}
else if (length == 1) {
// 1e30
buffer[1] = 'e';
return WriteExponent(kk - 1, &buffer[2]);
}
else {
// 1234e30 -> 1.234e33
std::memmove(&buffer[2], &buffer[1], static_cast<size_t>(length - 1));
buffer[1] = '.';
buffer[length + 1] = 'e';
return WriteExponent(kk - 1, &buffer[0 + length + 2]);
}
}
inline char *dtoa_milo(double value, char* buffer) {
Double d(value);
if (d.IsZero()) {
if (d.Sign())
*buffer++ = '-'; // -0.0, Issue #289
buffer[0] = '0';
buffer[1] = '.';
buffer[2] = '0';
return &buffer[3];
#if 0
} else if (d.IsInf()) {
if (d.Sign())
*buffer++ = '-';
buffer[0] = 'i';
buffer[1] = 'n';
buffer[2] = 'f';
return &buffer[3];
} else if (d.IsNan()) {
if (d.Sign())
*buffer++ = '-';
buffer[0] = 'n';
buffer[1] = 'a';
buffer[2] = 'n';
return &buffer[3];
#endif
} else {
if (value < 0) {
*buffer++ = '-';
value = -value;
}
int length, K;
Grisu2(value, buffer, &length, &K);
int maxDecimalPlaces = 324;
return Prettify(buffer, length, K, maxDecimalPlaces);
}
}

View File

@@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View File

@@ -1,112 +0,0 @@
floaxie
=======
[![Build Status in GCC/Clang](https://travis-ci.org/aclex/floaxie.svg?branch=master)](https://travis-ci.org/aclex/floaxie) [![Build status in Visual Studio](https://ci.appveyor.com/api/projects/status/nhidn1n2o66etirk?svg=true)](https://ci.appveyor.com/project/aclex/floaxie) [![Code coverage](https://codecov.io/gh/aclex/floaxie/branch/master/graph/badge.svg)](https://codecov.io/gh/aclex/floaxie)
Floaxie is C++14 header-only library for [fast](https://github.com/miloyip/dtoa-benchmark/) printing floating point values of arbitrary precision (`float`, `double` etc.) and parsing their textual representation back again (in ordinary or exponent notation).
What is it for?
---------------
One is obviously not worried too much about the speed of the values being printed on the console, so the primary places to use the library are readers, writers, encoders and decoders of different text-based formats (e.g. JSON, XML and so on), applications interacting through text pipes or rendering data structures.
Please, take a look also at the alternatives solving the same problem mentioned in the benchmark of @miloyip [here](https://github.com/miloyip/dtoa-benchmark/), if `floaxie` doesn't suite you well.
Compiler compatibility
----------------------
- [x] GCC 5
- [x] Clang 3.6
- [x] Visual Studio 2017 15.0
Printing
--------
**Grisu2** algorithm is adopted for printing floating point values. It is fast printing algorithm described by Florian Loitsch in his [Printing Floating-Point Numbers Quickly and Accurately with Integers](http://florian.loitsch.com/publications/dtoa-pldi2010.pdf) paper. **Grisu2** is chosen as probably the fastest **grisu** version, for cases, where shortest possible representation in 100% of cases is not ultimately important. Still it guarantees the best possible efficiency of more, than 99%.
Parsing
-------
The opposite to **Grisu** algorithm used for printing, an algorithm on the same theoretical base, but for parsing, is developed. Following the analogue of **Grisu** naming, who essentially appears to be cartoon character (Dragon), parsing algorithm is named after another animated character, rabbit, **Krosh:** ![Krosh](http://img4.wikia.nocookie.net/__cb20130427170555/smesharikiarhives/ru/images/0/03/%D0%9A%D1%80%D0%BE%D1%88.png "Krosh")
The algorithm parses decimal mantissa to extent of slightly more decimal digit capacity of floating point types, chooses a pre-calculated decimal power and then multiplies the two. Since the [rounding problem](http://www.exploringbinary.com/decimal-to-floating-point-needs-arbitrary-precision/) is not uncommon during such operations, and, in contrast to printing problem, one can't just return incorrectly rounded parsing results, such cases are detected instead and slower, but accurate fallback conversion is performed (C Standard Library functions like `strtod()` by default). In this respect **Krosh** is closer to **Grisu3**.
Example
-------
**Printing:**
```cpp
#include <iostream>
#include "floaxie/ftoa.h"
using namespace std;
using namespace floaxie;
int main(int, char**)
{
double pi = 0.1;
char buffer[128];
ftoa(pi, buffer);
cout << "pi: " << pi << ", buffer: " << buffer << endl;
return 0;
}
```
**Parsing:**
```cpp
#include <iostream>
#include "floaxie/atof.h"
using namespace std;
using namespace floaxie;
int main(int, char**)
{
char str[] = "0.1";
char* str_end;
double pi = 0;
pi = atof<double>(str, &str_end);
cout << "pi: " << pi << ", str: " << str << ", str_end: " << str_end << endl;
return 0;
}
```
Building
--------
Building is not required and completely optional, unless you would like to try the examples or tests or build the local documentation. Inside `git` project tree it can be done like this:
```shell
git submodule update --init # to check out common CMake modules' submodule
mkdir build && cd build
cmake -DBUILD_EXAMPLES=1 -DBUILD_TESTS=1 -DBUILD_DOCUMENTATION=1 ../ # switch on the desired flags
cmake --build . # or just `make` on systems with it
```
Adding to the project
---------------------
```shell
git submodule add https://github.com/aclex/floaxie <desired-path-in-your-project>
```
Don't forget to do `git submodule update --init --recursive` out of your project tree to pull submodules properly.
Including to CMake project as a subproject
-------
Since version 1.2 it's possible to include Floaxie as a subproject in any CMake project quite easily thanks to modern CMake `INTERFACE_LIBRARY` target facilities. Unfortunately, this works fully since CMake 3.13, so its minimum required version had to be increased.
`CMakeLists.txt` of a consumer CMake project would look like this (given Floaxie is cloned to `floaxie` subdirectory)':
```cmake
project(foo)
cmake_minimum_required(VERSION 3.13)
# `EXCLUDE_FOR_ALL` here to exclude supplementary targets
# like `install` from the main project target set
add_subdirectory(peli EXCLUDE_FROM_ALL)
add_executable(foo_main foo_main.cpp)
target_link_libraries(foo_main PUBLIC floaxie)
```

View File

@@ -1,179 +0,0 @@
/*
* Copyright 2015-2019 Alexey Chernov <4ernov@gmail.com>
*
* 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 FLOAXIE_ATOF_H
#define FLOAXIE_ATOF_H
#include <string>
#include <floaxie/krosh.h>
#include <floaxie/default_fallback.h>
#include <floaxie/conversion_status.h>
/** \brief Floaxie functions templates.
*
* This namespace contains two main public floaxie functions (`atof()` and
* `ftoa()`), as well as several helper functions (e.g. `max_buffer_size()`)
* and internal type and function templates.
*/
namespace floaxie
{
/** \brief Small decorator around returning value to help the client
* optionally receive minor error states along with it.
*
* \tparam FloatType target floating point type to store results.
*/
template<typename FloatType> struct value_and_status
{
/** \brief The returning result value itself. */
FloatType value;
/** \brief Conversion status indicating any problems occurred. */
conversion_status status;
/** \brief Constructs the object with empty value and successful status. */
value_and_status() noexcept : value(), status(conversion_status::success) { }
/** \brief Default conversion operator to `FloatType` to make use of the
* wrapper more transparent. */
operator FloatType() const noexcept { return value; }
};
/** \brief Parses floating point string representation.
*
* Interprets string representation of floating point value using Krosh
* algorithm and, if successful, value of the specified type is returned.
*
* The accepted representation format is ordinary or exponential decimal
* floating point expression, containing:
* - optional sign ('+' or '-')
* - sequence of one or more decimal digits optionally containing decimal
* point character ('.')
* - optional 'e' of 'E' character followed by optional sign ('+' or '-')
* and sequence of one or more decimal digits.
*
* Function doesn't expect any preceding spacing characters and treats the
* representation as incorrect, if there's any.
*
* \tparam FloatType target floating point type to store results.
* \tparam CharType character type (typically `char` or `wchar_t`) the input
* string \p **str** consists of.
* \tparam FallbackCallable fallback conversion function type, in case of
* Krosh is unsure if the result is correctly rounded (default is `strtof()`
* for `float`'s, `strtod()` for `double`'s, `strtold()` for `long double`'s).
*
* \param str buffer containing the string representation of the value.
* \param str_end out parameter, which will contain a pointer to first
* character after the parsed value in the specified buffer. If str_end is
* null, it is ignored.
* \param fallback_func pointer to fallback function. If omitted, by default
* is `strtof()` for `float`'s, `strtod()` for `double`'s, `strtold()` for
* `long double`'s. Null value will lead to undefined behaviour in case of
* algorithm is unsure and fall back to using it.
*
* \return structure containing the parsed value, if the
* input is correct (default constructed value otherwise) and status of the
* conversion made.
*
* \sa `value_and_status`
* \sa `conversion_status`
*/
template
<
typename FloatType,
typename CharType,
typename FallbackCallable = FloatType (const CharType*, CharType**)
>
inline value_and_status<FloatType> atof(const CharType* str, CharType** str_end, FallbackCallable fallback_func = default_fallback<FloatType, CharType>)
{
value_and_status<FloatType> result;
const auto& cr(krosh<FloatType>(str));
if (cr.str_end != str)
{
if (cr.is_accurate)
{
result.value = cr.value;
result.status = cr.status;
}
else
{
result.value = fallback_func(str, str_end);
result.status = check_errno(result.value);
return result;
}
}
if (str_end)
*str_end = const_cast<CharType*>(cr.str_end);
return result;
}
/** \brief Tiny overload for `atof()` function to allow passing `nullptr`
* as `str_end` parameter.
*/
template
<
typename FloatType,
typename CharType,
typename FallbackCallable = FloatType (const CharType*, CharType**)
>
inline value_and_status<FloatType> atof(const CharType* str, std::nullptr_t str_end, FallbackCallable fallback_func = default_fallback<FloatType, CharType>)
{
return atof<FloatType, CharType, FallbackCallable>(str, static_cast<CharType**>(str_end), fallback_func);
}
/** \brief Parses floating point represented in `std::basic_string`.
*
* `atof()` adapter, which may be more useful for cases, where
* `std::basic_string` strings are widely used.
*
* \tparam FloatType target floating point type to store results.
* \tparam CharType character type (typically `char` or `wchar_t`) the input
* string \p **str** consists of.
* \tparam FallbackCallable fallback conversion function type, in case of
* Krosh is unsure if the result is correctly rounded (default is `strtof()`
* for `float`'s, `strtod()` for `double`'s, `strtold()` for `long double`'s).
*
* \param str string representation of the value.
* \param fallback_func pointer to fallback function. If omitted, by default
* is `strtof()` for `float`'s, `strtod()` for `double`'s, `strtold()` for
* `long double`'s. Null value will lead to undefined behaviour in case of
* algorithm is unsure and fall back to using it.
*
* \return structure containing the parsed value, if the
* input is correct (default constructed value otherwise) and status of the
* conversion made.
*
* \sa `value_and_status`
* \sa `conversion_status`
*/
template
<
typename FloatType,
typename CharType,
typename FallbackCallable = FloatType (const CharType*, CharType**)
>
inline value_and_status<FloatType> from_string(const std::basic_string<CharType>& str, FallbackCallable fallback_func = default_fallback<FloatType, CharType>)
{
return atof<FloatType>(str.c_str(), nullptr, fallback_func);
}
}
#endif // FLOAXIE_ATOF_H

View File

@@ -1,220 +0,0 @@
/*
* Copyright 2015-2019 Alexey Chernov <4ernov@gmail.com>
*
* 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.
*
* diy_fp class and helper functions use code and influenced by
* Florian Loitsch's original Grisu algorithms implementation
* (http://florian.loitsch.com/publications/bench.tar.gz)
* and "Printing Floating-Point Numbers Quickly and Accurately with
* Integers" paper
* (http://florian.loitsch.com/publications/dtoa-pldi2010.pdf)
*/
#ifndef FLOAXIE_BIT_OPS_H
#define FLOAXIE_BIT_OPS_H
#include <limits>
#include <algorithm>
#include <cstddef>
#include <cassert>
#include <external/floaxie/floaxie/integer_of_size.h>
namespace floaxie
{
/** \brief Calculates size of type in bits in compile time.
*
* \tparam NumericType type to calculate size in bits of.
*
* \return type size in bits.
*/
template<typename NumericType> constexpr std::size_t bit_size() noexcept
{
return sizeof(NumericType) * std::numeric_limits<unsigned char>::digits;
}
/** \brief Returns a value with bit of the specified power raised.
*
* Calculates a value, which equals to 2 in the specified power, i.e. with
* bit at \p `power` position is `1` and all the remaining bits are `0`.
*
* \tparam NumericType type to store the calculated value.
*
* \param power power (0-based index, counting right to left) of bit to
* raise.
*
* \return value of \p **NumericType** with \p **power**-th bit is `1` and
* all the remaining bits are `0`.
*/
template<typename NumericType> constexpr NumericType raised_bit(std::size_t power)
{
assert(power < bit_size<NumericType>());
return NumericType(1) << power;
}
/** \brief Returns Most Significant Bit (MSB) value for the specified type.
*
* Calculates a value, which is equal to the value of Most
* Significant Bit of the integer type, which has the same length, as the
* specified one. The left most bit of the calculated value is equal to
* `1`, and the remaining bits are `0`.
*
* \tparam FloatType type to calculate MSB value for.
* \tparam NumericType integer type of the same size, as \p **FloatType**.
*
* \return value of Most Significant Bit (MSB).
*/
template<typename FloatType,
typename NumericType = typename integer_of_size<sizeof(FloatType)>::type>
constexpr NumericType msb_value() noexcept
{
return raised_bit<NumericType>(bit_size<NumericType>() - 1);
}
/** \brief Returns maximum unsigned integer value for the specified type.
*
* Calculates maximum value (using `std::numeric_limits`) of the integer
* type, which has the same length, as the specified one. Thus, all bits
* of the calculated value are equal to `1`.
*
* \tparam FloatType type to calculate MSB value for.
* \tparam NumericType integer type of the same size, as \p **FloatType**.
*
* \return maximum value of size the same as of the specified type.
*/
template<typename FloatType,
typename NumericType = typename integer_of_size<sizeof(FloatType)>::type>
constexpr NumericType max_integer_value() noexcept
{
return std::numeric_limits<NumericType>::max();
}
/** \brief Masks `n`-th bit of the specified value.
*
* Calculates a mask standing for the `n`-th bit, performs bitwise **AND**
* operation and returns the value of it.
*
* \param value the value, of which the specified bit is returned.
* \param power power (0-based right-to-left index) of bit to return.
*
* \return integer value, which has \p `power`-th bit of the \p **value**
* and the remaining bits equal to `0`.
*
*/
template<typename NumericType> constexpr bool nth_bit(NumericType value, std::size_t power) noexcept
{
return value & raised_bit<NumericType>(power);
}
/** \brief Returns Most Significant Bit (MSB) of the specified value.
*
* Masks the left most bit of the given value, performs bitwise **AND**
* operation with the mask and the value and returns the result.
*
* \tparam NumericType type of the value.
*
* \param value value to get the highest bit of.
*
* \return integer value, which left most bit of the \p **value** and the
* remaining bits equal to `0`.
*/
template<typename NumericType> constexpr bool highest_bit(NumericType value) noexcept
{
return nth_bit(value, bit_size<NumericType>() - 1);
}
/** \brief Returns mask of \p **n** bits from the right.
*
* \tparam NumericType type of the returned value.
* \param n number of bits from the right to mask.
*
* \return integer value with \p **n** right bits equal to `1` and the
* remaining bits equal to `0`.
*/
template<typename NumericType> constexpr NumericType mask(std::size_t n) noexcept
{
static_assert(!std::is_signed<NumericType>::value, "Unsigned integral type is expected.");
return n < bit_size<NumericType>() ? raised_bit<NumericType>(n) - 1 : std::numeric_limits<NumericType>::max();
}
/** \brief Rectified linear function.
*
* Returns the argument (\p **value**), if it's positive and `0` otherwise.
*
* \param value the argument.
*
* \return \p **value**, if \p **value** > `0`, `0` otherwise.
*
*/
template<typename NumericType> constexpr typename std::make_unsigned<NumericType>::type positive_part(NumericType value) noexcept
{
return static_cast<typename std::make_unsigned<NumericType>::type>((std::max)(0, value));
}
/** \brief Return structure for `round_up` function. */
struct round_result
{
/** \brief Round up result — flag indicating if the value should be
*rounded up (i.e. incremented).
*/
bool value;
/** \brief Flag indicating if the rounding was accurate. */
bool is_accurate;
};
/** \brief Detects if rounding up should be done.
*
* Applies IEEE algorithm of rounding up detection. The rounding criteria
* requires, that rounding bit (the bit to the right of target position,
* which rounding is being performed to) equals to `1`, and one of the
* following conditions is true: * - at least one bit to the right of the rounding bit equals to `1`
* - the bit in the target position equals to `1`
*
* \tparam NumericType type of \p **last_bits** parameter (auto-calculated).
*
* \param last_bits right suffix of the value, where rounding takes place.
* \param round_to_power the power (0-based right-to-left index) of the
* target position (which rounding is being performed to). According to the
* algorithm math it should be greater, than zero, otherwise behaviour is
* undefined.
*
* \returns `round_result` structure with the rounding decision.
*/
template<typename NumericType> inline round_result round_up(NumericType last_bits, std::size_t round_to_power) noexcept
{
round_result ret;
assert(round_to_power > 0);
const NumericType round_bit(raised_bit<NumericType>(round_to_power - 1));
const NumericType check_mask(mask<NumericType>(round_to_power + 1) ^ round_bit);
ret.is_accurate = (last_bits & mask<NumericType>(round_to_power)) != round_bit;
ret.value = (last_bits & round_bit) && (last_bits & check_mask);
return ret;
}
/** \brief `constexpr` version of `std::abs`, as the latter lacks `constepxr`.
*
* And is really not `constexpr` in e.g. Clang.
*
* \returns absolute value of the specified value.
*/
template<typename NumericType> constexpr NumericType constexpr_abs(NumericType value)
{
return NumericType(0) < value ? value : -value;
}
}
#endif // FLOAXIE_BIT_OPS_H

View File

@@ -1,53 +0,0 @@
/*
* Copyright 2015-2017 Alexey Chernov <4ernov@gmail.com>
*
* 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.
*
* cached power literals and cached_power() function use code taken from
* Florian Loitsch's original Grisu algorithms implementation
* (http://florian.loitsch.com/publications/bench.tar.gz)
* and "Printing Floating-Point Numbers Quickly and Accurately with
* Integers" paper
* (http://florian.loitsch.com/publications/dtoa-pldi2010.pdf)
*/
#ifndef FLOAXIE_CACHED_POWER_H
#define FLOAXIE_CACHED_POWER_H
#include <cstddef>
#include <cassert>
#include <external/floaxie/floaxie/powers_ten_single.h>
#include <external/floaxie/floaxie/powers_ten_double.h>
#include <external/floaxie/floaxie/diy_fp.h>
namespace floaxie
{
/** \brief Returns pre-calculated `diy_fp` value of 10 in the specified
* power using pre-calculated and compiled version of binary mantissa
* and exponent.
*
* \tparam FloatType floating point type to call the values for.
*/
template<typename FloatType> inline diy_fp<FloatType> cached_power(int k) noexcept
{
assert(k >= -static_cast<int>(powers_ten<FloatType>::pow_0_offset));
const std::size_t index = powers_ten<FloatType>::pow_0_offset + k;
return diy_fp<FloatType>(powers_ten<FloatType>::f[index], powers_ten<FloatType>::e[index]);
}
}
#endif // FLOAXIE_CACHED_POWER_H

View File

@@ -1,34 +0,0 @@
/*
* Copyright 2015, 2016, 2017 Alexey Chernov <4ernov@gmail.com>
*
* 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 FLOAXIE_CONVERSION_STATUS_H
#define FLOAXIE_CONVERSION_STATUS_H
namespace floaxie
{
/** \brief Enumeration of possible conversion results, either successful or
* not.
*/
enum class conversion_status : unsigned char
{
success, /**< The conversion was successful. */
underflow, /**< An underflow occurred during the conversion. */
overflow /**< An overflow occurred during the conversion. */
};
}
#endif // FLOAXIE_CONVERSION_STATUS_H

View File

@@ -1,94 +0,0 @@
/*
* Copyright 2015, 2016 Alexey Chernov <4ernov@gmail.com>
*
* 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 FLOAXIE_DEFAULT_FALLBACK_H
#define FLOAXIE_DEFAULT_FALLBACK_H
#include <cstdlib>
#include <cwchar>
#include <floaxie/conversion_status.h>
namespace floaxie
{
/** \brief Function template to wrap C Standard Library floating point
* parse function based on floating-point type and character type (normal
* or wide).
*
* \tparam FloatType floating point type to parse.
* \tparam CharType character type of string to parse.
*/
template<typename FloatType, typename CharType> FloatType default_fallback(const CharType* str, CharType** str_end);
/** \brief `float` and `char`. */
template<> inline float default_fallback<float, char>(const char* str, char** str_end)
{
return std::strtof(str, str_end);
}
/** \brief `double` and `char`. */
template<> inline double default_fallback<double, char>(const char* str, char** str_end)
{
return std::strtod(str, str_end);
}
/** \brief `long double` and `char`. */
template<> inline long double default_fallback<long double, char>(const char* str, char** str_end)
{
return std::strtold(str, str_end);
}
/** \brief `float` and `wchar_t`. */
template<> inline float default_fallback<float, wchar_t>(const wchar_t* str, wchar_t** str_end)
{
return std::wcstof(str, str_end);
}
/** \brief `double` and `wchar_t`. */
template<> inline double default_fallback<double, wchar_t>(const wchar_t* str, wchar_t** str_end)
{
return std::wcstod(str, str_end);
}
/** \brief `long double` and `wchar_t`. */
template<> inline long double default_fallback<long double, wchar_t>(const wchar_t* str, wchar_t** str_end)
{
return std::wcstold(str, str_end);
}
/** \brief Returns `conversion_status` based on `errno` value.
*
* Analyzes current value of `errno` together with the passed conversion
* result and returns `conversion_status` value for the case.
*
* \tparam FloatType floating-point type of the returned value passed.
*
* \p returned_value the value returned after the conversion.
*
* \return status of the last conversion.
*
* \sa `conversion_status`
*/
template<typename FloatType> conversion_status check_errno(FloatType returned_value)
{
if (errno != ERANGE)
return conversion_status::success;
return returned_value ? conversion_status::overflow : conversion_status::underflow;
}
}
#endif // FLOAXIE_DEFAULT_FALLBACK_H

View File

@@ -1,518 +0,0 @@
/*
* Copyright 2015-2019 Alexey Chernov <4ernov@gmail.com>
*
* 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.
*
* diy_fp class and helper functions use code and influenced by
* Florian Loitsch's original Grisu algorithms implementation
* (http://florian.loitsch.com/publications/bench.tar.gz)
* and "Printing Floating-Point Numbers Quickly and Accurately with
* Integers" paper
* (http://florian.loitsch.com/publications/dtoa-pldi2010.pdf)
*/
#ifndef FLOAXIE_DIY_FP_H
#define FLOAXIE_DIY_FP_H
#include <algorithm>
#include <limits>
#include <cstdint>
#include <cassert>
#include <ostream>
#include <utility>
#include <external/floaxie/floaxie/bit_ops.h>
#include <external/floaxie/floaxie/print.h>
#include <external/floaxie/floaxie/type_punning_cast.h>
#include <external/floaxie/floaxie/huge_val.h>
#include <external/floaxie/floaxie/conversion_status.h>
namespace floaxie
{
/** \brief Template structure to define `diy_fp` inner types for
* selected floating point types.
*/
template<typename FloatType> struct diy_fp_traits;
/** \brief `diy_fp_traits` specialization associated with single precision
* floating point type (`float`).
*
* **Mantissa** is stored using the fastest natively supported standard
* integer type — almost always it's 32-bit unsigned integer value.
*
* **Exponent** is stored in `int` value. Shorter types aren't used to
* avoid any performance impacts of not using default integer types
* (though it can be effective in terms of cache usage).
*/
template<> struct diy_fp_traits<float>
{
/** \brief Integer type to store mantissa value. */
typedef std::uint32_t mantissa_type;
/** \brief Integer type to store exponent value. */
typedef int exponent_type;
};
/** \brief `diy_fp_traits` specialization associated with double precision
* floating point type (`double`).
*
* **Mantissa** is stored using the biggest natively supported standard
* integer type — currently it's 64-bit unsigned integer value. Emulated
* (e.g. *big integer*) integer types are not used, as they don't follow
* the ideas behind the fast integer algorithms (they are slower due to
* the emulation part).
*
* **Exponent** is stored in `int` value.
*
*/
template<> struct diy_fp_traits<double>
{
/** \brief Integer type to store mantissa value. */
typedef std::uint64_t mantissa_type;
/** \brief Integer type to store exponent value. */
typedef int exponent_type;
};
/** \brief Integer representation of floating point value.
*
* The templated type represents floating point value using two integer values, one
* to store **mantissa** and another to hold **exponent**. Concrete types are
* expressed as **diy_fp** specializations, with pre-defined types for **mantissa**
* and **exponent**, suitable to process floating point value of the specified
* precision with maximum efficiency and without losing accuracy.
*
* \tparam FloatType floating point type the representation is instantiated for.
* \tparam Traits some inner settings (mainly, integer types to store mantissa and
* exponent) corresponding to `FloatType`.
*
* The type is used in **Grisu** and **Krosh** algorithms.
*/
template<typename FloatType, typename Traits = diy_fp_traits<FloatType>> class diy_fp
{
public:
/** \brief Mantissa storage type abstraction. */
typedef typename Traits::mantissa_type mantissa_storage_type;
/** \brief Exponent storage type abstraction. */
typedef typename Traits::exponent_type exponent_storage_type;
private:
static_assert(std::numeric_limits<FloatType>::is_iec559, "Only IEEE-754 floating point types are supported");
static_assert(sizeof(FloatType) == sizeof(mantissa_storage_type), "Float type is not compatible with its `diy_fp` representation layout.");
/** \brief Returns value of hidden bit for the specified floating point type.
*
* \return integer value of hidden bit of the specified type in
* `mantissa_storage_type` variable.
*/
static constexpr mantissa_storage_type hidden_bit()
{
return raised_bit<mantissa_storage_type>(std::numeric_limits<FloatType>::digits - 1);
}
public:
/** \brief Default constructor. */
diy_fp() = default;
/** \brief Copy constructor. */
diy_fp(const diy_fp&) = default;
/** \brief Component initialization constructor. */
constexpr diy_fp(mantissa_storage_type mantissa, exponent_storage_type exponent) noexcept : m_f(mantissa), m_e(exponent) { }
/** \brief Initializes `diy_fp` value from the value of floating point
* type.
*
* It splits floating point value into mantissa and exponent
* components, calculates hidden bit of mantissa and initializes
* `diy_fp` value with the results of calculations.
*/
explicit diy_fp(FloatType d) noexcept
{
constexpr auto full_mantissa_bit_size(std::numeric_limits<FloatType>::digits);
constexpr auto mantissa_bit_size(full_mantissa_bit_size - 1); // remember hidden bit
constexpr mantissa_storage_type mantissa_mask(mask<mantissa_storage_type>(mantissa_bit_size));
constexpr mantissa_storage_type exponent_mask((~mantissa_mask) ^ msb_value<FloatType>()); // ignore sign bit
constexpr exponent_storage_type exponent_bias(std::numeric_limits<FloatType>::max_exponent - 1 + mantissa_bit_size);
mantissa_storage_type parts = type_punning_cast<mantissa_storage_type>(d);
m_f = parts & mantissa_mask;
m_e = (parts & exponent_mask) >> mantissa_bit_size;
if (m_e)
{
m_f += hidden_bit();
m_e -= exponent_bias;
}
else
{
m_e = 1 - exponent_bias;
}
}
/** \brief Downsample result structure.
*
*/
struct downsample_result
{
/** \brief Downsampled floating point result. */
FloatType value;
/** \brief Status showing possible under- or overflow found during downsampling. */
conversion_status status;
/** \brief Flag indicating if the conversion is accurate (no
* [rounding errors] (http://www.exploringbinary.com/decimal-to-floating-point-needs-arbitrary-precision/). */
bool is_accurate;
};
/** \brief Convert `diy_fp` value back to floating point type correctly
* downsampling mantissa value.
*
* The caller should ensure, that the current mantissa value is not null
* and the whole `diy_fp` value is normalized, otherwise the behaviour is
* undefined.
*
* \return result structure with floating point value of the specified type.
*/
downsample_result downsample()
{
downsample_result ret;
ret.is_accurate = true;
ret.status = conversion_status::success;
assert(m_f != 0);
assert(is_normalized());
constexpr auto full_mantissa_bit_size(std::numeric_limits<FloatType>::digits);
constexpr auto mantissa_bit_size(full_mantissa_bit_size - 1); // remember hidden bit
constexpr mantissa_storage_type my_mantissa_size(bit_size<mantissa_storage_type>());
constexpr mantissa_storage_type mantissa_mask(mask<mantissa_storage_type>(mantissa_bit_size));
constexpr exponent_storage_type exponent_bias(std::numeric_limits<FloatType>::max_exponent - 1 + mantissa_bit_size);
constexpr std::size_t lsb_pow(my_mantissa_size - full_mantissa_bit_size);
const auto f(m_f);
if (m_e >= std::numeric_limits<FloatType>::max_exponent)
{
ret.value = huge_value<FloatType>();
ret.status = conversion_status::overflow;
return ret;
}
if (m_e + int(my_mantissa_size) < std::numeric_limits<FloatType>::min_exponent - int(mantissa_bit_size))
{
ret.value = FloatType(0);
ret.status = conversion_status::underflow;
return ret;
}
const std::size_t denorm_exp(positive_part(std::numeric_limits<FloatType>::min_exponent - int(mantissa_bit_size) - m_e - 1));
assert(denorm_exp < my_mantissa_size);
const std::size_t shift_amount(std::max(denorm_exp, lsb_pow));
mantissa_storage_type parts = m_e + shift_amount + exponent_bias - (denorm_exp > lsb_pow);
parts <<= mantissa_bit_size;
const auto& round(round_up(f, shift_amount));
parts |= ((f >> shift_amount) + round.value) & mantissa_mask;
ret.value = type_punning_cast<FloatType>(parts);
ret.is_accurate = round.is_accurate;
return ret;
}
/** \brief Mantissa component. */
constexpr mantissa_storage_type mantissa() const
{
return m_f;
}
/** \brief Exponent component. */
constexpr exponent_storage_type exponent() const
{
return m_e;
}
/** \brief Checks if the value is normalized.
*
* The behaviour is undefined, if called for null value.
*
*/
bool is_normalized() const noexcept
{
assert(m_f != 0); // normalization of zero is undefined
return m_f & msb_value<mantissa_storage_type>();
}
/** \brief Normalizes the value the common way.
*
* The caller should ensure, that the current mantissa value is not null,
* otherwise the behaviour is undefined.
*/
void normalize() noexcept
{
assert(m_f != 0); // normalization of zero is undefined
while (!highest_bit(m_f))
{
m_f <<= 1;
m_e--;
}
}
/** \brief Copy assignment operator. */
diy_fp& operator=(const diy_fp&) = default;
/** \brief Subtracts the specified `diy_fp` value from the current.
*
* Simple mantissa subtraction of `diy_fp` values.
*
* If exponents of the values differ or mantissa of left value is less,
* than mantissa of right value, the behaviour is undefined.
*
* \param rhs subtrahend.
*
* \return reference to current value, i.e. the result of the
* subtraction.
*/
diy_fp& operator-=(const diy_fp& rhs) noexcept
{
assert(m_e == rhs.m_e && m_f >= rhs.m_f);
m_f -= rhs.m_f;
return *this;
}
/** \brief Non-destructive version of `diy_fp::operator-=()`. */
diy_fp operator-(const diy_fp& rhs) const noexcept
{
return diy_fp(*this) -= rhs;
}
/** \brief Fast and coarse multiplication.
*
* Performs multiplication of `diy_fp` values ignoring some carriers
* for the sake of performance. This multiplication algorithm is used
* in original **Grisu** implementation and also works fine for
* **Krosh**.
*
* \param rhs multiplier.
*
* \return reference to current value, i.e. the result of the
* multiplication.
*/
diy_fp& operator*=(const diy_fp& rhs) noexcept
{
constexpr std::size_t half_width = bit_size<mantissa_storage_type>() / 2;
constexpr auto mask_half = mask<mantissa_storage_type>(half_width);
const mantissa_storage_type a = m_f >> half_width;
const mantissa_storage_type b = m_f & mask_half;
const mantissa_storage_type c = rhs.m_f >> half_width;
const mantissa_storage_type d = rhs.m_f & mask_half;
const mantissa_storage_type ac = a * c;
const mantissa_storage_type bc = b * c;
const mantissa_storage_type ad = a * d;
const mantissa_storage_type bd = b * d;
const mantissa_storage_type tmp = (bd >> half_width) + (ad & mask_half) + (bc & mask_half) + raised_bit<mantissa_storage_type>(half_width - 1);
m_f = ac + (ad >> half_width) + (bc >> half_width) + (tmp >> half_width);
m_e += rhs.m_e + bit_size<mantissa_storage_type>();
return *this;
}
/** \brief Non-destructive version of `diy_fp::operator*=()`. */
diy_fp operator*(const diy_fp& rhs) const noexcept
{
return diy_fp(*this) *= rhs;
}
/** \brief Increment (prefix) with mantissa overflow control. */
diy_fp& operator++() noexcept
{
if (m_f < std::numeric_limits<diy_fp::mantissa_storage_type>::max())
{
++m_f;
}
else
{
m_f >>= 1;
++m_f;
++m_e;
}
return *this;
}
/** \brief Postfix increment version. */
diy_fp operator++(int) noexcept
{
auto temp = *this;
++(*this);
return temp;
}
/** \brief Decrement (prefix) with mantissa underflow control. */
diy_fp& operator--() noexcept
{
if (m_f > 1)
{
--m_f;
}
else
{
m_f <<= 1;
--m_f;
--m_e;
}
return *this;
}
/** \brief Postfix decrement version. */
diy_fp operator--(int) noexcept
{
auto temp = *this;
--(*this);
return temp;
}
/** \brief Equality of `diy_fp` values.
*
* Just member-wise equality check.
*/
bool operator==(const diy_fp& d) const noexcept
{
return m_f == d.m_f && m_e == d.m_e;
}
/** \brief Inequality of `diy_fp` values.
*
* Negation of `diy_fp::operator==()` for consistency.
*/
bool operator!=(const diy_fp& d) const noexcept
{
return !operator==(d);
}
/** \brief Calculates boundary values (M+ and M-) for the specified
* floating point value.
*
* Helper function for **Grisu2** algorithm, which first converts the
* specified floating point value to `diy_fp` and then calculates lower
* (M-) and higher (M+) boundaries of it and thus of original accurate
* floating point value.
*
* These two boundary values define the range where all the values are
* correctly rounded to the specified floating point value, so any
* value within this range can be treated as correct representation of
* the specified one.
*
* \param d floating point value to calculate boundaries for.
*
* \return `std::pair` of two `diy_fp` values, **M-** and **M+**,
* respectively.
*
* \see [Printing Floating-Point Numbers Quickly and Accurately with
* Integers]
* (http://florian.loitsch.com/publications/dtoa-pldi2010.pdf)
*/
static std::pair<diy_fp, diy_fp> boundaries(FloatType d) noexcept
{
std::pair<diy_fp, diy_fp> result;
diy_fp &mi(result.first), &pl(result.second);
pl = diy_fp(d);
mi = pl;
pl.m_f <<= 1;
pl.m_f += 1;
pl.m_e -= 1;
pl.normalize_from_ieee754(); // as we increase precision of IEEE-754 type by 1
const unsigned char shift_amount(1 + (mi.m_f == hidden_bit()));
mi.m_f <<= shift_amount;
mi.m_f -= 1;
mi.m_e -= shift_amount;
mi.m_f <<= mi.m_e - pl.m_e;
mi.m_e = pl.m_e;
return result;
}
/** \brief Prints `diy_fp` value.
*
* \param os `std::basic_ostream` to print to.
* \param v `diy_fp` value to print.
*
* \return `std::basic_ostream` with the \p **v** value printed.
*/
template<typename Ch, typename Alloc>
friend std::basic_ostream<Ch, Alloc>& operator<<(std::basic_ostream<Ch, Alloc>& os, const diy_fp& v)
{
os << "(f = " << print_binary(v.m_f) << ", e = " << v.m_e << ')';
return os;
}
private:
/** \brief Normalizes the value using additional information on
* mantissa content of the `FloatType`.
* Mantissa value is treated as of the width defined in
* `std::numeric_limits`. This information speeds up the normalization,
* allowing to shift the value by several positions right at one take,
* rather than shifting it by one step and checking if it's still not normalized.
*
* The caller should ensure, that the current mantissa value is not null
* and is really represented in IEEE-754 format, otherwise the behaviour
* is undefined.
*/
void normalize_from_ieee754() noexcept
{
constexpr auto mantissa_bit_width(std::numeric_limits<FloatType>::digits);
static_assert(mantissa_bit_width >= 0, "Mantissa bit width should be positive.");
assert(m_f != 0); // normalization of zero is undefined
while (!nth_bit(m_f, mantissa_bit_width))
{
m_f <<= 1;
m_e--;
}
constexpr mantissa_storage_type my_mantissa_size(bit_size<mantissa_storage_type>());
constexpr mantissa_storage_type e_diff = my_mantissa_size - mantissa_bit_width - 1;
m_f <<= e_diff;
m_e -= e_diff;
}
mantissa_storage_type m_f;
exponent_storage_type m_e;
};
}
#endif // FLOAXIE_DIY_FP_H

View File

@@ -1,142 +0,0 @@
/*
* Copyright 2015, 2016 Alexey Chernov <4ernov@gmail.com>
*
* 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 FLOAXIE_FRACTION_H
#define FLOAXIE_FRACTION_H
#include <cstddef>
#include <floaxie/static_pow.h>
namespace floaxie
{
/** \brief Recursive structure template used to convert decimal common
* fraction to binary common fraction.
*
* The structure template hierarchy is used to calculate the binary common
* fraction value, which is approximately equal to given decimal common
* fraction.
*
* Example:
* ~~~
* 0.625 = 0.101
* ~~~
*
* \tparam T numeric type of input value and result.
* \tparam decimal_digits number of significant decimal digits in
* numerator (equals to the power of ten in denominator); for example, for
* 0.625 \p **decimal_digits** is 3.
* \tparam binary_digits number of significant binary digits in numerator
* of the result, which is defined by the necessary accuracy of
* result approximation; for example, for 0.101 \p **decimal_digits** is 3.
* \tparam current_binary_digit template cursor used to implement
* recursive descent.
* \tparam terminal automatically calculated flag of recursion termination.
*/
template
<
typename T,
std::size_t decimal_digits,
std::size_t binary_digits,
std::size_t current_binary_digit,
bool terminal = (binary_digits == current_binary_digit)
>
struct fraction_converter;
/** \brief Intermediate step implementation of `fraction_converter`
* template.
*/
template
<
typename T,
std::size_t decimal_digits,
std::size_t binary_digits,
std::size_t current_binary_digit
>
struct fraction_converter<T, decimal_digits, binary_digits, current_binary_digit, false>
{
/** \brief Calculates \p `current_binary_digit`-th digit of the result.
*
* \param decimal_numerator value of decimal numerator of common
* fraction to convert.
*
* \return properly shifted value of \p `current_binary_digit`-th
* digit of the result.
*/
static T convert(T decimal_numerator)
{
constexpr T numerator(static_pow<10, decimal_digits>());
constexpr T denominator(static_pow<2, current_binary_digit>());
constexpr T decimal_fraction(numerator / denominator);
constexpr std::size_t shift_amount(binary_digits - current_binary_digit);
const T decision(decimal_numerator >= decimal_fraction);
const T residue(decimal_numerator - decision * decimal_fraction);
return (decision << shift_amount) |
fraction_converter<T, decimal_digits, binary_digits, current_binary_digit + 1>::convert(residue);
}
};
/** \brief Terminal step implementation of `fraction_converter` template.
*
*/
template
<
typename T,
std::size_t decimal_digits,
std::size_t binary_digits,
std::size_t current_binary_digit
>
struct fraction_converter<T, decimal_digits, binary_digits, current_binary_digit, true>
{
/** \brief Calculates least significant digit of the result.
*
* \param decimal_numerator value of decimal numerator of common
* fraction to convert.
*
* \return right most (least significant) digit of the result.
*/
static T convert(T decimal_numerator)
{
constexpr T numerator(static_pow<10, decimal_digits>());
constexpr T denominator(static_pow<2, current_binary_digit>());
constexpr T decimal_fraction(numerator / denominator);
return decimal_numerator >= decimal_fraction;
}
};
/** \brief Wrapper function to convert numerator of decimal common fraction
* to numerator of approximately equal binary common fraction with the
* specified accuracy.
*
* \tparam decimal_digits number of digits in decimal numerator to observe
* (input accuracy).
* \tparam binary_digits number of digits in binary numerator to generate
* (output accuracy).
*
* \return value of binary numerator with the specified accuracy as
* calculated by `fraction_converter`.
*/
template<std::size_t decimal_digits, std::size_t binary_digits, typename T> inline T convert_numerator(T decimal_numerator)
{
return fraction_converter<T, decimal_digits, binary_digits, 1>::convert(decimal_numerator);
}
}
#endif // FLOAXIE_FRACTION_H

View File

@@ -1,202 +0,0 @@
/*
* Copyright 2015-2022 Alexey Chernov <4ernov@gmail.com>
*
* 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 FLOAXIE_FTOA_H
#define FLOAXIE_FTOA_H
#include <string>
#include <type_traits>
#include <cmath>
#include <cstddef>
#include <cassert>
#include <external/floaxie/floaxie/grisu.h>
#include <external/floaxie/floaxie/prettify.h>
namespace floaxie
{
/** \brief Returns maximum size of buffer can ever be required by `ftoa()`.
*
* Maximum size of buffer passed to `ftoa()` guaranteed not to lead to
* undefined behaviour.
*
* \tparam FloatType floating point type, which value is planned to be
* printed to the buffer.
*
* \return maximum size of buffer, which can ever be used in the very worst
* case.
*/
template<typename ValueType> constexpr std::size_t max_buffer_size() noexcept
{
typedef typename std::decay<ValueType>::type FloatType;
// digits, '.' (or 'e' plus three-digit power with optional sign) and '\0'
return max_digits<FloatType>() + 1 + 1 + 3 + 1;
}
/** \brief Prints floating point value to optimal string representation.
*
* The function prints the string representation of the specified floating
* point value using
* [**Grisu2**](http://florian.loitsch.com/publications/dtoa-pldi2010.pdf)
* algorithm and tries to get it as shorter, as possible. Usually it
* succeeds, but sometimes fails, and the output representation is not
* the shortest for this value. For the sake of speed improvement this is
* ignored, while there's **Grisu3** algorithm which rules this out
* informing the caller of the failure, so that it can call slower, but
* more accurate algorithm in this case.
*
* The format of the string representation is one of the following:
* 1. Decimal notation, which contains:
* - minus sign ('-') in case of negative value
* - sequence of one or more decimal digits optionally containing
* decimal point character ('.')
* 2. Decimal exponent notation, which contains:
* - minus ('-') sign in case of negative value
* - sequence of one or more decimal digits optionally containing
* decimal point character ('.')
* - 'e' character followed by minus sign ('-') in case of negative
* power of the value (i.e. the specified value is < 1) and
* sequence of one, two of three decimal digits.
*
* \tparam FloatType type of floating point value, calculated using passed
* input parameter \p **v**.
* \tparam CharType character type (typically `char` or `wchar_t`) of the
* output buffer \p **buffer**.
*
* \param v floating point value to print.
* \param buffer character buffer of enough size (see `max_buffer_size()`)
* to print the representation to.
*
* \return number of characters actually written.
*
* \see `max_buffer_size()`
*/
template<typename FloatType, typename CharType> inline std::size_t ftoa(FloatType v, CharType* buffer) noexcept
{
if (std::isnan(v))
{
buffer[0] = 'n';
buffer[1] = 'a';
buffer[2] = 'n';
buffer[3] = '\0';
return 3;
}
else if (std::isinf(v))
{
if (v > 0)
{
buffer[0] = 'i';
buffer[1] = 'n';
buffer[2] = 'f';
buffer[3] = '\0';
return 3;
}
else
{
buffer[0] = '-';
buffer[1] = 'i';
buffer[2] = 'n';
buffer[3] = 'f';
buffer[4] = '\0';
return 4;
}
}
else if (v == 0)
{
buffer[0] = '0';
buffer[1] = '\0';
return 1;
}
else
{
*buffer = '-';
buffer += v < 0;
constexpr int alpha(grisu_parameters<FloatType>.alpha), gamma(grisu_parameters<FloatType>.gamma);
constexpr unsigned int decimal_scientific_threshold(16);
int len, K;
grisu2<alpha, gamma>(v, buffer, &len, &K);
return (v < 0) + prettify<decimal_scientific_threshold>(buffer, len, K);
}
}
/** \brief Prints floating point value to optimal representation in
* `std::basic_string`.
*
* Wrapper function around `ftoa()`, which returns `std::basic_string`,
* rather than writing to the specified character buffer. This may be
* more useful, if working with `std::basic_string` strings is preferred.
* Please note, however, than common usage scenarios might be significantly
* slower, when many `std::basic_string`'s are created with this function
* and concatenated to each other, than when the outputs of `ftoa()` calls
* are written to one long buffer.
*
* \tparam FloatType type of floating point value, calculated using passed
* input parameter \p **v**.
* \tparam CharType character type (typically `char` or `wchar_t`) of the
* output buffer \p **buffer**.
*
* \param v floating point value to print.
* \param buffer character buffer of enough size (see `max_buffer_size()`)
* to print the representation to.
*
* \see `ftoa()`
*/
template<typename FloatType, typename CharType> inline std::basic_string<CharType> to_basic_string(FloatType v)
{
std::basic_string<CharType> result(max_buffer_size<FloatType>(), CharType());
ftoa(v, &result.front());
result.resize(std::char_traits<CharType>::length(result.data()));
result.shrink_to_fit();
return result;
}
/** \brief 'Specialization' of `to_basic_string()` template for `std::string`. */
template<typename FloatType> inline std::string to_string(FloatType v)
{
return to_basic_string<FloatType, char>(v);
}
/** \brief 'Specialization' of `to_basic_string()` template for `std::wstring`. */
template<typename FloatType> inline std::wstring to_wstring(FloatType v)
{
return to_basic_string<FloatType, wchar_t>(v);
}
/** \brief 'Specialization' of `to_basic_string()` template for `std::u16string`. */
template<typename FloatType> inline std::u16string to_u16string(FloatType v)
{
return to_basic_string<FloatType, char16_t>(v);
}
/** \brief 'Specialization' of `to_basic_string()` template for `std::u32string`. */
template<typename FloatType> inline std::u32string to_u32string(FloatType v)
{
return to_basic_string<FloatType, char32_t>(v);
}
}
#endif // FLOAXIE_FTOA_H

View File

@@ -1,303 +0,0 @@
/*
* Copyright 2015-2019 Alexey Chernov <4ernov@gmail.com>
*
* 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.
*
* digin_gen(), grisu() functions and kappa precalculation approach
* were greatly influenced by
* Florian Loitsch's original Grisu algorithms implementation
* (http://florian.loitsch.com/publications/bench.tar.gz),
* "Printing Floating-Point Numbers Quickly and Accurately with
* Integers" paper
* (http://florian.loitsch.com/publications/dtoa-pldi2010.pdf)
* and Milo Yip's Grisu implementation
* (https://github.com/miloyip/dtoa-benchmark)
*/
#ifndef FLOAXIE_GRISU_H
#define FLOAXIE_GRISU_H
#include <utility>
#include <limits>
#include <cstring>
#include <cstddef>
#include <cassert>
#include <external/floaxie/floaxie/diy_fp.h>
#include <external/floaxie/floaxie/cached_power.h>
#include <external/floaxie/floaxie/k_comp.h>
#include <external/floaxie/floaxie/static_pow.h>
#include <external/floaxie/floaxie/integer_of_size.h>
#include <external/floaxie/floaxie/bit_ops.h>
#include <external/floaxie/floaxie/memwrap.h>
namespace floaxie
{
/** \brief Half of `diy_fp::mantissa_storage_type` to use in calculations.
*/
typedef std::uint32_t half_of_mantissa_storage_type;
/** \brief `std::pair` containing exponent value and the corresponding
* power of 10.
*/
typedef std::pair<unsigned char, half_of_mantissa_storage_type> kappa_pair_type;
/** \brief Bind exponent value and the corresponding power of 10. */
template<unsigned char pow> constexpr kappa_pair_type make_kappa_div()
{
return kappa_pair_type(pow, static_pow<10, pow - 1>());
}
/** \brief Ground specialization of `make_kappa_div()` template. */
template<> constexpr kappa_pair_type make_kappa_div<0>()
{
return kappa_pair_type(0, 1);
}
/** \brief Calculate the biggest power of 10 divisor for the specified
* value.
*/
constexpr kappa_pair_type calculate_kappa_div(half_of_mantissa_storage_type n) noexcept
{
if (n < static_pow<10, 1>()) return make_kappa_div<1>();
if (n < static_pow<10, 2>()) return make_kappa_div<2>();
if (n < static_pow<10, 3>()) return make_kappa_div<3>();
if (n < static_pow<10, 4>()) return make_kappa_div<4>();
if (n < static_pow<10, 5>()) return make_kappa_div<5>();
if (n < static_pow<10, 6>()) return make_kappa_div<6>();
if (n < static_pow<10, 7>()) return make_kappa_div<7>();
if (n < static_pow<10, 8>()) return make_kappa_div<8>();
if (n < static_pow<10, 9>()) return make_kappa_div<9>();
return make_kappa_div<10>();
}
/** \brief Defines the maximum possible number of digits to be generated by
* the algorithm.
*
* \tparam FloatType floating point type to calculate the maximum number for.
*/
template<typename FloatType> constexpr std::size_t max_digits() noexcept
{
return std::numeric_limits<half_of_mantissa_storage_type>::digits10 +
std::numeric_limits<typename diy_fp<FloatType>::mantissa_storage_type>::digits10;
}
template<bool positive_exponent> struct digit_gen_select;
/** \brief Template class to select `digit_gen` implementation avoiding
* function template partial specialization.
*/
template<> struct digit_gen_select<false>
{
/** \brief Function of digit generation for the case of α < 0 and γ < 0.
*
* This is probably the fastest `digit_gen()` algorithm, which implies,
* that both **M+** and **M-** exponents are not positive.
*
* Implemented as static member to solve the problem of partial specialization
* of `digit_gen<bool, FloatType, CharType>()`.
*
* \tparam FloatType floating point type of `diy_fp` (`float` for single precision,
* `double` for double precision) of \p **Mm** and \p **Mp**.
* \tparam CharType character type (typically `char` or `wchar_t`) of \p **buffer**.
*
* \param buffer - buffer where digits are generated to.
* \param len - output parameter to pass the length of the digit sequence written.
* \param K - input/output parameter to reflect K modifications made.
*
* \see [Printing Floating-Point Numbers Quickly and Accurately with
* Integers]
* (http://florian.loitsch.com/publications/dtoa-pldi2010.pdf)
*/
template<typename FloatType, typename CharType>
inline static void gen(const diy_fp<FloatType>& Mp, const diy_fp<FloatType>& Mm, CharType* buffer, int* len, int* K) noexcept
{
assert(Mp.exponent() <= 0);
const diy_fp<FloatType>& delta(Mp - Mm);
const diy_fp<FloatType> one(raised_bit<typename diy_fp<FloatType>::mantissa_storage_type>(-Mp.exponent()), Mp.exponent());
half_of_mantissa_storage_type p1 = Mp.mantissa() >> -one.exponent();
typename diy_fp<FloatType>::mantissa_storage_type p2 = Mp.mantissa() & (one.mantissa() - 1);
assert(p1 || p2);
*len = 0;
auto delta_f = delta.mantissa();
const bool close_to_delta = p2 <= delta_f;
if (p1)
{
auto&& kappa_div(calculate_kappa_div(p1));
unsigned char& kappa(kappa_div.first);
half_of_mantissa_storage_type& div(kappa_div.second);
while (kappa > 0 && p1)
{
buffer[(*len)++] = '0' + p1 / div;
p1 %= div;
kappa--;
div /= 10;
}
if (close_to_delta)
{
*K += kappa;
return;
}
else
{
wrap::memset(buffer + (*len), CharType('0'), kappa);
(*len) += kappa;
}
}
const bool some_already_written = (*len) > 0;
unsigned char kappa(0);
while (p2 > delta_f)
{
p2 *= 10;
const unsigned char d = p2 >> -one.exponent();
if (some_already_written || d)
buffer[(*len)++] = '0' + d;
p2 &= one.mantissa() - 1;
++kappa;
delta_f *= 10;
}
*K -= kappa;
}
};
/** \brief Digit generation function template.
*
* Digit generation algorithm tries to find the value in the range of
* **(M-, M+)** with the shortest possible string representation, which is
* then treated as the representation of the original value, since it
* converts back to it.
*
* General (slow) implementation is of little use, but the optimized
* versions of the function relies so much on α and γ values used in
* **Grisu** algorithm. This implementation requires both α and γ be
* greater of equal, than the bit size of `diy_fp::mantissa_storage_type`
* to use the most optimal `digit_gen()` implementation.
*
* \tparam alpha α value of **Grisu** algorithm.
* \tparam gamma γ value of **Grisu** algorithm.
* \tparam CharType character type (typically `char` or `wchar_t`) of the
* output buffer \p **buffer**.
*
* \param Mp **M+** value (right boundary).
* \param Mm **M-** value (left boundary).
* \param buffer character buffer to print the representation to
* \param len output parameter to return the length of the representation
* printed.
* \param K output parameter to return **K** (decimal exponent) of the
* value.
*
* \see `max_digits()`
* \see `max_buffer_size()`
* \see [Printing Floating-Point Numbers Quickly and Accurately with
* Integers]
* (http://florian.loitsch.com/publications/dtoa-pldi2010.pdf)
*/
template<int alpha, int gamma,
typename FloatType, typename CharType>
inline void digit_gen(const diy_fp<FloatType>& Mp, const diy_fp<FloatType>& Mm, CharType* buffer, int* len, int* K) noexcept
{
static_assert(static_cast<std::size_t>(constexpr_abs(alpha)) >= bit_size<typename diy_fp<FloatType>::mantissa_storage_type>() / 2 &&
static_cast<std::size_t>(constexpr_abs(gamma)) >= bit_size<typename diy_fp<FloatType>::mantissa_storage_type>() / 2,
"Current implementation supports only α and γ, which absolute values are equal or higher, "
"than a half of integer mantissa bit size (typically 32) for performance reasons.");
constexpr bool exponent_is_positive = alpha > 0 && gamma > 0;
digit_gen_select<exponent_is_positive>::gen(Mp, Mm, buffer, len, K);
}
/** \brief **Grisu2** algorithm implementation.
*
* \tparam alpha α value of **Grisu** algorithm.
* \tparam gamma γ value of **Grisu** algorithm.
* \tparam FloatType type of input floating-point value (calculated by type
* of \p **v** parameter).
* \tparam CharType character type (typically `char` or `wchar_t`) of the
* output buffer \p **buffer**.
*
* \param v floating point value to print.
* \param buffer large enough character buffer to print to.
* \param length output parameter to return the length of printed
* representation.
* \param K output parameter to return **K** (decimal exponent) of the
* value.
*
* \see `max_digits()`
* \see `max_buffer_size()`
* \see [Printing Floating-Point Numbers Quickly and Accurately with
* Integers]
* (http://florian.loitsch.com/publications/dtoa-pldi2010.pdf)
*/
template<int alpha, int gamma,
typename FloatType, typename CharType> inline void grisu2(FloatType v, CharType* buffer, int* length, int* K) noexcept
{
static_assert(alpha <= gamma - 3,
"It's imposed that γα + 3, since otherwise it's not always possible to find a proper decimal cached power");
std::pair<diy_fp<FloatType>, diy_fp<FloatType>>&& w(diy_fp<FloatType>::boundaries(v));
diy_fp<FloatType> &w_m(w.first), &w_p(w.second);
const int mk = k_comp_exp<alpha, gamma>(w_p.exponent());
const diy_fp<FloatType>& c_mk(cached_power<FloatType>(mk));
w_m *= c_mk;
w_p *= c_mk;
++w_m;
--w_p;
*K = -mk;
digit_gen<alpha, gamma>(w_p, w_m, buffer, length, K);
}
/** \brief Structure to hold Grisu algorithm parameters, **α** and **γ**. */
struct parameters
{
int alpha, gamma;
};
/** \brief Template variable of Grisu algorithm parameters.
*
* Used to provide proper values of Grisu algorithm for the specified
* floating point type.
*/
template<typename FloatType> constexpr parameters grisu_parameters
{
-(int(bit_size<FloatType>() / 2 + 3)), // α
-int(bit_size<FloatType>() / 2) // γ
};
}
#endif // FLOAXIE_GRISU_H

View File

@@ -1,57 +0,0 @@
/*
* Copyright 2015, 2016, 2017 Alexey Chernov <4ernov@gmail.com>
*
* 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 FLOAXIE_HUGE_VAL_H
#define FLOAXIE_HUGE_VAL_H
#include <limits>
#include <cmath>
namespace floaxie
{
/** \brief Variable template to unify getting `HUGE_VAL` for
* different floating point types in parameterized manner.
*
* \tparam FloatType floating point type to get `HUGE_VAL` for.
*
* \see [HUGE_VALF, HUGE_VAL, HUGE_VALL]
* (http://en.cppreference.com/w/cpp/numeric/math/HUGE_VAL)
*/
template<typename FloatType> constexpr inline FloatType huge_value() noexcept
{
return std::numeric_limits<FloatType>::infinity();
}
/** \brief `float`. */
template<> constexpr inline float huge_value<float>() noexcept
{
return HUGE_VALF;
}
/** \brief `double`. */
template<> constexpr inline double huge_value<double>() noexcept
{
return HUGE_VAL;
}
/** \brief `long double`. */
template<> constexpr inline long double huge_value<long double>() noexcept
{
return HUGE_VALL;
}
}
#endif // FLOAXIE_HUGE_VAL_H

View File

@@ -1,51 +0,0 @@
/*
* Copyright 2015, 2016 Alexey Chernov <4ernov@gmail.com>
*
* 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 FLOAXIE_INTEGER_OF_SIZE_H
#define FLOAXIE_INTEGER_OF_SIZE_H
#include <cstdint>
#include <cstddef>
namespace floaxie
{
/** \brief Identity type — hold the specified type in internal `typedef`.
*
* \tparam T type to hold.
*/
template<typename T> struct identity
{
/** \brief Held type. */
typedef T type;
};
/** \brief Maps some of unsigned integer types to their sizes.
*
* Useful for choosing unsigned integer type of the same width as some
* target type (e.g. floating point) to increase possible accuracy.
*
* \tparam size size in bytes of the desired type.
*/
template<std::size_t size> struct integer_of_size : identity<std::uintmax_t()> {};
/** \brief Specialization for 64-bit unsigned integer. */
template<> struct integer_of_size<sizeof(std::uint64_t)> : identity<std::uint64_t> {};
/** \brief Specialization for 32-bit unsigned integer. */
template<> struct integer_of_size<sizeof(std::uint32_t)> : identity<std::uint32_t> {};
}
#endif // FLOAXIE_INTEGER_OF_SIZE_H

View File

@@ -1,54 +0,0 @@
/*
* Copyright 2015-2019 Alexey Chernov <4ernov@gmail.com>
*
* 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.
*
* k_comp() function uses code taken from
* Florian Loitsch's "Printing Floating-Point Numbers Quickly
* and Accurately with Integers" paper
* (http://florian.loitsch.com/publications/dtoa-pldi2010.pdf)
*/
#ifndef FLOAXIE_K_COMP_H
#define FLOAXIE_K_COMP_H
namespace floaxie
{
/** \brief Compiled value of \f$log{10} 2 \f$ */
constexpr auto lg_2(0.30102999566398114); // 1 / log2(10) = lg(2) ≈ 0.30102999566398114
/** \brief Calculate **K** decimal exponent value by binary exponent.
*
* We ignore mantissa component (q) in exponent to eliminate
* excessive add and subtract of it during K computation.
*
* Function name was changed to not confuse it with the original
* k_comp() function from reference paper where this component
* is considered.
*
* \tparam alpha α value of **Grisu** algorithm.
* \tparam gamma γ value of **Grisu** algorithm.
*
* \param e binary exponent of the floating point value.
*
* \see [Printing Floating-Point Numbers Quickly and Accurately with
* Integers]
* (http://florian.loitsch.com/publications/dtoa-pldi2010.pdf)
*/
template<int alpha, int gamma> constexpr int k_comp_exp(int e)
{
return (alpha - e - 1) * lg_2 + (e + 1 < alpha);
}
}
#endif // FLOAXIE_K_COMP_H

View File

@@ -1,621 +0,0 @@
/*
* Copyright 2015-2019 Alexey Chernov <4ernov@gmail.com>
*
* 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 FLOAXIE_CROSH_H
#define FLOAXIE_CROSH_H
#include <vector>
#include <locale>
#include <cstddef>
#include <cmath>
#include <cassert>
#include <floaxie/diy_fp.h>
#include <floaxie/static_pow.h>
#include <floaxie/k_comp.h>
#include <floaxie/cached_power.h>
#include <floaxie/bit_ops.h>
#include <floaxie/fraction.h>
#include <floaxie/conversion_status.h>
namespace floaxie
{
/** \brief Maximum number of decimal digits mantissa of `diy_fp` can hold. */
template<typename FloatType> constexpr std::size_t decimal_q = std::numeric_limits<typename diy_fp<FloatType>::mantissa_storage_type>::digits10;
/** \brief Maximum number of necessary binary digits of fraction part. */
constexpr std::size_t fraction_binary_digits(7);
/** \brief Maximum number of decimal digits of fraction part, which can be observed. */
constexpr std::size_t fraction_decimal_digits(4);
/** \brief Maximum length of input string (2 KB). */
constexpr std::size_t maximum_offset = 2048;
/** \brief Maximum number of decimal digits in the exponent value. */
constexpr std::size_t exponent_decimal_digits(3);
/** \brief Tries to find and eat NaN representation in one of two forms.
*
* Searches for either "NAN" or "NAN(<character sequence>)" form of NaN
* (not a number) value representation (case insensitive) to help
* converting it to quiet NaN and finding the end of the read value.
*
* \param str character buffer to analyze.
*
* \return number of consumed characters. Naturally, if it's equal to zero,
* NaN representation wasn't found.
*/
template<typename CharType> std::size_t eat_nan(const CharType* str) noexcept
{
std::size_t eaten(0);
if ((str[0] == 'a' || str[0] == 'A') && (str[1] == 'n' || str[1] == 'N'))
{
const CharType* cp = str + 2;
eaten = 2;
/* Match `(n-char-sequence-digit)'. */
if (*cp == '(')
{
do
++cp;
while ((*cp >= '0' && *cp <= '9') ||
(std::tolower(*cp, std::locale()) >= 'a' && std::tolower(*cp, std::locale()) <= 'z') ||
*cp == '_');
if (*cp == ')')
eaten = cp - str + 1;
}
}
return eaten;
}
/** \brief Tries to find and eat infinity representation.
*
* Searches for either "inf" or "infinity" sequence (case insensitive)
* to determine infinite floating point value representation.
*
* \param str character buffer to analyze.
*
* \return number of consumed characters. Naturally, if it's equal to zero,
* infinity representation wasn't found.
*/
template<typename CharType> std::size_t eat_inf(const CharType* str) noexcept
{
std::size_t eaten(0);
if ((str[0] == 'n' || str[0] == 'N') && (str[1] == 'f' || str[1] == 'F'))
{
const CharType* cp = str + 2;
eaten = 2;
if (*cp == 'i' || *cp == 'I')
{
++cp;
const std::array<CharType, 4> suffix {{ 'n', 'i', 't', 'y' }};
auto it = suffix.cbegin();
while (it != suffix.cend() && std::tolower(*cp, std::locale()) == *it)
{
++cp;
++it;
}
if (it == suffix.cend())
eaten = cp - str;
}
}
return eaten;
}
/** \brief Extracts up to \p **kappa** decimal digits from fraction part.
*
* Extracts decimal digits from fraction part and returns it as numerator
* value with denominator equal to \f$10^{\kappa}\f$.
*
* \tparam kappa maximum number of decimal digits to extract.
* \tparam FloatType destination type of floating point value to store the
* results.
* \tparam CharType character type (typically `char` or `wchar_t`) \p **str**
* consists of.
*
* \param str character buffer to extract from.
*
* \return Numerator value of the extracted decimal digits (i.e. as they
* are actually written after the decimal point).
*/
template<std::size_t kappa, typename CharType>
inline unsigned int extract_fraction_digits(const CharType* str)
{
static_assert(kappa <= std::numeric_limits<int>::digits10, "Extracting values, exceeding 'int' capacity, is not supported.");
std::array<unsigned char, kappa> parsed_digits;
parsed_digits.fill(0);
for (std::size_t pos = 0; pos < kappa; ++pos)
{
const auto c = str[pos] - '0';
if (c >= 0 && c <= 9)
parsed_digits[pos] = c;
else
break;
}
unsigned int result(0);
std::size_t pow(0);
for (auto rit = parsed_digits.rbegin(); rit != parsed_digits.rend(); ++rit)
result += (*rit) * seq_pow<unsigned int, 10, kappa>(pow++);
return result;
}
/** \brief Type of special value. */
enum class speciality : unsigned char
{
/** \brief Normal value - no special. */
no = 0,
/** \brief NaN (not a number) value. */
nan,
/** \brief infinity value. */
inf
};
/** \brief Return structure for `parse_digits`.
*
* \tparam FloatType destination type of floating point value to store the
* results.
* \tparam CharType character type (typically `char` or `wchar_t`) used.
*/
template<typename FloatType, typename CharType> struct digit_parse_result
{
/** \brief Pre-initializes members to sane values. */
digit_parse_result() : value(), K(0), str_end(nullptr), frac(0), special(), sign(true) { }
/** \brief Parsed mantissa value. */
typename diy_fp<FloatType>::mantissa_storage_type value;
/** \brief Decimal exponent, as calculated by exponent part and decimal
* point position.
*/
int K;
/** \brief Pointer to the memory after the parsed part of the buffer. */
const CharType* str_end;
/** \brief Binary numerator of fractional part, to help correct rounding. */
unsigned char frac;
/** \brief Flag of special value possibly occured. */
speciality special;
/** \brief Sign of the value. */
bool sign;
};
/** \brief Unified method to extract and parse digits in one pass.
*
* Goes through the string representation of the floating point value in
* the specified buffer, detects the meaning of each digit in its position
* and calculates main parts of floating point value — mantissa, exponent,
* sign, fractional part.
*
* \tparam kappa maximum number of digits to expect.
* \tparam calc_frac if `true`, try to calculate fractional part, if any.
* \tparam FloatType destination type of floating point value to store the
* results.
* \tparam CharType character type (typically `char` or `wchar_t`) \p **str**
* consists of.
*
* \param str Character buffer with floating point value representation to
* parse.
*
* \return `digit_parse_result` with the parsing results.
*/
template<typename FloatType, typename CharType>
inline digit_parse_result<FloatType, CharType> parse_digits(const CharType* str) noexcept
{
digit_parse_result<FloatType, CharType> ret;
constexpr std::size_t kappa = decimal_q<FloatType>;
std::vector<unsigned char> parsed_digits;
parsed_digits.reserve(kappa);
bool dot_set(false);
bool sign_set(false);
bool frac_calculated(false);
std::size_t pow_gain(0);
std::size_t zero_substring_length(0), fraction_digits_count(0);
bool go_to_beach(false);
std::size_t pos(0);
while(!go_to_beach)
{
const auto c = str[pos];
switch (c)
{
case '0':
if (!parsed_digits.empty() || dot_set)
{
++zero_substring_length;
pow_gain += !dot_set;
}
break;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if (zero_substring_length && parsed_digits.size() < kappa)
{
const std::size_t spare_digits { kappa - parsed_digits.size() };
auto zero_copy_count = zero_substring_length;
auto pow_gain_reduced = pow_gain;
if (!parsed_digits.empty())
{
zero_copy_count = std::min(zero_substring_length, spare_digits);
pow_gain_reduced = std::min(pow_gain, spare_digits);
parsed_digits.insert(parsed_digits.end(), zero_copy_count, 0);
}
fraction_digits_count += zero_copy_count - pow_gain_reduced;
zero_substring_length -= zero_copy_count;
pow_gain -= pow_gain_reduced;
}
if (parsed_digits.size() < kappa)
{
parsed_digits.push_back(c - '0');
fraction_digits_count += dot_set;
}
else
{
if (!frac_calculated)
{
const std::size_t frac_suffix_size = parsed_digits.size() + zero_substring_length - kappa;
auto tail = extract_fraction_digits<fraction_decimal_digits>(str + pos - frac_suffix_size);
ret.frac = convert_numerator<fraction_decimal_digits, fraction_binary_digits>(tail);
frac_calculated = true;
}
pow_gain += !dot_set;
}
break;
case '.':
go_to_beach = dot_set;
dot_set = true;
break;
case 'n':
case 'N':
if (pos == sign_set)
{
const std::size_t eaten = eat_nan(str + pos + 1);
pos += eaten + 1;
if (eaten)
ret.special = speciality::nan;
}
go_to_beach = true;
break;
case 'i':
case 'I':
if (pos == sign_set)
{
const std::size_t eaten = eat_inf(str + pos + 1);
pos += eaten + 1;
if (eaten)
ret.special = speciality::inf;
}
go_to_beach = true;
break;
case '-':
case '+':
if (pos == 0)
{
ret.sign = static_cast<bool>('-' - c); // '+' => true, '-' => false
sign_set = true;
break;
}
// fall through
default:
go_to_beach = true;
break;
}
go_to_beach |= pos > maximum_offset;
++pos;
}
std::size_t pow(0);
for (auto rit = parsed_digits.rbegin(); rit != parsed_digits.rend(); ++rit)
ret.value += (*rit) * seq_pow<typename diy_fp<FloatType>::mantissa_storage_type, 10, decimal_q<FloatType>>(pow++);
ret.str_end = str + (pos - 1);
ret.K = pow_gain - fraction_digits_count;
return ret;
}
/** \brief Return structure for `parse_mantissa`.
*
* \tparam FloatType destination value floating point type.
* \tparam CharType character type (typically `char` or `wchar_t`) used.
*/
template<typename FloatType, typename CharType> struct mantissa_parse_result
{
/** \brief Calculated mantissa value. */
diy_fp<FloatType> value;
/** \brief Corrected value of decimal exponent value. */
int K;
/** \brief Pointer to the memory after the parsed part of the buffer. */
const CharType* str_end;
/** \brief Flag of special value. */
speciality special;
/** \brief Sign of the value. */
bool sign;
};
/** \brief Tides up results of `parse_digits` for **Krosh** to use.
*
* Packs mantissa value into `diy_fp` structure and performs the necessary
* rounding up according to the fractional part value.
*
* \tparam FloatType destination type of floating point value to store the
* results.
* \tparam CharType character type (typically `char` or `wchar_t`) \p **str**
* consists of.
*
* \param str Character buffer with floating point value representation to
* parse.
*
* \return `mantissa_parse_result` structure with the results of parsing
* and corrections.
*/
template<typename FloatType, typename CharType> inline mantissa_parse_result<FloatType, CharType> parse_mantissa(const CharType* str)
{
mantissa_parse_result<FloatType, CharType> ret;
const auto& digits_parts(parse_digits<FloatType>(str));
ret.special = digits_parts.special;
ret.str_end = digits_parts.str_end;
ret.sign = digits_parts.sign;
if (digits_parts.special == speciality::no)
{
ret.value = diy_fp<FloatType>(digits_parts.value, 0);
ret.K = digits_parts.K;
if (digits_parts.value)
{
auto& w(ret.value);
w.normalize();
// extract additional binary digits and round up gently
if (digits_parts.frac)
{
assert(w.exponent() > (-1) * static_cast<int>(fraction_binary_digits));
const std::size_t lsb_pow(fraction_binary_digits + w.exponent());
typename diy_fp<FloatType>::mantissa_storage_type f(w.mantissa());
f |= digits_parts.frac >> lsb_pow;
w = diy_fp<FloatType>(f, w.exponent());
// round correctly avoiding integer overflow, undefined behaviour, pain and suffering
if (round_up(digits_parts.frac, lsb_pow).value)
{
++w;
}
}
}
}
return ret;
}
/** \brief Return structure for `parse_exponent`.
*
* \tparam CharType character type (typically `char` or `wchar_t`) used.
*/
template<typename CharType> struct exponent_parse_result
{
/** \brief Value of the exponent. */
int value;
/** \brief Pointer to the memory after the parsed part of the buffer. */
const CharType* str_end;
};
/** \brief Parses exponent part of the floating point string representation.
*
* \tparam CharType character type (typically `char` or `wchar_t`) of \p **str**.
*
* \param str Exponent part of character buffer with floating point value
* representation to parse.
*
* \return `exponent_parse_result` structure with parse results.
*/
template<typename CharType> inline exponent_parse_result<CharType> parse_exponent(const CharType* str)
{
exponent_parse_result<CharType> ret;
if (*str != 'e' && *str != 'E')
{
ret.value = 0;
ret.str_end = str;
}
else
{
++str;
const auto& digit_parts(parse_digits<float>(str));
ret.value = digit_parts.value * seq_pow<int, 10, exponent_decimal_digits>(digit_parts.K);
if (!digit_parts.sign)
ret.value = -ret.value;
ret.str_end = digit_parts.str_end;
}
return ret;
}
/** \brief Return structure, containing **Krosh** algorithm results.
*
* \tparam FloatType destination type of floating point value to store the
* results.
* \tparam CharType character type (typically `char` or `wchar_t`) used.
*/
template<typename FloatType, typename CharType> struct krosh_result
{
/** \brief The result floating point value, downsampled to the defined
* floating point type.
*/
FloatType value;
/** \brief Pointer to the memory after the parsed part of the buffer. */
const CharType* str_end;
/** \brief Status of the performed conversion. */
conversion_status status;
/** \brief Flag indicating if the result ensured to be rounded correctly. */
bool is_accurate;
};
/** \brief Implements **Krosh** algorithm.
*
* \tparam FloatType destination type of floating point value to store the
* results.
*
* \tparam CharType character type (typically `char` or `wchar_t`) \p **str**
* consists of.
*
* \param str Character buffer with floating point value
* representation to parse.
*
* \return `krosh_result` structure with all the results of **Krosh**
* algorithm.
*/
template<typename FloatType, typename CharType> krosh_result<FloatType, CharType> krosh(const CharType* str)
{
krosh_result<FloatType, CharType> ret;
static_assert(sizeof(FloatType) <= sizeof(typename diy_fp<FloatType>::mantissa_storage_type), "Only floating point types no longer, than 64 bits are supported.");
auto mp(parse_mantissa<FloatType>(str));
if (mp.special == speciality::no && mp.value.mantissa())
{
diy_fp<FloatType>& w(mp.value);
const auto& ep(parse_exponent(mp.str_end));
mp.K += ep.value;
if (mp.K)
{
const bool b1 = mp.K >= powers_ten<FloatType>::boundaries.first;
const bool b2 = mp.K <= powers_ten<FloatType>::boundaries.second;
if (b1 && b2)
{
w *= cached_power<FloatType>(mp.K);
}
else
{
if (!b1)
{
ret.value = FloatType(0);
ret.status = conversion_status::underflow;
}
else // !b2
{
ret.value = huge_value<FloatType>();
ret.status = conversion_status::overflow;
}
ret.str_end = ep.str_end;
ret.is_accurate = true;
return ret;
}
}
w.normalize();
const auto& v(w.downsample());
ret.value = v.value;
ret.str_end = ep.str_end;
ret.is_accurate = v.is_accurate;
ret.status = v.status;
}
else
{
switch (mp.special)
{
case speciality::nan:
ret.value = std::numeric_limits<FloatType>::quiet_NaN();
break;
case speciality::inf:
ret.value = std::numeric_limits<FloatType>::infinity();
break;
default:
ret.value = 0;
break;
}
ret.str_end = mp.str_end;
ret.is_accurate = true;
ret.status = conversion_status::success;
}
if (!mp.sign)
ret.value = -ret.value;
return ret;
}
}
#endif // FLOAXIE_CROSH_H

View File

@@ -1,61 +0,0 @@
/*
* Copyright 2015, 2016 Alexey Chernov <4ernov@gmail.com>
*
* 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 FLOAXIE_MEMWRAP_H
#define FLOAXIE_MEMWRAP_H
#include <cstring>
#include <cwchar>
namespace floaxie
{
namespace wrap
{
/** \brief wrapper to template `std::(w)memset` by character type.
*
* \tparam CharType character type used.
*/
template<typename CharType> inline CharType* memset(CharType* dest, CharType ch, std::size_t count);
template<> inline char* memset(char* dest, char ch, std::size_t count)
{
return static_cast<char*>(std::memset(dest, ch, count));
}
template<> inline wchar_t* memset(wchar_t* dest, wchar_t ch, std::size_t count)
{
return std::wmemset(dest, ch, count);
}
/** \brief wrapper to template `std::(w)memmove` by character type.
*
* \tparam CharType character type used.
*/
template<typename CharType> inline CharType* memmove(CharType* dest, const CharType* src, std::size_t count);
template<> inline char* memmove(char* dest, const char* src, std::size_t count)
{
return static_cast<char*>(std::memmove(dest, src, count));
}
template<> inline wchar_t* memmove(wchar_t* dest, const wchar_t* src, std::size_t count)
{
return std::wmemmove(dest, src, count);
}
}
}
#endif // FLOAXIE_MEMWRAP_H

View File

@@ -1,47 +0,0 @@
/*
* Copyright 2015-2017 Alexey Chernov <4ernov@gmail.com>
*
* 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.
*
* cached power literals and cached_power() function use code taken from
* Florian Loitsch's original Grisu algorithms implementation
* (http://florian.loitsch.com/publications/bench.tar.gz)
* and "Printing Floating-Point Numbers Quickly and Accurately with
* Integers" paper
* (http://florian.loitsch.com/publications/dtoa-pldi2010.pdf)
*/
#ifndef FLOAXIE_POWERS_TEN_H
#define FLOAXIE_POWERS_TEN_H
namespace floaxie
{
/** \brief Structure template to store compile-time values of powers of ten.
*
* The template represents a structure family, which stores pre-calculated
* values of significands and exponents of powers of ten, represented in
* integer form of the specified precision.
*
* The exact values are written in specializations of the template for
* single precision or double precision floating point type.
*
* The template specializations are used in **Grisu** and **Krosh** algorithms
* to get the pre-calculated cached power of ten.
*
* \tparam FloatType floating point type, for which precision the values are
* calculated.
*/
template<typename FloatType> struct powers_ten;
}
#endif // FLOAXIE_POWERS_TEN_H

View File

@@ -1,419 +0,0 @@
/*
* Copyright 2015-2017 Alexey Chernov <4ernov@gmail.com>
*
* 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.
*
* cached power literals and cached_power() function use code taken from
* Florian Loitsch's original Grisu algorithms implementation
* (http://florian.loitsch.com/publications/bench.tar.gz)
* and "Printing Floating-Point Numbers Quickly and Accurately with
* Integers" paper
* (http://florian.loitsch.com/publications/dtoa-pldi2010.pdf)
*/
#ifndef FLOAXIE_POWERS_TEN_DOUBLE_H
#define FLOAXIE_POWERS_TEN_DOUBLE_H
#include <cstddef>
#include <array>
#include <external/floaxie/floaxie/powers_ten.h>
namespace floaxie
{
/** \brief Specialization of **powers_ten** for double precision
* floating point type (`double`).
*
* Significand (mantissa) type is `std::uint64_t`, exponent type is
* `int` (typically 32 bit, at least 16 bit).
*
* Values are calculated for powers of 10 in the range of [-343, 343]
* exponent.
*/
template<> struct powers_ten<double>
{
/** \brief Pre-calculated binary 64-bit representation of mantissa of
* powers of 10 in the range of [-343, 343] for single precision
* floating point values.
*/
static constexpr std::uint64_t f[] =
{
0xbf29dcaba82fdeae , 0xeef453d6923bd65a , 0x9558b4661b6565f8 , 0xbaaee17fa23ebf76 ,
0xe95a99df8ace6f54 , 0x91d8a02bb6c10594 , 0xb64ec836a47146fa , 0xe3e27a444d8d98b8 ,
0x8e6d8c6ab0787f73 , 0xb208ef855c969f50 , 0xde8b2b66b3bc4724 , 0x8b16fb203055ac76 ,
0xaddcb9e83c6b1794 , 0xd953e8624b85dd79 , 0x87d4713d6f33aa6c , 0xa9c98d8ccb009506 ,
0xd43bf0effdc0ba48 , 0x84a57695fe98746d , 0xa5ced43b7e3e9188 , 0xcf42894a5dce35ea ,
0x818995ce7aa0e1b2 , 0xa1ebfb4219491a1f , 0xca66fa129f9b60a7 , 0xfd00b897478238d1 ,
0x9e20735e8cb16382 , 0xc5a890362fddbc63 , 0xf712b443bbd52b7c , 0x9a6bb0aa55653b2d ,
0xc1069cd4eabe89f9 , 0xf148440a256e2c77 , 0x96cd2a865764dbca , 0xbc807527ed3e12bd ,
0xeba09271e88d976c , 0x93445b8731587ea3 , 0xb8157268fdae9e4c , 0xe61acf033d1a45df ,
0x8fd0c16206306bac , 0xb3c4f1ba87bc8697 , 0xe0b62e2929aba83c , 0x8c71dcd9ba0b4926 ,
0xaf8e5410288e1b6f , 0xdb71e91432b1a24b , 0x892731ac9faf056f , 0xab70fe17c79ac6ca ,
0xd64d3d9db981787d , 0x85f0468293f0eb4e , 0xa76c582338ed2622 , 0xd1476e2c07286faa ,
0x82cca4db847945ca , 0xa37fce126597973d , 0xcc5fc196fefd7d0c , 0xff77b1fcbebcdc4f ,
0x9faacf3df73609b1 , 0xc795830d75038c1e , 0xf97ae3d0d2446f25 , 0x9becce62836ac577 ,
0xc2e801fb244576d5 , 0xf3a20279ed56d48a , 0x9845418c345644d7 , 0xbe5691ef416bd60c ,
0xedec366b11c6cb8f , 0x94b3a202eb1c3f39 , 0xb9e08a83a5e34f08 , 0xe858ad248f5c22ca ,
0x91376c36d99995be , 0xb58547448ffffb2e , 0xe2e69915b3fff9f9 , 0x8dd01fad907ffc3c ,
0xb1442798f49ffb4b , 0xdd95317f31c7fa1d , 0x8a7d3eef7f1cfc52 , 0xad1c8eab5ee43b67 ,
0xd863b256369d4a41 , 0x873e4f75e2224e68 , 0xa90de3535aaae202 , 0xd3515c2831559a83 ,
0x8412d9991ed58092 , 0xa5178fff668ae0b6 , 0xce5d73ff402d98e4 , 0x80fa687f881c7f8e ,
0xa139029f6a239f72 , 0xc987434744ac874f , 0xfbe9141915d7a922 , 0x9d71ac8fada6c9b5 ,
0xc4ce17b399107c23 , 0xf6019da07f549b2b , 0x99c102844f94e0fb , 0xc0314325637a193a ,
0xf03d93eebc589f88 , 0x96267c7535b763b5 , 0xbbb01b9283253ca3 , 0xea9c227723ee8bcb ,
0x92a1958a7675175f , 0xb749faed14125d37 , 0xe51c79a85916f485 , 0x8f31cc0937ae58d3 ,
0xb2fe3f0b8599ef08 , 0xdfbdcece67006ac9 , 0x8bd6a141006042be , 0xaecc49914078536d ,
0xda7f5bf590966849 , 0x888f99797a5e012d , 0xaab37fd7d8f58179 , 0xd5605fcdcf32e1d7 ,
0x855c3be0a17fcd26 , 0xa6b34ad8c9dfc070 , 0xd0601d8efc57b08c , 0x823c12795db6ce57 ,
0xa2cb1717b52481ed , 0xcb7ddcdda26da269 , 0xfe5d54150b090b03 , 0x9efa548d26e5a6e2 ,
0xc6b8e9b0709f109a , 0xf867241c8cc6d4c1 , 0x9b407691d7fc44f8 , 0xc21094364dfb5637 ,
0xf294b943e17a2bc4 , 0x979cf3ca6cec5b5b , 0xbd8430bd08277231 , 0xece53cec4a314ebe ,
0x940f4613ae5ed137 , 0xb913179899f68584 , 0xe757dd7ec07426e5 , 0x9096ea6f3848984f ,
0xb4bca50b065abe63 , 0xe1ebce4dc7f16dfc , 0x8d3360f09cf6e4bd , 0xb080392cc4349ded ,
0xdca04777f541c568 , 0x89e42caaf9491b61 , 0xac5d37d5b79b6239 , 0xd77485cb25823ac7 ,
0x86a8d39ef77164bd , 0xa8530886b54dbdec , 0xd267caa862a12d67 , 0x8380dea93da4bc60 ,
0xa46116538d0deb78 , 0xcd795be870516656 , 0x806bd9714632dff6 , 0xa086cfcd97bf97f4 ,
0xc8a883c0fdaf7df0 , 0xfad2a4b13d1b5d6c , 0x9cc3a6eec6311a64 , 0xc3f490aa77bd60fd ,
0xf4f1b4d515acb93c , 0x991711052d8bf3c5 , 0xbf5cd54678eef0b7 , 0xef340a98172aace5 ,
0x9580869f0e7aac0f , 0xbae0a846d2195713 , 0xe998d258869facd7 , 0x91ff83775423cc06 ,
0xb67f6455292cbf08 , 0xe41f3d6a7377eeca , 0x8e938662882af53e , 0xb23867fb2a35b28e ,
0xdec681f9f4c31f31 , 0x8b3c113c38f9f37f , 0xae0b158b4738705f , 0xd98ddaee19068c76 ,
0x87f8a8d4cfa417ca , 0xa9f6d30a038d1dbc , 0xd47487cc8470652b , 0x84c8d4dfd2c63f3b ,
0xa5fb0a17c777cf0a , 0xcf79cc9db955c2cc , 0x81ac1fe293d599c0 , 0xa21727db38cb0030 ,
0xca9cf1d206fdc03c , 0xfd442e4688bd304b , 0x9e4a9cec15763e2f , 0xc5dd44271ad3cdba ,
0xf7549530e188c129 , 0x9a94dd3e8cf578ba , 0xc13a148e3032d6e8 , 0xf18899b1bc3f8ca2 ,
0x96f5600f15a7b7e5 , 0xbcb2b812db11a5de , 0xebdf661791d60f56 , 0x936b9fcebb25c996 ,
0xb84687c269ef3bfb , 0xe65829b3046b0afa , 0x8ff71a0fe2c2e6dc , 0xb3f4e093db73a093 ,
0xe0f218b8d25088b8 , 0x8c974f7383725573 , 0xafbd2350644eead0 , 0xdbac6c247d62a584 ,
0x894bc396ce5da772 , 0xab9eb47c81f5114f , 0xd686619ba27255a3 , 0x8613fd0145877586 ,
0xa798fc4196e952e7 , 0xd17f3b51fca3a7a1 , 0x82ef85133de648c5 , 0xa3ab66580d5fdaf6 ,
0xcc963fee10b7d1b3 , 0xffbbcfe994e5c620 , 0x9fd561f1fd0f9bd4 , 0xc7caba6e7c5382c9 ,
0xf9bd690a1b68637b , 0x9c1661a651213e2d , 0xc31bfa0fe5698db8 , 0xf3e2f893dec3f126 ,
0x986ddb5c6b3a76b8 , 0xbe89523386091466 , 0xee2ba6c0678b597f , 0x94db483840b717f0 ,
0xba121a4650e4ddec , 0xe896a0d7e51e1566 , 0x915e2486ef32cd60 , 0xb5b5ada8aaff80b8 ,
0xe3231912d5bf60e6 , 0x8df5efabc5979c90 , 0xb1736b96b6fd83b4 , 0xddd0467c64bce4a1 ,
0x8aa22c0dbef60ee4 , 0xad4ab7112eb3929e , 0xd89d64d57a607745 , 0x87625f056c7c4a8b ,
0xa93af6c6c79b5d2e , 0xd389b47879823479 , 0x843610cb4bf160cc , 0xa54394fe1eedb8ff ,
0xce947a3da6a9273e , 0x811ccc668829b887 , 0xa163ff802a3426a9 , 0xc9bcff6034c13053 ,
0xfc2c3f3841f17c68 , 0x9d9ba7832936edc1 , 0xc5029163f384a931 , 0xf64335bcf065d37d ,
0x99ea0196163fa42e , 0xc06481fb9bcf8d3a , 0xf07da27a82c37088 , 0x964e858c91ba2655 ,
0xbbe226efb628afeb , 0xeadab0aba3b2dbe5 , 0x92c8ae6b464fc96f , 0xb77ada0617e3bbcb ,
0xe55990879ddcaabe , 0x8f57fa54c2a9eab7 , 0xb32df8e9f3546564 , 0xdff9772470297ebd ,
0x8bfbea76c619ef36 , 0xaefae51477a06b04 , 0xdab99e59958885c5 , 0x88b402f7fd75539b ,
0xaae103b5fcd2a882 , 0xd59944a37c0752a2 , 0x857fcae62d8493a5 , 0xa6dfbd9fb8e5b88f ,
0xd097ad07a71f26b2 , 0x825ecc24c8737830 , 0xa2f67f2dfa90563b , 0xcbb41ef979346bca ,
0xfea126b7d78186bd , 0x9f24b832e6b0f436 , 0xc6ede63fa05d3144 , 0xf8a95fcf88747d94 ,
0x9b69dbe1b548ce7d , 0xc24452da229b021c , 0xf2d56790ab41c2a3 , 0x97c560ba6b0919a6 ,
0xbdb6b8e905cb600f , 0xed246723473e3813 , 0x9436c0760c86e30c , 0xb94470938fa89bcf ,
0xe7958cb87392c2c3 , 0x90bd77f3483bb9ba , 0xb4ecd5f01a4aa828 , 0xe2280b6c20dd5232 ,
0x8d590723948a535f , 0xb0af48ec79ace837 , 0xdcdb1b2798182245 , 0x8a08f0f8bf0f156b ,
0xac8b2d36eed2dac6 , 0xd7adf884aa879177 , 0x86ccbb52ea94baeb , 0xa87fea27a539e9a5 ,
0xd29fe4b18e88640f , 0x83a3eeeef9153e89 , 0xa48ceaaab75a8e2b , 0xcdb02555653131b6 ,
0x808e17555f3ebf12 , 0xa0b19d2ab70e6ed6 , 0xc8de047564d20a8c , 0xfb158592be068d2f ,
0x9ced737bb6c4183d , 0xc428d05aa4751e4d , 0xf53304714d9265e0 , 0x993fe2c6d07b7fac ,
0xbf8fdb78849a5f97 , 0xef73d256a5c0f77d , 0x95a8637627989aae , 0xbb127c53b17ec159 ,
0xe9d71b689dde71b0 , 0x9226712162ab070e , 0xb6b00d69bb55c8d1 , 0xe45c10c42a2b3b06 ,
0x8eb98a7a9a5b04e3 , 0xb267ed1940f1c61c , 0xdf01e85f912e37a3 , 0x8b61313bbabce2c6 ,
0xae397d8aa96c1b78 , 0xd9c7dced53c72256 , 0x881cea14545c7575 , 0xaa242499697392d3 ,
0xd4ad2dbfc3d07788 , 0x84ec3c97da624ab5 , 0xa6274bbdd0fadd62 , 0xcfb11ead453994ba ,
0x81ceb32c4b43fcf5 , 0xa2425ff75e14fc32 , 0xcad2f7f5359a3b3e , 0xfd87b5f28300ca0e ,
0x9e74d1b791e07e48 , 0xc612062576589ddb , 0xf79687aed3eec551 , 0x9abe14cd44753b53 ,
0xc16d9a0095928a27 , 0xf1c90080baf72cb1 , 0x971da05074da7bef , 0xbce5086492111aeb ,
0xec1e4a7db69561a5 , 0x9392ee8e921d5d07 , 0xb877aa3236a4b449 , 0xe69594bec44de15b ,
0x901d7cf73ab0acd9 , 0xb424dc35095cd80f , 0xe12e13424bb40e13 , 0x8cbccc096f5088cc ,
0xafebff0bcb24aaff , 0xdbe6fecebdedd5bf , 0x89705f4136b4a597 , 0xabcc77118461cefd ,
0xd6bf94d5e57a42bc , 0x8637bd05af6c69b6 , 0xa7c5ac471b478423 , 0xd1b71758e219652c ,
0x83126e978d4fdf3b , 0xa3d70a3d70a3d70a , 0xcccccccccccccccd , 0x8000000000000000 ,
0xa000000000000000 , 0xc800000000000000 , 0xfa00000000000000 , 0x9c40000000000000 ,
0xc350000000000000 , 0xf424000000000000 , 0x9896800000000000 , 0xbebc200000000000 ,
0xee6b280000000000 , 0x9502f90000000000 , 0xba43b74000000000 , 0xe8d4a51000000000 ,
0x9184e72a00000000 , 0xb5e620f480000000 , 0xe35fa931a0000000 , 0x8e1bc9bf04000000 ,
0xb1a2bc2ec5000000 , 0xde0b6b3a76400000 , 0x8ac7230489e80000 , 0xad78ebc5ac620000 ,
0xd8d726b7177a8000 , 0x878678326eac9000 , 0xa968163f0a57b400 , 0xd3c21bcecceda100 ,
0x84595161401484a0 , 0xa56fa5b99019a5c8 , 0xcecb8f27f4200f3a , 0x813f3978f8940984 ,
0xa18f07d736b90be5 , 0xc9f2c9cd04674edf , 0xfc6f7c4045812296 , 0x9dc5ada82b70b59e ,
0xc5371912364ce305 , 0xf684df56c3e01bc7 , 0x9a130b963a6c115c , 0xc097ce7bc90715b3 ,
0xf0bdc21abb48db20 , 0x96769950b50d88f4 , 0xbc143fa4e250eb31 , 0xeb194f8e1ae525fd ,
0x92efd1b8d0cf37be , 0xb7abc627050305ae , 0xe596b7b0c643c719 , 0x8f7e32ce7bea5c70 ,
0xb35dbf821ae4f38c , 0xe0352f62a19e306f , 0x8c213d9da502de45 , 0xaf298d050e4395d7 ,
0xdaf3f04651d47b4c , 0x88d8762bf324cd10 , 0xab0e93b6efee0054 , 0xd5d238a4abe98068 ,
0x85a36366eb71f041 , 0xa70c3c40a64e6c52 , 0xd0cf4b50cfe20766 , 0x82818f1281ed44a0 ,
0xa321f2d7226895c8 , 0xcbea6f8ceb02bb3a , 0xfee50b7025c36a08 , 0x9f4f2726179a2245 ,
0xc722f0ef9d80aad6 , 0xf8ebad2b84e0d58c , 0x9b934c3b330c8577 , 0xc2781f49ffcfa6d5 ,
0xf316271c7fc3908b , 0x97edd871cfda3a57 , 0xbde94e8e43d0c8ec , 0xed63a231d4c4fb27 ,
0x945e455f24fb1cf9 , 0xb975d6b6ee39e437 , 0xe7d34c64a9c85d44 , 0x90e40fbeea1d3a4b ,
0xb51d13aea4a488dd , 0xe264589a4dcdab15 , 0x8d7eb76070a08aed , 0xb0de65388cc8ada8 ,
0xdd15fe86affad912 , 0x8a2dbf142dfcc7ab , 0xacb92ed9397bf996 , 0xd7e77a8f87daf7fc ,
0x86f0ac99b4e8dafd , 0xa8acd7c0222311bd , 0xd2d80db02aabd62c , 0x83c7088e1aab65db ,
0xa4b8cab1a1563f52 , 0xcde6fd5e09abcf27 , 0x80b05e5ac60b6178 , 0xa0dc75f1778e39d6 ,
0xc913936dd571c84c , 0xfb5878494ace3a5f , 0x9d174b2dcec0e47b , 0xc45d1df942711d9a ,
0xf5746577930d6501 , 0x9968bf6abbe85f20 , 0xbfc2ef456ae276e9 , 0xefb3ab16c59b14a3 ,
0x95d04aee3b80ece6 , 0xbb445da9ca61281f , 0xea1575143cf97227 , 0x924d692ca61be758 ,
0xb6e0c377cfa2e12e , 0xe498f455c38b997a , 0x8edf98b59a373fec , 0xb2977ee300c50fe7 ,
0xdf3d5e9bc0f653e1 , 0x8b865b215899f46d , 0xae67f1e9aec07188 , 0xda01ee641a708dea ,
0x884134fe908658b2 , 0xaa51823e34a7eedf , 0xd4e5e2cdc1d1ea96 , 0x850fadc09923329e ,
0xa6539930bf6bff46 , 0xcfe87f7cef46ff17 , 0x81f14fae158c5f6e , 0xa26da3999aef774a ,
0xcb090c8001ab551c , 0xfdcb4fa002162a63 , 0x9e9f11c4014dda7e , 0xc646d63501a1511e ,
0xf7d88bc24209a565 , 0x9ae757596946075f , 0xc1a12d2fc3978937 , 0xf209787bb47d6b85 ,
0x9745eb4d50ce6333 , 0xbd176620a501fc00 , 0xec5d3fa8ce427b00 , 0x93ba47c980e98ce0 ,
0xb8a8d9bbe123f018 , 0xe6d3102ad96cec1e , 0x9043ea1ac7e41393 , 0xb454e4a179dd1877 ,
0xe16a1dc9d8545e95 , 0x8ce2529e2734bb1d , 0xb01ae745b101e9e4 , 0xdc21a1171d42645d ,
0x899504ae72497eba , 0xabfa45da0edbde69 , 0xd6f8d7509292d603 , 0x865b86925b9bc5c2 ,
0xa7f26836f282b733 , 0xd1ef0244af2364ff , 0x8335616aed761f1f , 0xa402b9c5a8d3a6e7 ,
0xcd036837130890a1 , 0x802221226be55a65 , 0xa02aa96b06deb0fe , 0xc83553c5c8965d3d ,
0xfa42a8b73abbf48d , 0x9c69a97284b578d8 , 0xc38413cf25e2d70e , 0xf46518c2ef5b8cd1 ,
0x98bf2f79d5993803 , 0xbeeefb584aff8604 , 0xeeaaba2e5dbf6785 , 0x952ab45cfa97a0b3 ,
0xba756174393d88e0 , 0xe912b9d1478ceb17 , 0x91abb422ccb812ef , 0xb616a12b7fe617aa ,
0xe39c49765fdf9d95 , 0x8e41ade9fbebc27d , 0xb1d219647ae6b31c , 0xde469fbd99a05fe3 ,
0x8aec23d680043bee , 0xada72ccc20054aea , 0xd910f7ff28069da4 , 0x87aa9aff79042287 ,
0xa99541bf57452b28 , 0xd3fa922f2d1675f2 , 0x847c9b5d7c2e09b7 , 0xa59bc234db398c25 ,
0xcf02b2c21207ef2f , 0x8161afb94b44f57d , 0xa1ba1ba79e1632dc , 0xca28a291859bbf93 ,
0xfcb2cb35e702af78 , 0x9defbf01b061adab , 0xc56baec21c7a1916 , 0xf6c69a72a3989f5c ,
0x9a3c2087a63f6399 , 0xc0cb28a98fcf3c80 , 0xf0fdf2d3f3c30b9f , 0x969eb7c47859e744 ,
0xbc4665b596706115 , 0xeb57ff22fc0c795a , 0x9316ff75dd87cbd8 , 0xb7dcbf5354e9bece ,
0xe5d3ef282a242e82 , 0x8fa475791a569d11 , 0xb38d92d760ec4455 , 0xe070f78d3927556b ,
0x8c469ab843b89563 , 0xaf58416654a6babb , 0xdb2e51bfe9d0696a , 0x88fcf317f22241e2 ,
0xab3c2fddeeaad25b , 0xd60b3bd56a5586f2 , 0x85c7056562757457 , 0xa738c6bebb12d16d ,
0xd106f86e69d785c8 , 0x82a45b450226b39d , 0xa34d721642b06084 , 0xcc20ce9bd35c78a5 ,
0xff290242c83396ce , 0x9f79a169bd203e41 , 0xc75809c42c684dd1 , 0xf92e0c3537826146 ,
0x9bbcc7a142b17ccc , 0xc2abf989935ddbfe , 0xf356f7ebf83552fe , 0x98165af37b2153df ,
0xbe1bf1b059e9a8d6 , 0xeda2ee1c7064130c , 0x9485d4d1c63e8be8 , 0xb9a74a0637ce2ee1 ,
0xe8111c87c5c1ba9a , 0x910ab1d4db9914a0 , 0xb54d5e4a127f59c8 , 0xe2a0b5dc971f303a ,
0x8da471a9de737e24 , 0xb10d8e1456105dad , 0xdd50f1996b947519 , 0x8a5296ffe33cc930 ,
0xace73cbfdc0bfb7b , 0xd8210befd30efa5a , 0x8714a775e3e95c78 , 0xa8d9d1535ce3b396 ,
0xd31045a8341ca07c , 0x83ea2b892091e44e , 0xa4e4b66b68b65d61 , 0xce1de40642e3f4b9 ,
0x80d2ae83e9ce78f4 , 0xa1075a24e4421731 , 0xc94930ae1d529cfd , 0xfb9b7cd9a4a7443c ,
0x9d412e0806e88aa6 , 0xc491798a08a2ad4f , 0xf5b5d7ec8acb58a3 , 0x9991a6f3d6bf1766 ,
0xbff610b0cc6edd3f , 0xeff394dcff8a948f , 0x95f83d0a1fb69cd9 , 0xbb764c4ca7a44410 ,
0xea53df5fd18d5514 , 0x92746b9be2f8552c , 0xb7118682dbb66a77 , 0xe4d5e82392a40515 ,
0x8f05b1163ba6832d , 0xb2c71d5bca9023f8 , 0xdf78e4b2bd342cf7 , 0x8bab8eefb6409c1a ,
0xae9672aba3d0c321 , 0xda3c0f568cc4f3e9 , 0x8865899617fb1871 , 0xaa7eebfb9df9de8e ,
0xd51ea6fa85785631 , 0x8533285c936b35df , 0xa67ff273b8460357 , 0xd01fef10a657842c ,
0x8213f56a67f6b29c , 0xa298f2c501f45f43 , 0xcb3f2f7642717713 , 0xfe0efb53d30dd4d8 ,
0x9ec95d1463e8a507 , 0xc67bb4597ce2ce49 , 0xf81aa16fdc1b81db , 0x9b10a4e5e9913129 ,
0xc1d4ce1f63f57d73 , 0xf24a01a73cf2dcd0 , 0x976e41088617ca02 , 0xbd49d14aa79dbc82 ,
0xec9c459d51852ba3 , 0x93e1ab8252f33b46 , 0xb8da1662e7b00a17 , 0xe7109bfba19c0c9d ,
0x906a617d450187e2 , 0xb484f9dc9641e9db , 0xe1a63853bbd26451 , 0x8d07e33455637eb3 ,
0xb049dc016abc5e60 , 0xdc5c5301c56b75f7 , 0x89b9b3e11b6329bb , 0xac2820d9623bf429 ,
0xd732290fbacaf134 , 0x867f59a9d4bed6c0 , 0xa81f301449ee8c70 , 0xd226fc195c6a2f8c ,
0x83585d8fd9c25db8 , 0xa42e74f3d032f526 , 0xcd3a1230c43fb26f , 0x80444b5e7aa7cf85 ,
0xa0555e361951c367 , 0xc86ab5c39fa63441 , 0xfa856334878fc151 , 0x9c935e00d4b9d8d2 ,
0xc3b8358109e84f07 , 0xf4a642e14c6262c9 , 0x98e7e9cccfbd7dbe , 0xbf21e44003acdd2d ,
0xeeea5d5004981478 , 0x95527a5202df0ccb , 0xbaa718e68396cffe , 0xe950df20247c83fd ,
0x91d28b7416cdd27e , 0xb6472e511c81471e , 0xe3d8f9e563a198e5 , 0x8e679c2f5e44ff8f ,
0xb201833b35d63f73 , 0xde81e40a034bcf50 , 0x8b112e86420f6192 , 0xadd57a27d29339f6 ,
0xd94ad8b1c7380874 , 0x87cec76f1c830549 , 0xa9c2794ae3a3c69b , 0xd433179d9c8cb841 ,
0x849feec281d7f329 , 0xa5c7ea73224deff3 , 0xcf39e50feae16bf0 , 0x81842f29f2cce376 ,
0xa1e53af46f801c53 , 0xca5e89b18b602368 , 0xfcf62c1dee382c42 , 0x9e19db92b4e31ba9 ,
0xc5a05277621be294 , 0xf70867153aa2db39 , 0x9a65406d44a5c903 , 0xc0fe908895cf3b44 ,
0xf13e34aabb430a15 , 0x96c6e0eab509e64d , 0xbc789925624c5fe1 , 0xeb96bf6ebadf77d9 ,
0x933e37a534cbaae8 , 0xb80dc58e81fe95a1 , 0xe61136f2227e3b0a , 0x8fcac257558ee4e6 ,
0xb3bd72ed2af29e20 , 0xe0accfa875af45a8 , 0x8c6c01c9498d8b89 , 0xaf87023b9bf0ee6b ,
0xdb68c2ca82ed2a06 , 0x892179be91d43a44 , 0xab69d82e364948d4
};
/** \brief Pre-calculated values of binary exponent of powers of 10 in the
* range of [-343, 343].
*/
static constexpr int e[] =
{
-1203 , -1200 , -1196 , -1193 ,
-1190 , -1186 , -1183 , -1180 ,
-1176 , -1173 , -1170 , -1166 ,
-1163 , -1160 , -1156 , -1153 ,
-1150 , -1146 , -1143 , -1140 ,
-1136 , -1133 , -1130 , -1127 ,
-1123 , -1120 , -1117 , -1113 ,
-1110 , -1107 , -1103 , -1100 ,
-1097 , -1093 , -1090 , -1087 ,
-1083 , -1080 , -1077 , -1073 ,
-1070 , -1067 , -1063 , -1060 ,
-1057 , -1053 , -1050 , -1047 ,
-1043 , -1040 , -1037 , -1034 ,
-1030 , -1027 , -1024 , -1020 ,
-1017 , -1014 , -1010 , -1007 ,
-1004 , -1000 , -997 , -994 ,
-990 , -987 , -984 , -980 ,
-977 , -974 , -970 , -967 ,
-964 , -960 , -957 , -954 ,
-950 , -947 , -944 , -940 ,
-937 , -934 , -931 , -927 ,
-924 , -921 , -917 , -914 ,
-911 , -907 , -904 , -901 ,
-897 , -894 , -891 , -887 ,
-884 , -881 , -877 , -874 ,
-871 , -867 , -864 , -861 ,
-857 , -854 , -851 , -847 ,
-844 , -841 , -838 , -834 ,
-831 , -828 , -824 , -821 ,
-818 , -814 , -811 , -808 ,
-804 , -801 , -798 , -794 ,
-791 , -788 , -784 , -781 ,
-778 , -774 , -771 , -768 ,
-764 , -761 , -758 , -754 ,
-751 , -748 , -744 , -741 ,
-738 , -735 , -731 , -728 ,
-725 , -721 , -718 , -715 ,
-711 , -708 , -705 , -701 ,
-698 , -695 , -691 , -688 ,
-685 , -681 , -678 , -675 ,
-671 , -668 , -665 , -661 ,
-658 , -655 , -651 , -648 ,
-645 , -642 , -638 , -635 ,
-632 , -628 , -625 , -622 ,
-618 , -615 , -612 , -608 ,
-605 , -602 , -598 , -595 ,
-592 , -588 , -585 , -582 ,
-578 , -575 , -572 , -568 ,
-565 , -562 , -558 , -555 ,
-552 , -549 , -545 , -542 ,
-539 , -535 , -532 , -529 ,
-525 , -522 , -519 , -515 ,
-512 , -509 , -505 , -502 ,
-499 , -495 , -492 , -489 ,
-485 , -482 , -479 , -475 ,
-472 , -469 , -465 , -462 ,
-459 , -455 , -452 , -449 ,
-446 , -442 , -439 , -436 ,
-432 , -429 , -426 , -422 ,
-419 , -416 , -412 , -409 ,
-406 , -402 , -399 , -396 ,
-392 , -389 , -386 , -382 ,
-379 , -376 , -372 , -369 ,
-366 , -362 , -359 , -356 ,
-353 , -349 , -346 , -343 ,
-339 , -336 , -333 , -329 ,
-326 , -323 , -319 , -316 ,
-313 , -309 , -306 , -303 ,
-299 , -296 , -293 , -289 ,
-286 , -283 , -279 , -276 ,
-273 , -269 , -266 , -263 ,
-259 , -256 , -253 , -250 ,
-246 , -243 , -240 , -236 ,
-233 , -230 , -226 , -223 ,
-220 , -216 , -213 , -210 ,
-206 , -203 , -200 , -196 ,
-193 , -190 , -186 , -183 ,
-180 , -176 , -173 , -170 ,
-166 , -163 , -160 , -157 ,
-153 , -150 , -147 , -143 ,
-140 , -137 , -133 , -130 ,
-127 , -123 , -120 , -117 ,
-113 , -110 , -107 , -103 ,
-100 , -97 , -93 , -90 ,
-87 , -83 , -80 , -77 ,
-73 , -70 , -67 , -63 ,
-60 , -57 , -54 , -50 ,
-47 , -44 , -40 , -37 ,
-34 , -30 , -27 , -24 ,
-20 , -17 , -14 , -10 ,
-7 , -4 , 0 , 3 ,
6 , 10 , 13 , 16 ,
20 , 23 , 26 , 30 ,
33 , 36 , 39 , 43 ,
46 , 49 , 53 , 56 ,
59 , 63 , 66 , 69 ,
73 , 76 , 79 , 83 ,
86 , 89 , 93 , 96 ,
99 , 103 , 106 , 109 ,
113 , 116 , 119 , 123 ,
126 , 129 , 132 , 136 ,
139 , 142 , 146 , 149 ,
152 , 156 , 159 , 162 ,
166 , 169 , 172 , 176 ,
179 , 182 , 186 , 189 ,
192 , 196 , 199 , 202 ,
206 , 209 , 212 , 216 ,
219 , 222 , 226 , 229 ,
232 , 235 , 239 , 242 ,
245 , 249 , 252 , 255 ,
259 , 262 , 265 , 269 ,
272 , 275 , 279 , 282 ,
285 , 289 , 292 , 295 ,
299 , 302 , 305 , 309 ,
312 , 315 , 319 , 322 ,
325 , 328 , 332 , 335 ,
338 , 342 , 345 , 348 ,
352 , 355 , 358 , 362 ,
365 , 368 , 372 , 375 ,
378 , 382 , 385 , 388 ,
392 , 395 , 398 , 402 ,
405 , 408 , 412 , 415 ,
418 , 422 , 425 , 428 ,
431 , 435 , 438 , 441 ,
445 , 448 , 451 , 455 ,
458 , 461 , 465 , 468 ,
471 , 475 , 478 , 481 ,
485 , 488 , 491 , 495 ,
498 , 501 , 505 , 508 ,
511 , 515 , 518 , 521 ,
524 , 528 , 531 , 534 ,
538 , 541 , 544 , 548 ,
551 , 554 , 558 , 561 ,
564 , 568 , 571 , 574 ,
578 , 581 , 584 , 588 ,
591 , 594 , 598 , 601 ,
604 , 608 , 611 , 614 ,
617 , 621 , 624 , 627 ,
631 , 634 , 637 , 641 ,
644 , 647 , 651 , 654 ,
657 , 661 , 664 , 667 ,
671 , 674 , 677 , 681 ,
684 , 687 , 691 , 694 ,
697 , 701 , 704 , 707 ,
711 , 714 , 717 , 720 ,
724 , 727 , 730 , 734 ,
737 , 740 , 744 , 747 ,
750 , 754 , 757 , 760 ,
764 , 767 , 770 , 774 ,
777 , 780 , 784 , 787 ,
790 , 794 , 797 , 800 ,
804 , 807 , 810 , 813 ,
817 , 820 , 823 , 827 ,
830 , 833 , 837 , 840 ,
843 , 847 , 850 , 853 ,
857 , 860 , 863 , 867 ,
870 , 873 , 877 , 880 ,
883 , 887 , 890 , 893 ,
897 , 900 , 903 , 907 ,
910 , 913 , 916 , 920 ,
923 , 926 , 930 , 933 ,
936 , 940 , 943 , 946 ,
950 , 953 , 956 , 960 ,
963 , 966 , 970 , 973 ,
976 , 980 , 983 , 986 ,
990 , 993 , 996 , 1000 ,
1003 , 1006 , 1009 , 1013 ,
1016 , 1019 , 1023 , 1026 ,
1029 , 1033 , 1036 , 1039 ,
1043 , 1046 , 1049 , 1053 ,
1056 , 1059 , 1063 , 1066 ,
1069 , 1073 , 1076
};
/** \brief Offsef of the values for zero power in the arrays.
*/
static constexpr std::size_t pow_0_offset = 343;
/** \brief Boundaries of possible powers of ten for the type.
*/
static constexpr std::pair<int, int> boundaries = { -343, 343 };
};
constexpr decltype(powers_ten<double>::f) powers_ten<double>::f;
constexpr decltype(powers_ten<double>::e) powers_ten<double>::e;
constexpr std::size_t powers_ten<double>::pow_0_offset;
constexpr std::pair<int, int> powers_ten<double>::boundaries;
}
#endif // FLOAXIE_POWERS_TEN_DOUBLE_H

View File

@@ -1,276 +0,0 @@
/*
* Copyright 2015-2017 Alexey Chernov <4ernov@gmail.com>
*
* 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.
*
* cached power literals and cached_power() function use code taken from
* Florian Loitsch's original Grisu algorithms implementation
* (http://florian.loitsch.com/publications/bench.tar.gz)
* and "Printing Floating-Point Numbers Quickly and Accurately with
* Integers" paper
* (http://florian.loitsch.com/publications/dtoa-pldi2010.pdf)
*/
#ifndef FLOAXIE_POWERS_TEN_SINGLE_H
#define FLOAXIE_POWERS_TEN_SINGLE_H
#include <cstddef>
#include <external/floaxie/floaxie/powers_ten.h>
namespace floaxie
{
/** \brief Specialization of **powers_ten** for single precision
* floating point type (`float`).
*
* Significand (mantissa) type is `std::uint32_t`, exponent type is
* `int` (typically 32 bit, at least 16 bit).
*
* Values are calculated for powers of 10 in the range of [-50, 50]
* exponent.
*/
template<> struct powers_ten<float>
{
/** \brief Pre-calculated binary 32-bit representation of mantissa of
* powers of 10 in the range of [-50, 50] for single precision
* floating point values.
*/
static constexpr std::uint32_t f[] =
{
0xef73d257,
0x95a86376,
0xbb127c54,
0xe9d71b69,
0x92267121,
0xb6b00d6a,
0xe45c10c4,
0x8eb98a7b,
0xb267ed19,
0xdf01e860,
0x8b61313c,
0xae397d8b,
0xd9c7dced,
0x881cea14,
0xaa242499,
0xd4ad2dc0,
0x84ec3c98,
0xa6274bbe,
0xcfb11ead,
0x81ceb32c,
0xa2425ff7,
0xcad2f7f5,
0xfd87b5f3,
0x9e74d1b8,
0xc6120625,
0xf79687af,
0x9abe14cd,
0xc16d9a01,
0xf1c90081,
0x971da050,
0xbce50865,
0xec1e4a7e,
0x9392ee8f,
0xb877aa32,
0xe69594bf,
0x901d7cf7,
0xb424dc35,
0xe12e1342,
0x8cbccc09,
0xafebff0c,
0xdbe6fecf,
0x89705f41,
0xabcc7712,
0xd6bf94d6,
0x8637bd06,
0xa7c5ac47,
0xd1b71759,
0x83126e98,
0xa3d70a3d,
0xcccccccd,
0x80000000,
0xa0000000,
0xc8000000,
0xfa000000,
0x9c400000,
0xc3500000,
0xf4240000,
0x98968000,
0xbebc2000,
0xee6b2800,
0x9502f900,
0xba43b740,
0xe8d4a510,
0x9184e72a,
0xb5e620f4,
0xe35fa932,
0x8e1bc9bf,
0xb1a2bc2f,
0xde0b6b3a,
0x8ac72305,
0xad78ebc6,
0xd8d726b7,
0x87867832,
0xa968163f,
0xd3c21bcf,
0x84595161,
0xa56fa5ba,
0xcecb8f28,
0x813f3979,
0xa18f07d7,
0xc9f2c9cd,
0xfc6f7c40,
0x9dc5ada8,
0xc5371912,
0xf684df57,
0x9a130b96,
0xc097ce7c,
0xf0bdc21b,
0x96769951,
0xbc143fa5,
0xeb194f8e,
0x92efd1b9,
0xb7abc627,
0xe596b7b1,
0x8f7e32ce,
0xb35dbf82,
0xe0352f63,
0x8c213d9e,
0xaf298d05,
0xdaf3f046,
0x88d8762c
};
/** \brief Pre-calculated values of binary exponent of powers of 10 in the
* range of [-50, 50].
*/
static constexpr int e[] =
{
-198,
-194,
-191,
-188,
-184,
-181,
-178,
-174,
-171,
-168,
-164,
-161,
-158,
-154,
-151,
-148,
-144,
-141,
-138,
-134,
-131,
-128,
-125,
-121,
-118,
-115,
-111,
-108,
-105,
-101,
-98,
-95,
-91,
-88,
-85,
-81,
-78,
-75,
-71,
-68,
-65,
-61,
-58,
-55,
-51,
-48,
-45,
-41,
-38,
-35,
-31,
-28,
-25,
-22,
-18,
-15,
-12,
-8,
-5,
-2,
2,
5,
8,
12,
15,
18,
22,
25,
28,
32,
35,
38,
42,
45,
48,
52,
55,
58,
62,
65,
68,
71,
75,
78,
81,
85,
88,
91,
95,
98,
101,
105,
108,
111,
115,
118,
121,
125,
128,
131,
135
};
/** \brief Offsef of the values for zero power in the arrays.
*/
static constexpr std::size_t pow_0_offset = 50;
/** \brief Boundaries of possible powers of ten for the type.
*/
static constexpr std::pair<int, int> boundaries = { -50, 50 };
};
constexpr decltype(powers_ten<float>::f) powers_ten<float>::f;
constexpr decltype(powers_ten<float>::e) powers_ten<float>::e;
constexpr std::size_t powers_ten<float>::pow_0_offset;
constexpr std::pair<int, int> powers_ten<float>::boundaries;
}
#endif // FLOAXIE_POWERS_TEN_SINGLE_H

View File

@@ -1,221 +0,0 @@
/*
* Copyright 2015-2022 Alexey Chernov <4ernov@gmail.com>
*
* 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.
*
* prettify_string() and fill_exponent() functions use code taken from
* Florian Loitsch's original Grisu algorithms implementation
* (http://florian.loitsch.com/publications/bench.tar.gz)
* and "Printing Floating-Point Numbers Quickly and Accurately with
* Integers" paper
* (http://florian.loitsch.com/publications/dtoa-pldi2010.pdf)
*/
#ifndef FLOAXIE_PRETTIFY_H
#define FLOAXIE_PRETTIFY_H
#include <cstring>
#include <cassert>
#include <cstdlib>
#include <algorithm>
#include <external/floaxie/floaxie/static_pow.h>
#include <external/floaxie/floaxie/print.h>
#include <external/floaxie/floaxie/memwrap.h>
namespace floaxie
{
/** \brief Format enumeration.
*
*/
enum format {
/** \brief Decimal format. */
decimal,
/** \brief Decimal exponent (a.k.a. *scientific*) format. */
scientific
};
/** \brief LUT of two-digit decimal values to speed up their printing. */
constexpr const char digits_lut[200] = {
'0', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '5', '0', '6', '0', '7', '0', '8', '0', '9',
'1', '0', '1', '1', '1', '2', '1', '3', '1', '4', '1', '5', '1', '6', '1', '7', '1', '8', '1', '9',
'2', '0', '2', '1', '2', '2', '2', '3', '2', '4', '2', '5', '2', '6', '2', '7', '2', '8', '2', '9',
'3', '0', '3', '1', '3', '2', '3', '3', '3', '4', '3', '5', '3', '6', '3', '7', '3', '8', '3', '9',
'4', '0', '4', '1', '4', '2', '4', '3', '4', '4', '4', '5', '4', '6', '4', '7', '4', '8', '4', '9',
'5', '0', '5', '1', '5', '2', '5', '3', '5', '4', '5', '5', '5', '6', '5', '7', '5', '8', '5', '9',
'6', '0', '6', '1', '6', '2', '6', '3', '6', '4', '6', '5', '6', '6', '6', '7', '6', '8', '6', '9',
'7', '0', '7', '1', '7', '2', '7', '3', '7', '4', '7', '5', '7', '6', '7', '7', '7', '8', '7', '9',
'8', '0', '8', '1', '8', '2', '8', '3', '8', '4', '8', '5', '8', '6', '8', '7', '8', '8', '8', '9',
'9', '0', '9', '1', '9', '2', '9', '3', '9', '4', '9', '5', '9', '6', '9', '7', '9', '8', '9', '9'
};
/** \brief Detects the more appropriate format to print based on
* \p **threshold** value.
* \tparam threshold the maximum number of digits in the string
* representation, when decimal format can be used (otherwise decimal
* exponent or *scientific* format is used).
*
* \return value of the chosen format.
* \see `format`
*/
template<std::size_t threshold> inline format choose_format(const std::size_t field_width) noexcept
{
static_assert(threshold > static_pow<10, 1>(), "Only 10 ⩽ |threshold| ⩽ 100 is supported");
return field_width > threshold ? format::scientific : format::decimal;
}
/** \brief Prints decimal exponent value.
*
* \tparam CharType character type (typically `char` or `wchar_t`) of the
* output buffer \p **buffer**.
*
* \param K decimal exponent value.
* \param buffer character buffer to print to.
*
* \return number of characters written to the buffer.
*/
template<typename CharType> inline std::size_t fill_exponent(unsigned int K, CharType* buffer) noexcept
{
const unsigned char hundreds = static_cast<unsigned char>(K / 100);
K %= 100;
buffer[0] = '0' + hundreds;
buffer += (hundreds > 0);
const char* d = digits_lut + K * 2;
buffer[0] = d[0];
buffer[1] = d[1];
buffer[2] = '\0';
return 2 + (hundreds > 0);
}
/** \brief Prints exponent (*scientific*) part of value representation in
* decimal exponent format.
*
* \tparam CharType character type (typically `char` or `wchar_t`) of the
* output buffer \p **buffer**.
*
* \param buffer character buffer with properly printed mantissa.
* \param len output parameter to return the length of printed
* representation.
* \param dot_pos number of character, where dot position should be placed.
*
* \return number of characters written to the buffer.
*
* \see `print_decimal()`
*/
template<typename CharType> inline std::size_t print_scientific(CharType* buffer, const unsigned int len, const int dot_pos) noexcept
{
const int K = dot_pos - 1;
if (len > 1)
{
/* leave the first digit. then add a '.' and at the end 'e...' */
wrap::memmove(buffer + 2, buffer + 1, len - 1);
buffer[1] = '.';
buffer += len;
}
/* add 'e...' */
buffer[1] = 'e';
buffer[2] = '-';
buffer += K < 0;
return len + /*dot*/(len > 1) + /*'e'*/1 + /*exp sign*/(K < 0) + fill_exponent(std::abs(K), buffer + 2);
}
/** \brief Formats decimal mantissa part of value representation.
*
* Tides up the printed digits in \p **buffer**, adding leading zeros,
* placing the decimal point into the proper place etc.
*
* \tparam CharType character type (typically `char` or `wchar_t`) of the
* output buffer \p **buffer**.
*
* \param buffer character buffer with printed digits.
* \param len length of current representation in \p **buffer**.
* \param k decimal exponent of the value.
*
* \return number of characters written to the buffer.
*/
template<typename CharType> inline std::size_t print_decimal(CharType* buffer, const unsigned int len, const int k) noexcept
{
const int dot_pos = static_cast<int>(len) + k;
const unsigned int actual_dot_pos = dot_pos > 0 ? uint32_t(dot_pos) : 1;
const unsigned int left_offset = dot_pos > 0 ? 0 : 2 - uint32_t(dot_pos);
const unsigned int right_offset = positive_part(k);
const unsigned int left_shift_src = positive_part(dot_pos);
const unsigned int left_shift_dest = dot_pos > 0 ? left_shift_src + (k < 0) : left_offset;
const unsigned int left_shift_len = positive_part(static_cast<int>(len) - static_cast<int>(left_shift_src));
const unsigned int term_pos = len + right_offset + (left_shift_dest - left_shift_src);
wrap::memmove(buffer + left_shift_dest, buffer + left_shift_src, left_shift_len);
wrap::memset(buffer, CharType('0'), left_offset);
wrap::memset(buffer + len, CharType('0'), right_offset);
buffer[actual_dot_pos] = '.';
buffer[term_pos] = '\0';
return term_pos;
}
/** \brief Makes final format corrections to have the string representation
* be properly and pretty printed (with decimal point in place, exponent
* part, where appropriate etc.).
*
* \tparam decimal_scientific_threshold the maximum number of digits in the
* string representation, when decimal format can be used (otherwise
* decimal exponent or *scientific* format is used).
* \tparam CharType character type (typically `char` or `wchar_t`) of the
* output buffer \p **buffer**.
*
* \param buffer character buffer with printed digits.
* \param len length of current representation in \p **buffer**.
* \param k decimal exponent of the value.
*
* \return number of characters written to the buffer.
*
* \see `print_decimal()`
* \see `print_scientific()`
*/
template<std::size_t decimal_scientific_threshold, typename CharType>
inline std::size_t prettify(CharType* buffer, const unsigned int len, const int k) noexcept
{
/* v = buffer * 10 ^ k
dot_pos is such that 10 ^ (dot_pos - 1) <= v < 10 ^ dot_pos
this way dot_pos gives the position of the comma.
*/
const int dot_pos = static_cast<int>(len) + k;
// is always positive, since dot_pos is negative only when k is negative
const std::size_t field_width = size_t((std::max)(dot_pos, -k));
switch (choose_format<decimal_scientific_threshold>(field_width))
{
case format::decimal:
return print_decimal(buffer, len, k);
case format::scientific:
return print_scientific(buffer, len, dot_pos);
}
// never reach here
return 0;
}
}
#endif // FLOAXIE_PRETTIFY_H

View File

@@ -1,63 +0,0 @@
/*
* Copyright 2015, 2016 Alexey Chernov <4ernov@gmail.com>
*
* 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.
*
* Several utility functions as printing binary representation of floating
* point values and other stuff.
*
*/
#ifndef FLOAXIE_PRINT_H
#define FLOAXIE_PRINT_H
#include <bitset>
#include <string>
#include <cstdint>
#include <external/floaxie/floaxie/type_punning_cast.h>
#include <external/floaxie/floaxie/bit_ops.h>
namespace floaxie
{
/** \brief Prints `double` value in binary format, splitting sign, exponent
* and mantissa parts with spaces.
*
* Useful for debugging purposes.
*/
std::string inline print_binary(double f)
{
auto s(std::bitset<64>(type_punning_cast<std::uint64_t>(f)).to_string());
s.insert(1, 1, ' ');
s.insert(13, 1, ' ');
return s;
}
/** \brief Print arbitrary numeric value in binary format. */
template<typename NumericType> inline std::string print_binary(NumericType v)
{
return std::bitset<bit_size<NumericType>()>(v).to_string();
}
/** \brief Print arbitrary numeric value as if it were `double`. */
template<typename NumericType> inline std::string print_double_presentation(NumericType v)
{
auto s(std::bitset<64>(v).to_string());
s.insert(1, 1, ' ');
s.insert(54, 1, ' ');
s.insert(56, 1, ' ');
return s;
}
}
#endif // FLOAXIE_PRINT_H

View File

@@ -1,164 +0,0 @@
/*
* Copyright 2015-2019 Alexey Chernov <4ernov@gmail.com>
*
* 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 FLOAXIE_STATIC_POW_H
#define FLOAXIE_STATIC_POW_H
#include <array>
#include <utility>
namespace floaxie
{
/** \brief Helper class template to calculate integer positive power in
* compile time.
*
* Unspecialized template contains intermediate steps implementation.
*
* \tparam base base of the power.
* \tparam pow exponent to power base.
*/
template<unsigned int base, unsigned int pow> struct static_pow_helper
{
/** \brief Result of power calculation (in compile time).
*
* Is calculated recursively in intermediate steps.
*/
static const unsigned int value = base * static_pow_helper<base, pow - 1>::value;
};
/** \brief Terminal specialization of `static_pow_helper` template. */
template<unsigned int base> struct static_pow_helper<base, 0>
{
/** \brief Any value in zero power is `1`. */
static const unsigned int value = 1;
};
/** \brief Handy wrapper function to calculate positive power in compile
* time.
*
* \tparam base base of the power.
* \tparam pow exponent to power base.
*
* \return Result of power calculation (in compile time).
*/
template<unsigned int base, unsigned int pow> constexpr unsigned long static_pow()
{
static_assert(base > 0, "Base should be positive");
return static_pow_helper<base, pow>::value;
}
/** \brief Helper structure template to append value to
* `std::integer_sequence`.
*
* \tparam T type of elements of `std::integer_sequence`.
* \tparam Add element to add to the specified sequence.
* \tparam Seq original `std::integer_sequence` expressed by parameter pack
* of its elements in template parameters.
*/
template<typename T, T Add, typename Seq> struct concat_sequence;
/** \brief Implements the concatenation itself.
*
* Steals parameters (read: contents) of passed `std::integer_sequence`-like
* type and creates another `std::integer_sequence`-like type with the
* specified value appended at the end.
*/
template<typename T, T Add, T... Seq> struct concat_sequence<T, Add, std::integer_sequence<T, Seq...>>
{
/** \brief New `std::integer_sequence`-like type with the specified
* element added to the end.
*/
using type = std::integer_sequence<T, Seq..., Add>;
};
/** \brief Helper structure template to convert `std::integer_sequence`
* to `std::array`.
*
* \tparam Seq sequence to convert.
*/
template<typename Seq> struct make_integer_array;
/** \brief Main specialization of `make_integer_array` with the specified
* input `std::integer_sequence`.
*
* \tparam T type of elements of `std::integer_sequence`.
* \tparam Ints elements of the `std::integer_sequence` to convert.
*/
template<typename T, T... Ints> struct make_integer_array<std::integer_sequence<T, Ints...>>
{
/** \brief Type of the resulting `std::array` (specialized with
* element type and length). */
using type = std::array<T, sizeof...(Ints)>;
/** \brief Instance of the resulting `std::array` filled in with
* elements from the specified `std::integer_sequence` in compile time.
*/
static constexpr type value = type{{Ints...}};
};
/** \brief Creates `std::integer_sequence` with sequence of powers of
* \p **base** up to the exponent value, defined by \p **current_pow**.
*
* For example, if \p base is `10` and \p current_pow is `3`, the result
* will contain values `1000`, `100`, `10`, `1`.
*
* \tparam T type of elements.
* \tparam base base of powers to calculate.
* \tparam current_pow the maximum exponent value to calculate power for.
*/
template<typename T, T base, std::size_t current_pow> struct pow_sequencer
{
/** \brief Value of power on the current step (in recursion). */
static const T value = pow_sequencer<T, base, current_pow - 1>::value * base;
/** \brief `std::integer_sequence`-like type containing all the
* calculated powers at the moment.
*/
typedef typename concat_sequence<T, value, typename pow_sequencer<T, base, current_pow - 1>::sequence_type>::type sequence_type;
};
/** \brief Terminal step specialization for `pow_sequencer` template. */
template<typename T, T base> struct pow_sequencer<T, base, 0>
{
/** \brief Zero power of base. */
static constexpr T value = 1;
/** \brief `std::integer_sequence` with zero power only yet. */
typedef std::integer_sequence<T, 1> sequence_type;
};
/** \brief Handy wrapper function to calculate a sequence of powers.
*
* \tparam T type of elements.
* \tparam base base for powers to calculate.
* \tparam max_pow maximum exponent, up to which the calculation will be
* performed.
*
* \return `std::array` filled with powers of the specified arguments in
* reverse order. I.e. for \p **T** of `unsigned int`, \p **base** of 10
* and \p **max_pow** 3 this would be:
* `std::array<unsigned int>({{1000, 100, 10, 1}})`.
*/
template<typename T, T base, std::size_t max_pow> constexpr T seq_pow(std::size_t pow)
{
typedef make_integer_array<typename pow_sequencer<T, base, max_pow>::sequence_type> maker;
constexpr typename maker::type arr(maker::value);
return arr[pow];
}
}
#endif // FLOAXIE_STATIC_POW_H

View File

@@ -1,45 +0,0 @@
/*
* Copyright 2015, 2016 Alexey Chernov <4ernov@gmail.com>
*
* 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.
*
* type_punning_cast is copy-paste of pseudo_cast as posted by plasmacel
* at StackOverflow
* (http://stackoverflow.com/questions/17789928/whats-a-proper-way-of-type-punning-a-float-to-an-int-and-vice-versa)
*
*/
#ifndef FLOAXIE_TYPE_PUNNING_CAST
#define FLOAXIE_TYPE_PUNNING_CAST
#include <cstring>
namespace floaxie
{
/** \brief Correct type-punning cast implementation to avoid any possible
* undefined behaviour.
*
* \see https://en.wikipedia.org/wiki/Type_punning
*/
template<typename T, typename U> inline T type_punning_cast(const U& x)
{
static_assert(sizeof(T) == sizeof(U),
"type_punning_cast can't handle types with different size");
T to;
std::memcpy(&to, &x, sizeof(T));
return to;
}
}
#endif // FLOAXIE_TYPE_PUNNING_CAST

View File

@@ -3,11 +3,8 @@
#include "str-util.hh"
#include <string>
// dtoa_milo does not work well for float types
// (e.g. it prints float 0.01 as 0.009999999997),
// so use floaxie for float types
// TODO: Use floaxie also for double?
#include "external/dtoa_milo.h"
// NOTE: dtos() from str-util.hh uses dragonbox algorithm for
// shortest float-to-string conversion
#ifdef __clang__
#pragma clang diagnostic push
@@ -29,24 +26,17 @@ namespace json {
namespace detail {
#if 0
inline std::string dtos(const double v) {
char buf[384];
*dtoa_milo(v, buf) = '\0';
return std::string(buf);
}
#endif
// NOTE: Use tinyusdz::dtos() from str-util.hh instead
#if 0
static bool WriteInt(const int value, std::string *out_json) {
if (!out_json) return false;
(*out_json) = detail::dtos(double(value));
(*out_json) = tinyusdz::dtos(double(value));
return true;
}
static bool WriteString(const std::string &str, std::string *out_json) {

View File

@@ -30,28 +30,13 @@
#include "external/jeaiii_to_text.h"
#endif
// dtoa_milo does not work well for float types
// (e.g. it prints float 0.01 as 0.009999999997),
// so use floaxie for float types
// TODO: Use floaxie also for double?
#include "external/dtoa_milo.h"
// NOTE: Using dragonbox-based dtos() from str-util.hh for float-to-string
// conversion - it produces shortest representation for 100% of values
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Weverything"
#endif
// #include "external/floaxie/floaxie/ftoa.h"
#ifdef __clang__
#pragma clang diagnostic pop
#endif
// TODO:
// - [ ] Print properties based on lexcographically(USDA)
// - [ ] Refactor variantSet stmt print.
// - [ ] Implement our own dtos
// stringify.
namespace tinyusdz {
@@ -64,8 +49,6 @@ void itoa(uint64_t n, char *b) { *jeaiii::to_text_from_integer(b, n) = '\0'; }
void itoa(int64_t n, char *b) { *jeaiii::to_text_from_integer(b, n) = '\0'; }
#endif
// NOTE: dtos() is now provided by str-util.hh using dragonbox algorithm
// Path quote
std::string pquote(const Path &p) {
return wquote(p.full_path_name(), "<", ">");

View File

@@ -34,6 +34,7 @@
#include <cmath>
#include <cstring>
#include "value-types.hh"
#include "str-util.hh" // For dragonbox-based dtos()
#endif
namespace tinyusdz {
@@ -486,7 +487,7 @@ static std::string AttributeToJSON(const Attribute* attr) {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (i > 0 || j > 0) oss << ",";
oss << v.value().m[i][j];
oss << dtos(v.value().m[i][j]);
}
}
oss << "]";
@@ -501,7 +502,7 @@ static std::string AttributeToJSON(const Attribute* attr) {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (i > 0 || j > 0) oss << ",";
oss << v.value().m[i][j];
oss << dtos(v.value().m[i][j]);
}
}
oss << "]";
@@ -514,7 +515,7 @@ static std::string AttributeToJSON(const Attribute* attr) {
else if (tid == value::TypeTraits<value::half2>::type_id()) {
auto v = attr->get_value<value::half2>();
if (v) {
oss << "[" << value::half_to_float(v.value()[0]) << "," << value::half_to_float(v.value()[1]) << "]";
oss << "[" << dtos(value::half_to_float(v.value()[0])) << "," << dtos(value::half_to_float(v.value()[1])) << "]";
} else {
oss << "null";
}
@@ -523,7 +524,7 @@ static std::string AttributeToJSON(const Attribute* attr) {
else if (tid == value::TypeTraits<value::half3>::type_id()) {
auto v = attr->get_value<value::half3>();
if (v) {
oss << "[" << value::half_to_float(v.value()[0]) << "," << value::half_to_float(v.value()[1]) << "," << value::half_to_float(v.value()[2]) << "]";
oss << "[" << dtos(value::half_to_float(v.value()[0])) << "," << dtos(value::half_to_float(v.value()[1])) << "," << dtos(value::half_to_float(v.value()[2])) << "]";
} else {
oss << "null";
}
@@ -532,8 +533,8 @@ static std::string AttributeToJSON(const Attribute* attr) {
else if (tid == value::TypeTraits<value::half4>::type_id()) {
auto v = attr->get_value<value::half4>();
if (v) {
oss << "[" << value::half_to_float(v.value()[0]) << "," << value::half_to_float(v.value()[1]) << ","
<< value::half_to_float(v.value()[2]) << "," << value::half_to_float(v.value()[3]) << "]";
oss << "[" << dtos(value::half_to_float(v.value()[0])) << "," << dtos(value::half_to_float(v.value()[1])) << ","
<< dtos(value::half_to_float(v.value()[2])) << "," << dtos(value::half_to_float(v.value()[3])) << "]";
} else {
oss << "null";
}
@@ -556,7 +557,7 @@ static std::string AttributeToJSON(const Attribute* attr) {
oss << "[";
for (size_t i = 0; i < v.value().size(); i++) {
if (i > 0) oss << ",";
oss << v.value()[i];
oss << dtos(v.value()[i]);
}
oss << "]";
} else {
@@ -583,7 +584,7 @@ static std::string AttributeToJSON(const Attribute* attr) {
for (size_t i = 0; i < v.value().size(); i++) {
if (i > 0) oss << ",";
// Flatten each float3 into the main array
oss << v.value()[i][0] << "," << v.value()[i][1] << "," << v.value()[i][2];
oss << dtos(v.value()[i][0]) << "," << dtos(v.value()[i][1]) << "," << dtos(v.value()[i][2]);
}
oss << "]";
} else {
@@ -598,7 +599,7 @@ static std::string AttributeToJSON(const Attribute* attr) {
oss << "[";
for (size_t i = 0; i < v.value().size(); i++) {
if (i > 0) oss << ",";
oss << v.value()[i];
oss << dtos(v.value()[i]);
}
oss << "]";
} else {
@@ -612,7 +613,7 @@ static std::string AttributeToJSON(const Attribute* attr) {
for (size_t i = 0; i < v.value().size(); i++) {
if (i > 0) oss << ",";
// Flatten each double2 into the main array
oss << v.value()[i][0] << "," << v.value()[i][1];
oss << dtos(v.value()[i][0]) << "," << dtos(v.value()[i][1]);
}
oss << "]";
} else {
@@ -626,7 +627,7 @@ static std::string AttributeToJSON(const Attribute* attr) {
for (size_t i = 0; i < v.value().size(); i++) {
if (i > 0) oss << ",";
// Flatten each double3 into the main array
oss << v.value()[i][0] << "," << v.value()[i][1] << "," << v.value()[i][2];
oss << dtos(v.value()[i][0]) << "," << dtos(v.value()[i][1]) << "," << dtos(v.value()[i][2]);
}
oss << "]";
} else {
@@ -640,7 +641,7 @@ static std::string AttributeToJSON(const Attribute* attr) {
for (size_t i = 0; i < v.value().size(); i++) {
if (i > 0) oss << ",";
// Flatten each double4 into the main array
oss << v.value()[i][0] << "," << v.value()[i][1] << "," << v.value()[i][2] << "," << v.value()[i][3];
oss << dtos(v.value()[i][0]) << "," << dtos(v.value()[i][1]) << "," << dtos(v.value()[i][2]) << "," << dtos(v.value()[i][3]);
}
oss << "]";
} else {
@@ -703,7 +704,7 @@ static std::string AttributeToJSON(const Attribute* attr) {
for (int row = 0; row < 3; row++) {
for (int col = 0; col < 3; col++) {
if (row > 0 || col > 0) oss << ",";
oss << v.value()[i].m[row][col];
oss << dtos(v.value()[i].m[row][col]);
}
}
}
@@ -722,7 +723,7 @@ static std::string AttributeToJSON(const Attribute* attr) {
for (int row = 0; row < 4; row++) {
for (int col = 0; col < 4; col++) {
if (row > 0 || col > 0) oss << ",";
oss << v.value()[i].m[row][col];
oss << dtos(v.value()[i].m[row][col]);
}
}
}
@@ -739,7 +740,7 @@ static std::string AttributeToJSON(const Attribute* attr) {
oss << "[";
for (size_t i = 0; i < v.value().size(); ++i) {
if (i > 0) oss << ",";
oss << value::half_to_float(v.value()[i]);
oss << dtos(value::half_to_float(v.value()[i]));
}
oss << "]";
} else {
@@ -753,7 +754,7 @@ static std::string AttributeToJSON(const Attribute* attr) {
oss << "[";
for (size_t i = 0; i < v.value().size(); ++i) {
if (i > 0) oss << ",";
oss << value::half_to_float(v.value()[i][0]) << "," << value::half_to_float(v.value()[i][1]);
oss << dtos(value::half_to_float(v.value()[i][0])) << "," << dtos(value::half_to_float(v.value()[i][1]));
}
oss << "]";
} else {
@@ -767,8 +768,8 @@ static std::string AttributeToJSON(const Attribute* attr) {
oss << "[";
for (size_t i = 0; i < v.value().size(); ++i) {
if (i > 0) oss << ",";
oss << value::half_to_float(v.value()[i][0]) << "," << value::half_to_float(v.value()[i][1]) << ","
<< value::half_to_float(v.value()[i][2]);
oss << dtos(value::half_to_float(v.value()[i][0])) << "," << dtos(value::half_to_float(v.value()[i][1])) << ","
<< dtos(value::half_to_float(v.value()[i][2]));
}
oss << "]";
} else {
@@ -782,8 +783,8 @@ static std::string AttributeToJSON(const Attribute* attr) {
oss << "[";
for (size_t i = 0; i < v.value().size(); ++i) {
if (i > 0) oss << ",";
oss << value::half_to_float(v.value()[i][0]) << "," << value::half_to_float(v.value()[i][1]) << ","
<< value::half_to_float(v.value()[i][2]) << "," << value::half_to_float(v.value()[i][3]);
oss << dtos(value::half_to_float(v.value()[i][0])) << "," << dtos(value::half_to_float(v.value()[i][1])) << ","
<< dtos(value::half_to_float(v.value()[i][2])) << "," << dtos(value::half_to_float(v.value()[i][3]));
}
oss << "]";
} else {

View File

@@ -12,6 +12,7 @@
#include <sstream>
#include <iomanip>
#include <cmath>
#include "str-util.hh" // For dragonbox-based dtos()
namespace tinyusdz {
namespace tydra {
@@ -634,7 +635,7 @@ bool ThreeJSMaterialExporter::ExportMaterialX(const RenderMaterial& material,
auto export_float = [&ss](const std::string& name, const ShaderParam<float>& param) {
if (!param.is_texture()) {
ss << " <input name=\"" << name << "\" type=\"float\" value=\""
<< param.value << "\" />\n";
<< dtos(param.value) << "\" />\n";
} else {
ss << " <input name=\"" << name << "\" type=\"float\" nodename=\""
<< name << "_texture\" />\n";
@@ -645,9 +646,9 @@ bool ThreeJSMaterialExporter::ExportMaterialX(const RenderMaterial& material,
auto export_color3 = [&ss](const std::string& name, const ShaderParam<vec3>& param) {
if (!param.is_texture()) {
ss << " <input name=\"" << name << "\" type=\"color3\" value=\""
<< param.value[0] << ", "
<< param.value[1] << ", "
<< param.value[2] << "\" />\n";
<< dtos(param.value[0]) << ", "
<< dtos(param.value[1]) << ", "
<< dtos(param.value[2]) << "\" />\n";
} else {
ss << " <input name=\"" << name << "\" type=\"color3\" nodename=\""
<< name << "_texture\" />\n";
@@ -658,9 +659,9 @@ bool ThreeJSMaterialExporter::ExportMaterialX(const RenderMaterial& material,
auto export_vector3 = [&ss](const std::string& name, const ShaderParam<vec3>& param) {
if (!param.is_texture()) {
ss << " <input name=\"" << name << "\" type=\"vector3\" value=\""
<< param.value[0] << ", "
<< param.value[1] << ", "
<< param.value[2] << "\" />\n";
<< dtos(param.value[0]) << ", "
<< dtos(param.value[1]) << ", "
<< dtos(param.value[2]) << "\" />\n";
} else {
ss << " <input name=\"" << name << "\" type=\"vector3\" nodename=\""
<< name << "_texture\" />\n";
@@ -802,33 +803,33 @@ bool ThreeJSMaterialExporter::ExportMaterialX(const RenderMaterial& material,
// Map UsdPreviewSurface to standard_surface parameters
ss << " <input name=\"base_color\" type=\"color3\" value=\""
<< shader.diffuseColor.value[0] << ", "
<< shader.diffuseColor.value[1] << ", "
<< shader.diffuseColor.value[2] << "\" />\n";
<< dtos(shader.diffuseColor.value[0]) << ", "
<< dtos(shader.diffuseColor.value[1]) << ", "
<< dtos(shader.diffuseColor.value[2]) << "\" />\n";
ss << " <input name=\"metalness\" type=\"float\" value=\""
<< shader.metallic.value << "\" />\n";
<< dtos(shader.metallic.value) << "\" />\n";
ss << " <input name=\"specular_roughness\" type=\"float\" value=\""
<< shader.roughness.value << "\" />\n";
<< dtos(shader.roughness.value) << "\" />\n";
ss << " <input name=\"emission_color\" type=\"color3\" value=\""
<< shader.emissiveColor.value[0] << ", "
<< shader.emissiveColor.value[1] << ", "
<< shader.emissiveColor.value[2] << "\" />\n";
<< dtos(shader.emissiveColor.value[0]) << ", "
<< dtos(shader.emissiveColor.value[1]) << ", "
<< dtos(shader.emissiveColor.value[2]) << "\" />\n";
ss << " <input name=\"opacity\" type=\"float\" value=\""
<< shader.opacity.value << "\" />\n";
<< dtos(shader.opacity.value) << "\" />\n";
if (shader.clearcoat.value > 0.0f) {
ss << " <input name=\"coat\" type=\"float\" value=\""
<< shader.clearcoat.value << "\" />\n";
<< dtos(shader.clearcoat.value) << "\" />\n";
ss << " <input name=\"coat_roughness\" type=\"float\" value=\""
<< shader.clearcoatRoughness.value << "\" />\n";
<< dtos(shader.clearcoatRoughness.value) << "\" />\n";
}
ss << " <input name=\"specular_IOR\" type=\"float\" value=\""
<< shader.ior.value << "\" />\n";
<< dtos(shader.ior.value) << "\" />\n";
ss << " </standard_surface>\n\n";

View File

@@ -13,38 +13,15 @@
#include "ascii-parser.hh" // To parse color3f value
#include "common-macros.inc"
#include "external/dtoa_milo.h"
#include "io-util.hh"
#include "pprinter.hh"
#include "str-util.hh" // For dragonbox-based dtos()
#include "tiny-format.hh"
#include "value-pprint.hh"
inline std::string dtos(const double v) {
char buf[384];
*dtoa_milo(v, buf) = '\0';
return std::string(buf);
}
// Helper function to format float values cleanly for MaterialX XML
inline std::string float_to_xml_string(float val) {
std::ostringstream oss;
oss.precision(6); // 6 significant digits
oss << val;
std::string result = oss.str();
// Remove trailing zeros after decimal point
if (result.find('.') != std::string::npos) {
size_t end = result.find_last_not_of('0');
if (end != std::string::npos && result[end] != '.') {
result = result.substr(0, end + 1);
} else if (end != std::string::npos && result[end] == '.') {
result = result.substr(0, end);
}
}
return result;
}
// Use dragonbox-based dtos from str-util.hh for shortest representation
// No need for local dtos() or float_to_xml_string() - dtos() already
// produces the shortest round-trip-correct representation without trailing zeros
#define PushWarn(msg) \
do { \
@@ -413,7 +390,7 @@ static bool SerializeNodeGraphs(const std::map<std::string, PrimSpec> &nodegraph
template <>
std::string to_xml_string(const float &val) {
return float_to_xml_string(val);
return dtos(val);
}
template <>
@@ -428,14 +405,12 @@ std::string to_xml_string(const bool &val) {
template <>
std::string to_xml_string(const value::color3f &val) {
return float_to_xml_string(val.r) + ", " + float_to_xml_string(val.g) + ", " +
float_to_xml_string(val.b);
return dtos(val.r) + ", " + dtos(val.g) + ", " + dtos(val.b);
}
template <>
std::string to_xml_string(const value::normal3f &val) {
return float_to_xml_string(val.x) + ", " + float_to_xml_string(val.y) + ", " +
float_to_xml_string(val.z);
return dtos(val.x) + ", " + dtos(val.y) + ", " + dtos(val.z);
}
template <typename T>

View File

@@ -24,11 +24,8 @@
#include "external/jeaiii_to_text.h"
#endif
// dtoa_milo does not work well for float types
// (e.g. it prints float 0.01 as 0.009999999997),
// so use floaxie for float types
// TODO: Use floaxie also for double?
#include "external/dtoa_milo.h"
// NOTE: Using dragonbox-based dtos() from str-util.hh for all float/double
// conversions - it produces shortest representation for 100% of values.
#ifdef __clang__
#pragma clang diagnostic push
@@ -52,7 +49,8 @@ inline void append_float_to_stream(std::ostream &os, float v) {
namespace std {
std::ostream &operator<<(std::ostream &os, const tinyusdz::value::half &v) {
os << tinyusdz::value::half_to_float(v);
// Use dtos for consistent shortest representation
os << tinyusdz::dtos(tinyusdz::value::half_to_float(v));
return os;
}
@@ -546,21 +544,12 @@ std::ostream &operator<<(std::ostream &ofs,
template <>
std::ostream &operator<<(std::ostream &ofs, const std::vector<double> &v) {
// Not sure what is the HARD-LIMT buffer length for dtoa_milo,
// but according to std::numeric_limits<double>::digits10(=15),
// 32 should be sufficient, but allocate 384 just in case
char buf[384];
// TODO: multi-threading for further performance gain?
ofs << "[";
for (size_t i = 0; i < v.size(); i++) {
if (i > 0) {
ofs << ", ";
}
char *e = dtoa_milo(v[i], buf);
(*e) = '\0';
ofs << std::string(buf);
ofs << tinyusdz::dtos(v[i]); // use dragonbox for shortest representation
}
ofs << "]";