19

我正在编写一个程序,该程序打印要在另一个程序中使用的浮点文字。

为了保持原始浮点数的精度,我需要打印多少位?

由于浮点数具有24 * (log(2) / log(10)) = 7.2247199小数位精度,我最初的想法是打印 8 位数字就足够了。但如果我不走运,它们0.2247199会分布在 7 个有效数字的左侧和右侧,所以我可能应该打印 9 个十进制数字。

我的分析正确吗?对于所有情况,9 个十进制数字是否足够?喜欢printf("%.9g", x);

在 7 或 8 就足够的情况下,是否有一个标准函数可以将浮点数转换为具有该值所需的最小小数位数的字符串,所以我不打印不必要的数字?

注意:我不能使用十六进制浮点文字,因为标准 C++ 不支持它们。

4

8 回答 8

16

In order to guarantee that a binary->decimal->binary roundtrip recovers the original binary value, IEEE 754 requires


The original binary value will be preserved by converting to decimal and back again using:[10]

    5 decimal digits for binary16
    9 decimal digits for binary32
    17 decimal digits for binary64
    36 decimal digits for binary128

For other binary formats the required number of decimal digits is

    1 + ceiling(p*log10(2)) 

where p is the number of significant bits in the binary format, e.g. 24 bits for binary32.

In C, the functions you can use for these conversions are snprintf() and strtof/strtod/strtold().

Of course, in some cases even more digits can be useful (no, they are not always "noise", depending on the implementation of the decimal conversion routines such as snprintf() ). Consider e.g. printing dyadic fractions.

于 2012-06-05T11:27:16.287 回答
3

24 * (log(2) / log(10)) = 7.2247199

That's pretty representative for the problem. It makes no sense whatsoever to express the number of significant digits with an accuracy of 0.0000001 digits. You are converting numbers to text for the benefit of a human, not a machine. A human couldn't care less, and would much prefer, if you wrote

24 * (log(2) / log(10)) = 7

Trying to display 8 significant digits just generates random noise digits. With non-zero odds that 7 is already too much because floating point error accumulates in calculations. Above all, print numbers using a reasonable unit of measure. People are interested in millimeters, grams, pounds, inches, etcetera. No architect will care about the size of a window expressed more accurately than 1 mm. No window manufacturing plant will promise a window sized as accurate as that.

Last but not least, you cannot ignore the accuracy of the numbers you feed into your program. Measuring the speed of an unladen European swallow down to 7 digits is not possible. It is roughly 11 meters per second, 2 digits at best. So performing calculations on that speed and printing a result that has more significant digits produces nonsensical results that promise accuracy that isn't there.

于 2012-06-05T10:56:44.207 回答
3

If you have a C library that is conforming to C99 (and if your float types have a base that is a power of 2 :) the printf format character %a can print floating point values without lack of precision in hexadecimal form, and utilities as scanf and strod will be able to read them.

于 2012-06-05T11:40:26.760 回答
2

If the program is meant to be read by a computer, I would do the simple trick of using char* aliasing.

  • alias float* to char*
  • copy into an unsigned (or whatever unsigned type is sufficiently large) via char* aliasing
  • print the unsigned value

Decoding is just reversing the process (and on most platform a direct reinterpret_cast can be used).

于 2012-06-05T11:21:35.180 回答
1

Java 中使用的浮点到十进制转换保证产生的小数点后的十进制位数最少,需要将数字与其邻居(或多或少)区分开来。

您可以从这里复制算法:http: //www.docjar.com/html/api/sun/misc/FloatingDecimal.java.html 注意FloatingDecimal(float)构造函数和toJavaFormatString()方法。

于 2012-06-05T10:29:54.607 回答
1

If you read these papers (see below), you'll find that there are some algorithm that print the minimum number of decimal digits such that the number can be re-interpreted unchanged (i.e. by scanf).

Since there might be several such numbers, the algorithm also pick the nearest decimal fraction to the original binary fraction (I named float value).

A pity that there's no such standard library in C.

于 2012-08-14T12:52:01.787 回答
0

您可以使用sprintf. 我不确定这是否完全回答了您的问题,但无论如何,这里是示例代码

#include <stdio.h>
int main( void )
{
float d_n = 123.45;
char s_cp[13] = { '\0' };
char s_cnp[4] = { '\0' };
/*
* with sprintf you need to make sure there's enough space
* declared in the array
*/
sprintf( s_cp, "%.2f", d_n );
printf( "%s\n", s_cp );
/*
* snprinft allows to control how much is read into array.
* it might have portable issues if you are not using C99
*/
snprintf( s_cnp, sizeof s_cnp - 1 , "%f", d_n );
printf( "%s\n", s_cnp );
getchar();
return 0;
}
/* output :
* 123.45
* 123
*/
于 2012-06-05T10:31:05.023 回答
-1

With something like

def f(a):
    b=0
    while a != int(a): a*=2; b+=1
    return a, b

(which is Python) you should be able to get mantissa and exponent in a loss-free way.

In C, this would probably be

struct float_decomp {
    float mantissa;
    int exponent;
}

struct float_decomp decomp(float x)
{
    struct float_decomp ret = { .mantissa = x, .exponent = 0};
    while x != floor(x) {
        ret.mantissa *= 2;
        ret.exponent += 1;
    }
    return ret;
}

But be aware that still not all values can be represented in that way, it is just a quick shot which should give the idea, but probably needs improvement.

于 2012-06-05T10:58:29.087 回答