std::cout 打印浮点值的所有数字

std::cout print all digits of float value

我有这个功能:

template<typename T> // T can be float, double or long double
void printAllDigits(T value)
{
    std::cout << std::fixed << std::setprecision(999) << value;
}

打印浮点值的所有数字是一个愚蠢的实现。

这有一些问题:

但它做对了一些事情:

如何在满足我的要求的同时减少该功能的浪费?

std::numeric_limits<T>::max_digits10 不正确,因为它太小了! std::numeric_limits<float>::min() gets printed as "0.000000000" instead of "0.0000000000000000000000000000000000000117549435082228750796873653722224567781866555677208752150875170627841725945472717285156250000000..."

假设实现使用基数 2 作为浮点数,
if (std::numeric_limits<T>::radix == 2)
然后写 ALL ALL 可能值的十进制数字需要:

std::cout << std::setprecision(std::numeric_limits<T>::digits - std::numeric_limits<T>::min_exponent);

我怀疑 radix == 10 的公式相同,但我无法检查(手头没有实现)。

我想知道你为什么要打印所有小数。如果只是原封不动地重构值,这就没有必要了。混合使用精度为 std::numeric_limits<T>::max_digits10 的科学记数法应该可以完成这项工作。否则,有众所周知的算法来打印足够的十进制数字。它们用于主要的 REPL 语言(参见 python repr,或者 java、javascript、一些 Smalltalk 等如何打印浮点值),不幸的是,它们不是标准 C++ 库 AFAIK 的一部分。

这是另一个可以避免删除尾随零的答案。

我们的想法是缩放值(乘以基数^比例),直到我们获得一个带有空小数部分的有效数字int.frac。

这适用于基数 == 2 或 10。

// return the number of fractional decimal digits required to print a floating point exact value
template<class FloatType> int required_precision( FloatType value )
{
    assert( std::numeric_limits<FloatType>::radix == 2 ||
            std::numeric_limits<FloatType>::radix == 10 );
    if (! std::isfinite(value) ) return 0;
    // exponent of 0.0 is implementation defined, so don't rely on ilogb
    if (value == FloatType(0) ) return 1;
    // use ilogb for initial guess of scale                                                                                                                                         int exponent = std::ilogb( value );
    int scale =  - exponent;
    FloatType significand = std::scalbn( std::abs(value) , scale );
    // scale up until fraction part is null
    while( significand-std::trunc(significand) != FloatType(0) ) {
        scale += 1;
        significand = std::scalbn( significand - std::trunc(significand) , 1 );
    }
    // print at least 1 fractional zero for case of integral value
    return std::max(1,scale);
}

这应该适用于任何基数。

如果基数为2,并且位数不超过unsigned long long,那么我们可以只缩放一次,得到一个整数有效位,统计那个有效位的尾随零来调整规模。

template<class FloatType> int required_precision( FloatType value )
{
    assert( std::numeric_limits<FloatType>::radix == 2 &&
            std::numeric_limits<FloatType>::digits <= std::numeric_limits<unsigned long long>::digits );
    if (! std::isfinite(value) ) return 0;
    // exponent of 0.0 is implementation defined, so don't rely on ilogb
    if (value == FloatType(0) ) return 1;
    int exponent = std::ilogb( value );
    // quick check if exponent is greater than max number of digits, then it is an integral value
    if( exponent >= std::numeric_limits<FloatType>::digits ) return 1;
    // scale significand to integer
    int scale =  std::numeric_limits<FloatType>::digits - 1 - exponent;
    FloatType significand = std::scalbn( std::abs(value) , scale );
    unsigned long long int_significand = static_cast<unsigned long long>(significand);
    // replace this by your own trick to count trailing zeroes if not compiling with gcc/g++
    return std::max(1,scale - __builtin_ctzll( int_significand ));
}

如果 unsigned long long 没有足够的位来保存某些 FloatType 的尾数,则缩放比例必须用上述 2 之间的解决方案分开:

template<class FloatType> int required_precision( FloatType value )
{
    assert( std::numeric_limits<FloatType>::radix == 2 );
    if (! std::isfinite(value) ) return 0;
    if (value == FloatType(0) ) return 1;
    int exponent = std::ilogb( value );
    if( exponent >= std::numeric_limits<FloatType>::digits ) return 1;
    // scale significand to integer : care to not overflow UNSIGNED_LONG_LONG_MAX
    int max_bits = std::numeric_limits<unsigned long long>::digits;
    int scale =  max_bits - 1 - exponent;
    FloatType significand = std::scalbn( std::abs(value) , scale );
    while( significand-std::trunc(significand) != FloatType(0) ) {
        scale += max_bits;
        significand = std::scalbn( significand - std::trunc(significand) , max_bits );
    }
    unsigned long long int_significand = static_cast<unsigned long long>(significand);
    return std::max(1,scale - __builtin_ctzll( int_significand ));
}

999 digits is probably enough

典型的 float 需要小数点右边大约 150 位数字,而 double 需要大约 1075 位才能在某些情况下打印 exact 值。设置如此高的精度可能不会产生准确的输出,需要 specialized code 才能做到这一点。

放弃“我从不想要科学记数法”,使用十进制浮点数记数法,打印至少6(float),9(double) 重要的地方足以 round-trip 从 floating-point 到文本到浮点数的值。

或使用十六进制表示法。