例如,使用IEEE-754 32位二进制浮点,让我们表示1 / 3的值.虽然不能精确计算,但0x3eaaaaab生成的值与1 / 3最接近.您可能希望以十进制形式写入值,并让编译器将十进制文字转换为二进制浮点数.

0.333333f    -> 0x3eaaaa9f (0.333332986)
0.3333333f   -> 0x3eaaaaaa (0.333333313)
0.33333333f  -> 0x3eaaaaab (0.333333343)
0.333333333f -> 0x3eaaaaab (0.333333343)

您可以看到,8位(有效)十进制数字足以表示尽可能正确的值(最接近实际值).

我用π和e(自然对数的基数)进行了测试,两者都需要8位小数才能得到最正确的结果.

3.14159f    -> 0x40490fd0 (3.14159012)
3.141593f   -> 0x40490fdc (3.14159298)
3.1415927f  -> 0x40490fdb (3.14159274)
3.14159265f -> 0x40490fdb (3.14159274)

2.71828f    -> 0x402df84d (2.71828008)
2.718282f   -> 0x402df855 (2.71828198)
2.7182818f  -> 0x402df854 (2.71828175)
2.71828183f -> 0x402df854 (2.71828175)

然而,√2似乎需要9位数字.

1.41421f     -> 0x3fb504d5 (1.41420996)
1.414214f    -> 0x3fb504f7 (1.41421402)
1.4142136f   -> 0x3fb504f4 (1.41421366)
1.41421356f  -> 0x3fb504f3 (1.41421354)
1.414213562f -> 0x3fb504f3 (1.41421354)

https://godbolt.org/z/W5vEcs695

从这些结果来看,一个包含9个有效数字的十进制浮点文字足以生成最正确的32位二进制浮点值,这可能是正确的.在实践中,如果存储额外数字的空间无关紧要,12~15位之类的数字肯定会行得通.

但我对它背后的数学很感兴趣.在这种情况下,如何确保9位数足够?对于double或任意精度,是否有一个简单的公式来推导所需的位数?


当前的答案和 comments 中的链接证实,9位数足以 payable most个 case ,但我发现了一个反例,其中9位数是不够的.事实上,十进制格式的无限精度需要始终正确地转换(四舍五入到最接近的值)为某种二进制浮点格式(IEEE-754 binary32浮点用于讨论).

9个有效十进制数字表示的8388609.4998388609.50.转换为float的数字的值为8388610.另一方面,用10或更多数字表示的数字将始终保持原始值,并且转换为float的数字的值为8388609.

你可以看到8388609.499需要超过9个数字才能最准确地转换为float.有无限多个这样的数字,以二进制浮点格式放置在非常接近两个可表示值的半点处.

推荐答案

对于双精度甚至任意精度,有没有一个简单的公式来推导所需的位数>;

来自C17§5.2.4.2.2 11 FLT_DECIMAL_DIG, DBL_DECIMAL_DIG, LDBL_DECIMAL_DIG

小数位数,n,这样,任何以p为基数的b位浮点数都可以四舍五入为具有n位小数的浮点数,然后在不改变数值的情况下返回,

pmax log10 b: if b is a power of 10
1 + pmax log10 b: otherwise


但我对它背后的数学很感兴趣.在这种情况下,如何确保9位数足够?

每个二进制浮点范围,如[1.0…2.0],[128.0…256.0],[0.125…0.5)包含2个均匀分布的p-1值.例如,对于float,p=24.

十年十进制文本的每个范围都包含10个均匀分布的n-1值,其中n个有效数字采用指数表示法,如[1.0…9.999…,[n.0f…999.999…,[0.001…0.00999…).

例子: common float:
When p is 24 with 224 combinations, n must at least 8 to form the 16,777,216 combinations to distinctly round-trip float to decimal text to float. As the end-points of two decimal ranges above may exist well within that set of 224, the larger decimal values are spaced out further apart. This necessitates a +1 decimal digit.

例子:

考虑两个相邻的float

10.000009_5367431640625
10.000010_49041748046875

两者都转换为8位有效数字十进制文本"10.000010".8是不够的.

9总是足够的,因为我们不需要超过167772160来区分16777216 float 个值.


OP也询问了大约8388609.499人.(为了简单起见,我们只考虑float.)

这个值几乎是2float个值之间的一半.

8388608.0f  // Nearest lower float value
8388609.499 // OP's constant as code
8388609.0f  // Nearest upper float value

OP reports:"你可以看到8388609.499需要超过9位数才能最准确地转换为浮点."

让我们回顾一下标题"为了尽可能正确地表示value,浮点文本*1中有效小数位数的最小数量是多少?"

这个新的问题部分强调,所讨论的value是源代码8388609.499的值,而不是它在发出的代码8388608.0f中变成的浮点常量.

如果我们认为valuefloating point constant的值,那么定义floating point constant 8388608.0f最多只需要9个有效的十进制数字.8388608.49,因为源代码已经足够了.

但是,如果要根据某个数字作为代码"是"得到最接近的floating point constant,确实需要很多位数.

考虑典型的最小值floatFLT_TRUE_MIN,精确的十进制值为:

0.00000000000000000000000000000000000000000000140129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125

在这和0.0之间的一半是0.000..(~39个零)..0007006..(~100多个数字)..15625

最后一个数字是6或4,最接近的float分别是FLT_TRUE_MIN0.0f.现在我们有一个例子,需要109个有效数字来 Select 2个可能的float个.

为了避免我们超过cliffs of insanity,IEEE-758已经解决了这个问题.

翻译(编译器)必须判断以符合该规范(不一定是C规范)的有效十进制数字的数量要有限得多,即使额外的数字可以转换为另一个FP值.

IIRC,实际上是FLT_DECIMAL_DIG + 3.因此,对于普通的float,只需判断9+3个有效的十进制数字.

(我稍后会查一些章节)


*1C没有定义:floating point literal,但定义了floating point constant,因此使用了该术语.

C++相关问答推荐

GCC:try 使用—WError或—pedantic using pragmas

从组播组地址了解收到的数据包长度

C编译器是否遵循restrict的正式定义?

当我更改编译优化时,相同的C代码以不同的方式运行

使用双指针动态分配和初始化2D数组

为什么该函数不将参数值保存到数据 struct 中?

对重叠字符串使用MemMove

等同于铁 rust 的纯C语言S未实现!()宏

将返回的char*设置为S在函数中定义的字符串文字可能会产生什么问题?

OpenSSL:如何将吊销列表与SSL_CTX_LOAD_VERIFY_LOCATIONS一起使用?

链接到底是如何工作的,我在这里到底做错了什么

收到不兼容的指针类型警告,因为函数的返回不是空*,而是 struct 指针

pthread_create的用法

如何将大写/小写土耳其字母相互转换?

是否有单独的缓冲区用于读写库调用?

Tcl_GetDoubleFromObj在列表的迭代中是一个缺点

为什么这个代码的最后一次迭代不能正常工作?

在我的函数中实现va_arg的问题

为什么argc和argv即使在主函数之外也能工作?

C Makefile - 如何避免重复提及文件名