mirror of
https://github.com/lighttransport/tinyusdz.git
synced 2026-01-18 01:11:17 +01:00
add dtoa
This commit is contained in:
@@ -1,23 +1,21 @@
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "dragonbox_to_chars.h"
|
||||
#include "dtoa_milo.h"
|
||||
|
||||
std::vector<float> gen_floats(size_t n) {
|
||||
|
||||
std::vector<float> dst;
|
||||
dst.resize(n);
|
||||
|
||||
std::random_device rd;
|
||||
|
||||
std::mt19937 engine(rd());
|
||||
std::uniform_real_distribution<> dist(-0.001, 0.01);
|
||||
std::uniform_real_distribution<> dist(-0.1, 0.1);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
double f = dist(engine);
|
||||
@@ -27,14 +25,13 @@ std::vector<float> gen_floats(size_t n) {
|
||||
return dst;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// based on fmtlib
|
||||
//
|
||||
//
|
||||
|
||||
// TOOD: Use builtin_clz insturction
|
||||
// TOOD: Use builtin_clz insturction
|
||||
// T = uint32 or uint64
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
inline int count_digits(T n) {
|
||||
int count = 1;
|
||||
for (;;) {
|
||||
@@ -50,107 +47,118 @@ inline int count_digits(T n) {
|
||||
}
|
||||
}
|
||||
|
||||
// Converts value in the range [0, 100) to a string.
|
||||
// GCC generates slightly better code when value is pointer-size.
|
||||
inline auto digits2(size_t value) -> const char* {
|
||||
// Align data since unaligned access may be slower when crossing a
|
||||
// hardware-specific boundary.
|
||||
alignas(2) static const char data[] =
|
||||
"0001020304050607080910111213141516171819"
|
||||
"2021222324252627282930313233343536373839"
|
||||
"4041424344454647484950515253545556575859"
|
||||
"6061626364656667686970717273747576777879"
|
||||
"8081828384858687888990919293949596979899";
|
||||
return &data[value * 2];
|
||||
// Converts value in the range [0, 100) to a string.
|
||||
// GCC generates slightly better code when value is pointer-size.
|
||||
inline auto digits2(size_t value) -> const char* {
|
||||
// Align data since unaligned access may be slower when crossing a
|
||||
// hardware-specific boundary.
|
||||
alignas(2) static const char data[] =
|
||||
"0001020304050607080910111213141516171819"
|
||||
"2021222324252627282930313233343536373839"
|
||||
"4041424344454647484950515253545556575859"
|
||||
"6061626364656667686970717273747576777879"
|
||||
"8081828384858687888990919293949596979899";
|
||||
return &data[value * 2];
|
||||
}
|
||||
|
||||
// Writes a two-digit value to out.
|
||||
inline void write2digits(char* out, size_t value) {
|
||||
// if (!is_constant_evaluated() && std::is_same<Char, char>::value &&
|
||||
// !FMT_OPTIMIZE_SIZE) {
|
||||
// memcpy(out, digits2(value), 2);
|
||||
// return;
|
||||
// }
|
||||
*out++ = static_cast<char>('0' + value / 10);
|
||||
*out = static_cast<char>('0' + value % 10);
|
||||
}
|
||||
|
||||
// Writes the exponent exp in the form "[+-]d{2,3}" to buffer.
|
||||
char* write_exponent(int exp, char* out) {
|
||||
// FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range");
|
||||
if (exp < 0) {
|
||||
*out++ = '-';
|
||||
exp = -exp;
|
||||
} else {
|
||||
*out++ = '+';
|
||||
}
|
||||
|
||||
|
||||
// Writes a two-digit value to out.
|
||||
inline void write2digits(char* out, size_t value) {
|
||||
//if (!is_constant_evaluated() && std::is_same<Char, char>::value &&
|
||||
// !FMT_OPTIMIZE_SIZE) {
|
||||
// memcpy(out, digits2(value), 2);
|
||||
// return;
|
||||
//}
|
||||
*out++ = static_cast<char>('0' + value / 10);
|
||||
*out = static_cast<char>('0' + value % 10);
|
||||
auto uexp = static_cast<uint32_t>(exp);
|
||||
// if (is_constant_evaluated()) {
|
||||
// if (uexp < 10) *out++ = '0';
|
||||
// return format_decimal<Char>(out, uexp, count_digits(uexp));
|
||||
// }
|
||||
if (uexp >= 100u) {
|
||||
const char* top = digits2(uexp / 100);
|
||||
if (uexp >= 1000u) *out++ = top[0];
|
||||
*out++ = static_cast<char>(top[1]);
|
||||
uexp %= 100;
|
||||
}
|
||||
const char* d = digits2(uexp);
|
||||
*out++ = static_cast<char>(d[0]);
|
||||
*out++ = static_cast<char>(d[1]);
|
||||
return out;
|
||||
}
|
||||
|
||||
// Writes the exponent exp in the form "[+-]d{2,3}" to buffer.
|
||||
char *write_exponent(int exp, char *out) {
|
||||
//FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range");
|
||||
if (exp < 0) {
|
||||
*out++ = '-';
|
||||
exp = -exp;
|
||||
} else {
|
||||
*out++ = '+';
|
||||
}
|
||||
auto uexp = static_cast<uint32_t>(exp);
|
||||
//if (is_constant_evaluated()) {
|
||||
// if (uexp < 10) *out++ = '0';
|
||||
// return format_decimal<Char>(out, uexp, count_digits(uexp));
|
||||
//}
|
||||
if (uexp >= 100u) {
|
||||
const char* top = digits2(uexp / 100);
|
||||
if (uexp >= 1000u) *out++ = top[0];
|
||||
*out++ = static_cast<char>(top[1]);
|
||||
uexp %= 100;
|
||||
}
|
||||
const char* d = digits2(uexp);
|
||||
*out++ = static_cast<char>(d[0]);
|
||||
*out++ = static_cast<char>(d[1]);
|
||||
return out;
|
||||
}
|
||||
|
||||
inline char *fill_n(char *p, int n, char c) {
|
||||
inline char* fill_n(char* p, int n, char c) {
|
||||
for (int i = 0; i < n; i++, p++) {
|
||||
*p = c;
|
||||
*p = c;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
inline char *format_decimal(char* out, uint64_t value, uint32_t size) {
|
||||
//FMT_ASSERT(size >= count_digits(value), "invalid digit count");
|
||||
unsigned n = size;
|
||||
while (value >= 100) {
|
||||
// Integer division is slow so do it for a group of two digits instead
|
||||
// of for every digit. The idea comes from the talk by Alexandrescu
|
||||
// "Three Optimization Tips for C++". See speed-test for a comparison.
|
||||
n -= 2;
|
||||
write2digits(out + n, static_cast<unsigned>(value % 100));
|
||||
value /= 100;
|
||||
}
|
||||
if (value >= 10) {
|
||||
n -= 2;
|
||||
write2digits(out + n, static_cast<unsigned>(value));
|
||||
} else {
|
||||
out[--n] = static_cast<char>('0' + value);
|
||||
}
|
||||
return out + n;
|
||||
inline void format_decimal_impl(char* out, uint64_t value, uint32_t size) {
|
||||
// FMT_ASSERT(size >= count_digits(value), "invalid digit count");
|
||||
unsigned n = size;
|
||||
while (value >= 100) {
|
||||
// Integer division is slow so do it for a group of two digits instead
|
||||
// of for every digit. The idea comes from the talk by Alexandrescu
|
||||
// "Three Optimization Tips for C++". See speed-test for a comparison.
|
||||
n -= 2;
|
||||
write2digits(out + n, static_cast<unsigned>(value % 100));
|
||||
value /= 100;
|
||||
}
|
||||
|
||||
inline char* write_significand(char* out, uint64_t significand, int significand_size,
|
||||
int integral_size, char decimal_point) {
|
||||
if (!decimal_point) return format_decimal(out, significand, significand_size);
|
||||
out += significand_size + 1;
|
||||
char* end = out;
|
||||
int floating_size = significand_size - integral_size;
|
||||
for (int i = floating_size / 2; i > 0; --i) {
|
||||
out -= 2;
|
||||
write2digits(out, static_cast<std::size_t>(significand % 100));
|
||||
significand /= 100;
|
||||
}
|
||||
if (floating_size % 2 != 0) {
|
||||
*--out = static_cast<char>('0' + significand % 10);
|
||||
significand /= 10;
|
||||
}
|
||||
*--out = decimal_point;
|
||||
format_decimal(out - integral_size, significand, integral_size);
|
||||
return end;
|
||||
if (value >= 10) {
|
||||
n -= 2;
|
||||
write2digits(out + n, static_cast<unsigned>(value));
|
||||
} else {
|
||||
out[--n] = static_cast<char>('0' + value);
|
||||
}
|
||||
//return out + n;
|
||||
}
|
||||
|
||||
char *write_float(const float f, char *buf)
|
||||
{
|
||||
inline char* format_decimal(char* out, uint64_t value, uint32_t num_digits) {
|
||||
format_decimal_impl(out, value, num_digits);
|
||||
return out + num_digits;
|
||||
}
|
||||
|
||||
inline char* write_significand_e(char* out, uint64_t significand,
|
||||
int significand_size, int exponent) {
|
||||
out = format_decimal(out, significand, significand_size);
|
||||
return fill_n(out, exponent, '0');
|
||||
}
|
||||
|
||||
inline char* write_significand(char* out, uint64_t significand,
|
||||
int significand_size, int integral_size,
|
||||
char decimal_point) {
|
||||
if (!decimal_point) return format_decimal(out, significand, significand_size);
|
||||
out += significand_size + 1;
|
||||
char* end = out;
|
||||
int floating_size = significand_size - integral_size;
|
||||
for (int i = floating_size / 2; i > 0; --i) {
|
||||
out -= 2;
|
||||
write2digits(out, static_cast<std::size_t>(significand % 100));
|
||||
significand /= 100;
|
||||
}
|
||||
if (floating_size % 2 != 0) {
|
||||
*--out = static_cast<char>('0' + significand % 10);
|
||||
significand /= 10;
|
||||
}
|
||||
*--out = decimal_point;
|
||||
format_decimal(out - integral_size, significand, integral_size);
|
||||
return end;
|
||||
}
|
||||
|
||||
char* write_float(const double f, char* buf) {
|
||||
const int spec_precision = -1; // unlimited
|
||||
|
||||
bool is_negative = std::signbit(f);
|
||||
|
||||
@@ -158,7 +166,7 @@ char *write_float(const float f, char *buf)
|
||||
|
||||
// print human-readable float for the value in range [1e-4, 1e+16]
|
||||
const int exp_lower = -4;
|
||||
const int exp_upper = 16; // (15 + 1) for double, (6+1) for float
|
||||
const int exp_upper = 16; // (15 + 1) for double, (6+1) for float
|
||||
char exp_char = 'e';
|
||||
char zero_char = '0';
|
||||
|
||||
@@ -168,92 +176,118 @@ char *write_float(const float f, char *buf)
|
||||
size_t size = size_t(significand_size) + (is_negative ? 1u : 0u);
|
||||
|
||||
int output_exp = ret.exponent + significand_size - 1;
|
||||
bool use_exp_format = (output_exp < exp_lower) || (output_exp >= exp_upper);
|
||||
|
||||
bool use_exp_format = (output_exp < exp_lower) || (output_exp >= exp_upper);
|
||||
|
||||
char decimal_point = '.';
|
||||
if (use_exp_format) {
|
||||
int num_zeros = 0;
|
||||
if (significand_size == 1) {
|
||||
decimal_point = '\0';
|
||||
}
|
||||
auto abs_output_exp = output_exp >= 0 ? output_exp : -output_exp;
|
||||
int exp_digits = 2;
|
||||
if (abs_output_exp >= 100) exp_digits = abs_output_exp >= 1000 ? 4 : 3;
|
||||
auto abs_output_exp = output_exp >= 0 ? output_exp : -output_exp;
|
||||
int exp_digits = 2;
|
||||
if (abs_output_exp >= 100) exp_digits = abs_output_exp >= 1000 ? 4 : 3;
|
||||
|
||||
size += (decimal_point ? 1u : 0u) + 2u + size_t(exp_digits);
|
||||
|
||||
if (is_negative) {
|
||||
*buf++ = '-';
|
||||
*buf++ = '-';
|
||||
}
|
||||
|
||||
buf = write_significand(buf, significand, significand_size, 1, decimal_point);
|
||||
buf =
|
||||
write_significand(buf, significand, significand_size, 1, decimal_point);
|
||||
|
||||
if (num_zeros > 0) buf = fill_n(buf, num_zeros, zero_char);
|
||||
*buf++ = exp_char;
|
||||
return write_exponent(output_exp, buf);
|
||||
if (num_zeros > 0) buf = fill_n(buf, num_zeros, zero_char);
|
||||
*buf++ = exp_char;
|
||||
return write_exponent(output_exp, buf);
|
||||
}
|
||||
|
||||
int exp = f.exponent + significand_size;
|
||||
if (f.exponent >= 0) {
|
||||
// 1234e5 -> 123400000[.0+]
|
||||
size += to_unsigned(f.exponent);
|
||||
int num_zeros = specs.precision - exp;
|
||||
abort_fuzzing_if(num_zeros > 5000);
|
||||
if (specs.alt()) {
|
||||
++size;
|
||||
if (num_zeros <= 0 && specs.type() != presentation_type::fixed)
|
||||
num_zeros = 0;
|
||||
if (num_zeros > 0) size += to_unsigned(num_zeros);
|
||||
}
|
||||
auto grouping = Grouping(loc, specs.localized());
|
||||
size += to_unsigned(grouping.count_separators(exp));
|
||||
return write_padded<Char, align::right>(out, specs, size, [&](iterator it) {
|
||||
if (s != sign::none) *it++ = detail::getsign<Char>(s);
|
||||
it = write_significand<Char>(it, significand, significand_size,
|
||||
f.exponent, grouping);
|
||||
if (!specs.alt()) return it;
|
||||
*it++ = decimal_point;
|
||||
return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it;
|
||||
});
|
||||
} else if (exp > 0) {
|
||||
// 1234e-2 -> 12.34[0+]
|
||||
int num_zeros = specs.alt() ? specs.precision - significand_size : 0;
|
||||
size += 1 + static_cast<unsigned>(max_of(num_zeros, 0));
|
||||
auto grouping = Grouping(loc, specs.localized());
|
||||
size += to_unsigned(grouping.count_separators(exp));
|
||||
return write_padded<Char, align::right>(out, specs, size, [&](iterator it) {
|
||||
if (s != sign::none) *it++ = detail::getsign<Char>(s);
|
||||
it = write_significand(it, significand, significand_size, exp,
|
||||
decimal_point, grouping);
|
||||
return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it;
|
||||
});
|
||||
}
|
||||
// 1234e-6 -> 0.001234
|
||||
int num_zeros = -exp;
|
||||
if (significand_size == 0 && specs.precision >= 0 &&
|
||||
specs.precision < num_zeros) {
|
||||
num_zeros = specs.precision;
|
||||
}
|
||||
bool pointy = num_zeros != 0 || significand_size != 0 || specs.alt();
|
||||
size += 1 + (pointy ? 1 : 0) + to_unsigned(num_zeros);
|
||||
return write_padded<Char, align::right>(out, specs, size, [&](iterator it) {
|
||||
if (s != sign::none) *it++ = detail::getsign<Char>(s);
|
||||
*it++ = zero;
|
||||
if (!pointy) return it;
|
||||
*it++ = decimal_point;
|
||||
it = detail::fill_n(it, num_zeros, zero);
|
||||
return write_significand<Char>(it, significand, significand_size);
|
||||
});
|
||||
int exp = ret.exponent + significand_size;
|
||||
if (ret.exponent >= 0) {
|
||||
// 1234e5 -> 123400000[.0+]
|
||||
size += static_cast<size_t>(ret.exponent);
|
||||
int num_zeros = spec_precision - exp;
|
||||
// abort_fuzzing_if(num_zeros > 5000);
|
||||
// if (specs.alt()) {
|
||||
// ++size;
|
||||
// if (num_zeros <= 0 && specs.type() != presentation_type::fixed)
|
||||
// num_zeros = 0;
|
||||
// if (num_zeros > 0) size += size_t(num_zeros);
|
||||
// }
|
||||
// auto grouping = Grouping(loc, specs.localized());
|
||||
// size += size_t(grouping.count_separators(exp));
|
||||
// return write_padded<Char, align::right>(out, specs, size, [&](iterator
|
||||
// it) {
|
||||
// if (s != sign::none) *it++ = detail::getsign<Char>(s);
|
||||
// it = write_significand<Char>(it, significand, significand_size,
|
||||
// f.exponent, grouping);
|
||||
// if (!specs.alt()) return it;
|
||||
// *it++ = decimal_point;
|
||||
// return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it;
|
||||
// });
|
||||
|
||||
|
||||
// TODO
|
||||
return nullptr;
|
||||
if (is_negative) {
|
||||
*buf++ = '-';
|
||||
}
|
||||
|
||||
return write_significand_e(buf, significand, significand_size,
|
||||
ret.exponent);
|
||||
|
||||
} else if (exp > 0) {
|
||||
// 1234e-2 -> 12.34[0+]
|
||||
// int num_zeros = specs.alt() ? spec_precision - significand_size : 0;
|
||||
// size += 1 + static_cast<unsigned>(max_of(num_zeros, 0));
|
||||
size += 1;
|
||||
// auto grouping = Grouping(loc, specs.localized());
|
||||
// size += size_t(grouping.count_separators(exp));
|
||||
// return write_padded<Char, align::right>(out, specs, size, [&](iterator
|
||||
// it) {
|
||||
// if (s != sign::none) *it++ = detail::getsign<Char>(s);
|
||||
// it = write_significand(it, significand, significand_size, exp,
|
||||
// decimal_point, grouping);
|
||||
// return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it;
|
||||
// });
|
||||
if (is_negative) {
|
||||
*buf++ = '-';
|
||||
}
|
||||
|
||||
return write_significand(buf, significand, significand_size, exp,
|
||||
decimal_point);
|
||||
}
|
||||
// 1234e-6 -> 0.001234
|
||||
int num_zeros = -exp;
|
||||
// if (significand_size == 0 && specs.precision >= 0 &&
|
||||
// specs.precision < num_zeros) {
|
||||
// num_zeros = spec_precision;
|
||||
// }
|
||||
bool pointy = num_zeros != 0 || significand_size != 0; // || specs.alt();
|
||||
size += 1u + (pointy ? 1u : 0u) + size_t(num_zeros);
|
||||
// return write_padded<Char, align::right>(out, specs, size, [&](iterator it)
|
||||
// {
|
||||
// if (s != sign::none) *it++ = detail::getsign<Char>(s);
|
||||
// *it++ = zero;
|
||||
// if (!pointy) return it;
|
||||
// *it++ = decimal_point;
|
||||
// it = detail::fill_n(it, num_zeros, zero);
|
||||
// return write_significand<Char>(it, significand, significand_size);
|
||||
// });
|
||||
|
||||
if (is_negative) {
|
||||
*buf++ = '-';
|
||||
}
|
||||
|
||||
*buf++ = zero_char;
|
||||
|
||||
if (!pointy) return buf;
|
||||
*buf++ = decimal_point;
|
||||
buf = fill_n(buf, num_zeros, zero_char);
|
||||
|
||||
return format_decimal(buf, significand, significand_size);
|
||||
}
|
||||
|
||||
#if 0
|
||||
std::string print_floats(const std::vector<float> &v) {
|
||||
|
||||
char buffer[40];
|
||||
char buffer[40]; // 25 should be enough
|
||||
|
||||
size_t n = v.size();
|
||||
std::vector<char> dst;
|
||||
@@ -268,9 +302,8 @@ std::string print_floats(const std::vector<float> &v) {
|
||||
curr += 2;
|
||||
}
|
||||
|
||||
char *e = jkj::dragonbox::to_chars(v[i], buffer);
|
||||
char *e = write_float(v[i], buffer);
|
||||
size_t len = e - buffer; // includes '\0'
|
||||
std::cout << len << "\n";
|
||||
|
||||
// +2 for ', '
|
||||
if ((curr + len + 2) >= dst.size()) {
|
||||
@@ -285,7 +318,6 @@ std::string print_floats(const std::vector<float> &v) {
|
||||
std::string s(dst.data(), curr);
|
||||
return s;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
std::string print_floats(const std::vector<float> &v) {
|
||||
@@ -323,10 +355,9 @@ std::string print_floats(const std::vector<float> &v) {
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int main(int argc, char** argv) {
|
||||
bool delim_at_end = true;
|
||||
size_t n = 1024*1024*16;
|
||||
size_t n = 1024 * 1024 * 16;
|
||||
if (argc > 1) {
|
||||
n = std::stoi(argv[1]);
|
||||
}
|
||||
@@ -337,9 +368,10 @@ int main(int argc, char **argv)
|
||||
double d = 1.0;
|
||||
for (size_t i = 0; i < 32; i++) {
|
||||
char buf[25];
|
||||
//char *p = dtoa_milo(d, buf);
|
||||
//*p = '\0';
|
||||
//std::cout << buf << "\n";
|
||||
|
||||
char *p = write_float(d, buf);
|
||||
*p = '\0';
|
||||
std::cout << "db " << buf << "\n";
|
||||
|
||||
{
|
||||
auto ret = jkj::dragonbox::to_decimal(d);
|
||||
@@ -363,20 +395,21 @@ int main(int argc, char **argv)
|
||||
}
|
||||
|
||||
d = d * 10.0;
|
||||
|
||||
}
|
||||
|
||||
std::vector<float> arr = gen_floats(n);
|
||||
//std::cout << input << "\n";
|
||||
std::vector<float> arr = gen_floats(n);
|
||||
// std::cout << input << "\n";
|
||||
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
|
||||
//std::string s = print_floats(arr);
|
||||
//auto end = std::chrono::steady_clock::now();
|
||||
std::string s = print_floats(arr);
|
||||
auto end = std::chrono::steady_clock::now();
|
||||
|
||||
//std::cout << "n elems " << arr.size() << "\n";
|
||||
std::cout << "n elems " << arr.size() << "\n";
|
||||
|
||||
//std::cout << "print : " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << " [ms]\n";
|
||||
std::cout << "print : " <<
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()
|
||||
<< " [ms]\n";
|
||||
|
||||
//std::cout << s << "\n";
|
||||
|
||||
|
||||
295
src/str-util.cc
295
src/str-util.cc
@@ -1,10 +1,24 @@
|
||||
// SPDX-License-Identifier: Apache 2.0
|
||||
// Copyright 2023 - Present, Light Transport Entertainment, Inc.
|
||||
|
||||
#include <cmath>
|
||||
#include "str-util.hh"
|
||||
|
||||
#include "unicode-xid.hh"
|
||||
#include "common-macros.inc"
|
||||
|
||||
// external
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Weverything"
|
||||
#endif
|
||||
|
||||
#include "external/dragonbox/dragonbox_to_chars.h"
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
namespace tinyusdz {
|
||||
|
||||
std::string buildEscapedAndQuotedStringForUSDA(const std::string &str) {
|
||||
@@ -675,4 +689,285 @@ bool is_valid_utf8_identifier(const std::string &str) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// based on fmtlib
|
||||
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
|
||||
// MIT license.
|
||||
//
|
||||
|
||||
namespace internal {
|
||||
|
||||
// TOOD: Use builtin_clz insturction?
|
||||
// T = uint32 or uint64
|
||||
template <typename T>
|
||||
inline int count_digits(T n) {
|
||||
int count = 1;
|
||||
for (;;) {
|
||||
// Integer division is slow so do it for a group of four digits instead
|
||||
// of for every digit. The idea comes from the talk by Alexandrescu
|
||||
// "Three Optimization Tips for C++". See speed-test for a comparison.
|
||||
if (n < 10) return count;
|
||||
if (n < 100) return count + 1;
|
||||
if (n < 1000) return count + 2;
|
||||
if (n < 10000) return count + 3;
|
||||
n /= 10000u;
|
||||
count += 4;
|
||||
}
|
||||
}
|
||||
|
||||
// Converts value in the range [0, 100) to a string.
|
||||
// GCC generates slightly better code when value is pointer-size.
|
||||
inline auto digits2(size_t value) -> const char* {
|
||||
// Align data since unaligned access may be slower when crossing a
|
||||
// hardware-specific boundary.
|
||||
alignas(2) static const char data[] =
|
||||
"0001020304050607080910111213141516171819"
|
||||
"2021222324252627282930313233343536373839"
|
||||
"4041424344454647484950515253545556575859"
|
||||
"6061626364656667686970717273747576777879"
|
||||
"8081828384858687888990919293949596979899";
|
||||
return &data[value * 2];
|
||||
}
|
||||
|
||||
// Writes a two-digit value to out.
|
||||
inline void write2digits(char* out, size_t value) {
|
||||
// if (!is_constant_evaluated() && std::is_same<Char, char>::value &&
|
||||
// !FMT_OPTIMIZE_SIZE) {
|
||||
// memcpy(out, digits2(value), 2);
|
||||
// return;
|
||||
// }
|
||||
*out++ = static_cast<char>('0' + value / 10);
|
||||
*out = static_cast<char>('0' + value % 10);
|
||||
}
|
||||
|
||||
// Writes the exponent exp in the form "[+-]d{2,3}" to buffer.
|
||||
static char* write_exponent(int exp, char* out) {
|
||||
// FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range");
|
||||
if (exp < 0) {
|
||||
*out++ = '-';
|
||||
exp = -exp;
|
||||
} else {
|
||||
*out++ = '+';
|
||||
}
|
||||
auto uexp = static_cast<uint32_t>(exp);
|
||||
// if (is_constant_evaluated()) {
|
||||
// if (uexp < 10) *out++ = '0';
|
||||
// return format_decimal<Char>(out, uexp, count_digits(uexp));
|
||||
// }
|
||||
if (uexp >= 100u) {
|
||||
const char* top = digits2(uexp / 100);
|
||||
if (uexp >= 1000u) *out++ = top[0];
|
||||
*out++ = static_cast<char>(top[1]);
|
||||
uexp %= 100;
|
||||
}
|
||||
const char* d = digits2(uexp);
|
||||
*out++ = static_cast<char>(d[0]);
|
||||
*out++ = static_cast<char>(d[1]);
|
||||
return out;
|
||||
}
|
||||
|
||||
inline char* fill_n(char* p, int n, char c) {
|
||||
for (int i = 0; i < n; i++, p++) {
|
||||
*p = c;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
inline void format_decimal_impl(char* out, uint64_t value, uint32_t size) {
|
||||
// FMT_ASSERT(size >= count_digits(value), "invalid digit count");
|
||||
unsigned n = size;
|
||||
while (value >= 100) {
|
||||
// Integer division is slow so do it for a group of two digits instead
|
||||
// of for every digit. The idea comes from the talk by Alexandrescu
|
||||
// "Three Optimization Tips for C++". See speed-test for a comparison.
|
||||
n -= 2;
|
||||
write2digits(out + n, static_cast<unsigned>(value % 100));
|
||||
value /= 100;
|
||||
}
|
||||
if (value >= 10) {
|
||||
n -= 2;
|
||||
write2digits(out + n, static_cast<unsigned>(value));
|
||||
} else {
|
||||
out[--n] = static_cast<char>('0' + value);
|
||||
}
|
||||
//return out + n;
|
||||
}
|
||||
|
||||
inline char* format_decimal(char* out, uint64_t value, uint32_t num_digits) {
|
||||
format_decimal_impl(out, value, num_digits);
|
||||
return out + num_digits;
|
||||
}
|
||||
|
||||
inline char* write_significand_e(char* out, uint64_t significand,
|
||||
int significand_size, int exponent) {
|
||||
out = format_decimal(out, significand, uint32_t(significand_size));
|
||||
return fill_n(out, exponent, '0');
|
||||
}
|
||||
|
||||
inline char* write_significand(char* out, uint64_t significand,
|
||||
int significand_size, int integral_size,
|
||||
char decimal_point) {
|
||||
if (!decimal_point) return format_decimal(out, significand, uint32_t(significand_size));
|
||||
out += significand_size + 1;
|
||||
char* end = out;
|
||||
int floating_size = significand_size - integral_size;
|
||||
for (int i = floating_size / 2; i > 0; --i) {
|
||||
out -= 2;
|
||||
write2digits(out, static_cast<std::size_t>(significand % 100));
|
||||
significand /= 100;
|
||||
}
|
||||
if (floating_size % 2 != 0) {
|
||||
*--out = static_cast<char>('0' + significand % 10);
|
||||
significand /= 10;
|
||||
}
|
||||
*--out = decimal_point;
|
||||
format_decimal(out - integral_size, significand, uint32_t(integral_size));
|
||||
return end;
|
||||
}
|
||||
|
||||
// Use dragonbox algorithm to print floating point value.
|
||||
// Use to_deciamal and do human-readable pretty printing for some value range(e.g. print 1e-3 as 0.001)
|
||||
//
|
||||
// exp_upper: (15 + 1) for double, (6+1) for float
|
||||
static char* dtoa_dragonbox(const double f, char* buf, int exp_upper = 16) {
|
||||
//const int spec_precision = -1; // unlimited
|
||||
|
||||
bool is_negative = std::signbit(f);
|
||||
|
||||
auto ret = jkj::dragonbox::to_decimal(f);
|
||||
|
||||
// print human-readable float for the value in range [1e-exp_lower, 1e+exp_upper]
|
||||
const int exp_lower = -4;
|
||||
char exp_char = 'e';
|
||||
char zero_char = '0';
|
||||
|
||||
auto significand = ret.significand;
|
||||
int significand_size = count_digits(significand);
|
||||
|
||||
//size_t size = size_t(significand_size) + (is_negative ? 1u : 0u);
|
||||
|
||||
int output_exp = ret.exponent + significand_size - 1;
|
||||
bool use_exp_format = (output_exp < exp_lower) || (output_exp >= exp_upper);
|
||||
|
||||
char decimal_point = '.';
|
||||
if (use_exp_format) {
|
||||
int num_zeros = 0;
|
||||
if (significand_size == 1) {
|
||||
decimal_point = '\0';
|
||||
}
|
||||
//auto abs_output_exp = output_exp >= 0 ? output_exp : -output_exp;
|
||||
//int exp_digits = 2;
|
||||
//if (abs_output_exp >= 100) exp_digits = abs_output_exp >= 1000 ? 4 : 3;
|
||||
|
||||
//size += (decimal_point ? 1u : 0u) + 2u + size_t(exp_digits);
|
||||
|
||||
if (is_negative) {
|
||||
*buf++ = '-';
|
||||
}
|
||||
|
||||
buf =
|
||||
write_significand(buf, significand, significand_size, 1, decimal_point);
|
||||
|
||||
if (num_zeros > 0) buf = fill_n(buf, num_zeros, zero_char);
|
||||
*buf++ = exp_char;
|
||||
return write_exponent(output_exp, buf);
|
||||
}
|
||||
|
||||
int exp = ret.exponent + significand_size;
|
||||
if (ret.exponent >= 0) {
|
||||
// 1234e5 -> 123400000[.0+]
|
||||
//size += static_cast<size_t>(ret.exponent);
|
||||
//int num_zeros = spec_precision - exp;
|
||||
// abort_fuzzing_if(num_zeros > 5000);
|
||||
// if (specs.alt()) {
|
||||
// ++size;
|
||||
// if (num_zeros <= 0 && specs.type() != presentation_type::fixed)
|
||||
// num_zeros = 0;
|
||||
// if (num_zeros > 0) size += size_t(num_zeros);
|
||||
// }
|
||||
// auto grouping = Grouping(loc, specs.localized());
|
||||
// size += size_t(grouping.count_separators(exp));
|
||||
// return write_padded<Char, align::right>(out, specs, size, [&](iterator
|
||||
// it) {
|
||||
// if (s != sign::none) *it++ = detail::getsign<Char>(s);
|
||||
// it = write_significand<Char>(it, significand, significand_size,
|
||||
// f.exponent, grouping);
|
||||
// if (!specs.alt()) return it;
|
||||
// *it++ = decimal_point;
|
||||
// return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it;
|
||||
// });
|
||||
|
||||
if (is_negative) {
|
||||
*buf++ = '-';
|
||||
}
|
||||
|
||||
return write_significand_e(buf, significand, significand_size,
|
||||
ret.exponent);
|
||||
|
||||
} else if (exp > 0) {
|
||||
// 1234e-2 -> 12.34[0+]
|
||||
// int num_zeros = specs.alt() ? spec_precision - significand_size : 0;
|
||||
// size += 1 + static_cast<unsigned>(max_of(num_zeros, 0));
|
||||
//size += 1;
|
||||
// auto grouping = Grouping(loc, specs.localized());
|
||||
// size += size_t(grouping.count_separators(exp));
|
||||
// return write_padded<Char, align::right>(out, specs, size, [&](iterator
|
||||
// it) {
|
||||
// if (s != sign::none) *it++ = detail::getsign<Char>(s);
|
||||
// it = write_significand(it, significand, significand_size, exp,
|
||||
// decimal_point, grouping);
|
||||
// return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it;
|
||||
// });
|
||||
if (is_negative) {
|
||||
*buf++ = '-';
|
||||
}
|
||||
|
||||
return write_significand(buf, significand, significand_size, exp,
|
||||
decimal_point);
|
||||
}
|
||||
// 1234e-6 -> 0.001234
|
||||
int num_zeros = -exp;
|
||||
// if (significand_size == 0 && specs.precision >= 0 &&
|
||||
// specs.precision < num_zeros) {
|
||||
// num_zeros = spec_precision;
|
||||
// }
|
||||
bool pointy = num_zeros != 0 || significand_size != 0; // || specs.alt();
|
||||
//size += 1u + (pointy ? 1u : 0u) + size_t(num_zeros);
|
||||
// return write_padded<Char, align::right>(out, specs, size, [&](iterator it)
|
||||
// {
|
||||
// if (s != sign::none) *it++ = detail::getsign<Char>(s);
|
||||
// *it++ = zero;
|
||||
// if (!pointy) return it;
|
||||
// *it++ = decimal_point;
|
||||
// it = detail::fill_n(it, num_zeros, zero);
|
||||
// return write_significand<Char>(it, significand, significand_size);
|
||||
// });
|
||||
|
||||
if (is_negative) {
|
||||
*buf++ = '-';
|
||||
}
|
||||
|
||||
*buf++ = zero_char;
|
||||
|
||||
if (!pointy) return buf;
|
||||
*buf++ = decimal_point;
|
||||
buf = fill_n(buf, num_zeros, zero_char);
|
||||
|
||||
return format_decimal(buf, significand, uint32_t(significand_size));
|
||||
}
|
||||
|
||||
static char* dtoa_dragonbox(const float f, char* buf) {
|
||||
return dtoa_dragonbox(double(f), buf, 7);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
char *dtoa(float f, char *buffer) {
|
||||
return internal::dtoa_dragonbox(f, buffer);
|
||||
}
|
||||
|
||||
char *dtoa(double f, char *buffer) {
|
||||
return internal::dtoa_dragonbox(f, buffer);
|
||||
}
|
||||
|
||||
} // namespace tinyusdz
|
||||
|
||||
@@ -387,6 +387,17 @@ inline std::string codepoint_to_utf8(uint32_t code) {
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// float/double to string
|
||||
// Currently tinyusdz uses dragonbox algorithm
|
||||
//
|
||||
// buffer must be at least 25 bytes.
|
||||
// filled string is not null-terminated.
|
||||
// (Use *(dtoa(f, buf)) = '\0' if you want null-terminated string)
|
||||
//
|
||||
char *dtoa(float f, char *buf);
|
||||
char *dtoa(double f, char *buf);
|
||||
|
||||
#if 0 // TODO
|
||||
///
|
||||
/// Convert UTF-8 code to UTF-8 char
|
||||
|
||||
Reference in New Issue
Block a user