C++ 中,大多数能够输出浮点数的函数似乎都有这样一个特性:对于任意一个能够被 double
类型精确表示的整数值 x,若
x⋅2N(N 为整数)不会超过 double
类型的上界,那么这个值可以被完全精确地转换为十进制形式输出。
例如:
#include <cmath>
#include <cstdio>
#include <iostream>
#include <iomanip>
#include <format>
int main() {
double x = 3.0 * std::pow(2, 128);
std::printf("printf %.0lf\n", x);
std::cout << std::fixed << std::setprecision(0) << "cout " << x << std::endl;
std::cout << std::format("format {:.0f}", x) << std::endl;
return 0;
}
输出(GCC 和 Clang 编译器均为此结果):
printf 1020847100762815390390123822295304634368
cout 1020847100762815390390123822295304634368
format 1020847100762815390390123822295304634368
这与 Python 的高精度整数计算结果完全一致。
而简单的手写输出函数无法支持这样的特性。
template <typename T, typename std::enable_if<is_floating_point_or_float128<T>::value>::type* = nullptr>
Printer &write(T x) {
if (std::isnan(x)) return write("nan");
static char st[std::numeric_limits<T>::max_exponent10+1];
char *top = st;
if (x < 0) x = -x, put('-');
if (std::isinf(x)) return write("Infinity");
auto y = std::floor(x);
while (y >= 1) {
auto cur = std::fmod(y, 10);
y = (y - cur) / 10;
*top++ = (int)(cur) ^ 48;
}
if (top == st) put('0');
while (top != st) put(*--top);
x -= std::floor(x);
put('.');
for (auto i = 0; i < 6; i++) {
x = x * 10;
auto cur = std::floor(x);
x -= cur;
put((int)cur ^ 48);
}
return *this;
}
// ...
io << "IO: " << x << "\n";
输出:
1020847100762815628066424846468008666808.000000
如何以十进制输出一个浮点数,使得能够支持这样的特性?换句话说,标准库的浮点数输出是如何实现的?