STL/tools/scripts/charconv_tables_generate.cpp

181 строка
6.1 KiB
C++

// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// The following code generated the lookup tables for the
// scientific exponent X. Don't remove this code.
// cl /EHsc /nologo /W4 /MT /O2 /std:c++17 charconv_tables_generate.cpp && charconv_tables_generate
#include <algorithm>
#include <cassert>
#include <charconv>
#include <cmath>
#include <cstdint>
#include <cstdio>
#include <limits>
#include <map>
#include <system_error>
#include <type_traits>
#include <vector>
using namespace std;
template <typename UInt, typename Pred>
[[nodiscard]] UInt uint_partition_point(UInt first, const UInt last, Pred pred) {
// Find the beginning of the false partition in [first, last).
// [first, last) is partitioned when all of the true values occur before all of the false values.
static_assert(is_unsigned_v<UInt>);
assert(first <= last);
for (UInt n = last - first; n > 0;) {
const UInt n2 = n / 2;
const UInt mid = first + n2;
if (pred(mid)) {
first = mid + 1;
n = n - n2 - 1;
} else {
n = n2;
}
}
return first;
}
template <typename Floating>
[[nodiscard]] int scientific_exponent_X(const int P, const Floating flt) {
char buf[400]; // more than enough
// C11 7.21.6.1 "The fprintf function"/8 performs trial formatting with scientific precision P - 1.
const auto to_result = to_chars(buf, end(buf), flt, chars_format::scientific, P - 1);
assert(to_result.ec == errc{});
const char* exp_ptr = find(buf, to_result.ptr, 'e');
assert(exp_ptr != to_result.ptr);
++exp_ptr; // advance past 'e'
if (*exp_ptr == '+') {
++exp_ptr; // advance past '+' which from_chars() won't parse
}
int X;
const auto from_result = from_chars(exp_ptr, to_result.ptr, X);
assert(from_result.ec == errc{});
return X;
}
template <typename UInt>
void print_table(const vector<UInt>& v, const char* const name) {
constexpr const char* UIntName = is_same_v<UInt, uint32_t> ? "uint32_t" : "uint64_t";
printf("static const %s %s[%zu] = {\n", UIntName, name, v.size());
for (const auto& val : v) {
if constexpr (is_same_v<UInt, uint32_t>) {
printf("0x%08Xu,\n", val);
} else {
printf("0x%016llXu,\n", val);
}
}
printf("};\n");
}
enum class Mode { Tables, Tests };
template <typename Floating>
void generate_tables(const Mode mode) {
using Limits = numeric_limits<Floating>;
using UInt = conditional_t<is_same_v<Floating, float>, uint32_t, uint64_t>;
map<int, map<int, UInt>> P_X_LargestValWithX;
constexpr int MaxP = Limits::max_exponent10 + 1; // MaxP performs no rounding during trial formatting
for (int P = 1; P <= MaxP; ++P) {
for (int X = -5; X < P; ++X) {
constexpr Floating first = static_cast<Floating>(9e-5); // well below 9.5e-5, otherwise arbitrary
constexpr Floating last = Limits::infinity(); // one bit above Limits::max()
const UInt val_beyond_X = uint_partition_point(reinterpret_cast<const UInt&>(first),
reinterpret_cast<const UInt&>(last),
[P, X](const UInt u) { return scientific_exponent_X(P, reinterpret_cast<const Floating&>(u)) <= X; });
P_X_LargestValWithX[P][X] = val_beyond_X - 1;
}
}
constexpr const char* FloatingName = is_same_v<Floating, float> ? "float" : "double";
constexpr int MaxSpecialP = is_same_v<Floating, float> ? 7 : 15; // MaxSpecialP is affected by exponent adjustment
if (mode == Mode::Tables) {
printf("template <>\n");
printf("struct _General_precision_tables_2<%s> {\n", FloatingName);
printf("static constexpr int _Max_special_P = %d;\n", MaxSpecialP);
vector<UInt> special;
for (int P = 1; P <= MaxSpecialP; ++P) {
for (int X = -5; X < P; ++X) {
const UInt val = P_X_LargestValWithX[P][X];
special.push_back(val);
}
}
print_table(special, "_Special_X_table");
for (int P = MaxSpecialP + 1; P < MaxP; ++P) {
for (int X = -5; X < P; ++X) {
const UInt val = P_X_LargestValWithX[P][X];
assert(val == P_X_LargestValWithX[MaxP][X]);
}
}
printf("static constexpr int _Max_P = %d;\n", MaxP);
vector<UInt> ordinary;
for (int X = -5; X < MaxP; ++X) {
const UInt val = P_X_LargestValWithX[MaxP][X];
ordinary.push_back(val);
}
print_table(ordinary, "_Ordinary_X_table");
printf("};\n");
} else {
printf("==========\n");
printf("Test cases for %s:\n", FloatingName);
constexpr int Hexits = is_same_v<Floating, float> ? 6 : 13;
constexpr const char* Suffix = is_same_v<Floating, float> ? "f" : "";
for (int P = 1; P <= MaxP; ++P) {
for (int X = -5; X < P; ++X) {
if (P <= MaxSpecialP || P == 25 || P == MaxP || X == P - 1) {
const UInt val1 = P_X_LargestValWithX[P][X];
const Floating f1 = reinterpret_cast<const Floating&>(val1);
const UInt val2 = val1 + 1;
const Floating f2 = reinterpret_cast<const Floating&>(val2);
printf("{%.*a%s, chars_format::general, %d, \"%.*g\"},\n", Hexits, f1, Suffix, P, P, f1);
if (isfinite(f2)) {
printf("{%.*a%s, chars_format::general, %d, \"%.*g\"},\n", Hexits, f2, Suffix, P, P, f2);
}
}
}
}
}
}
int main() {
printf("template <class _Floating>\n");
printf("struct _General_precision_tables_2;\n");
generate_tables<float>(Mode::Tables);
generate_tables<double>(Mode::Tables);
generate_tables<float>(Mode::Tests);
generate_tables<double>(Mode::Tests);
}