IEEE 浮点数到精确的 base10 字符串

Posted

技术标签:

【中文标题】IEEE 浮点数到精确的 base10 字符串【英文标题】:IEEE floating-point number to exact base10 character string 【发布时间】:2015-09-17 21:58:24 【问题描述】:

如果value 是IEEE 单精度浮点数(C/C++ 浮点数),printf('%.9e', value) 是否总是打印value 的精确base10 表示?

如果 value 是 IEEE 双精度浮点数(C/C++ 双精度),printf('%.17e', value) 是否同样适用?

如果没有,我该怎么办?

看来printf('%.17f', value)printf('%.17g', value) 不会。

【问题讨论】:

我认为每个 base2 数字都可以完全表示为 base10 数字,所以我认为两者兼而有之。我知道并非每个 base10 数字都可以完全表示为 base2 数字,但我并不担心。我假设该数字已经作为浮点数或双精度数的 base2 存在。我实际上不确定如何展示示例。 一个 ieee754 单精度浮点数具有 23 位精度,而 10 仅具有两倍的幂次方,所以我希望可以找到一个需要 23 个有效十进制数字的单精度浮点数准确地表示。 必读:What every computer scientist should know about floating point. @ThomasMatthews:这与问题有什么关系? @OliverCharlesworth:它解释了如何解释任何值的exact base 10 表示,强调exact 【参考方案1】:

printf('%.9e', value) 是否总是打印精确的 base10 表示?

没有。考虑 0.5, 0.25, 0.125, 0.0625 ...。每个值都是前面的二分之一,并且每递减 2 的幂都需要另一个小数位。

float,通常binary32 可以表示大约pow(2,-127) 和亚法线的值甚至更小。精确表示这些需要 127 位以上的小数位。即使只计算重要位,数字也是 89+。一台机器上的示例FLT_MIN完全正确

0.000000000000000000000000000000000000011754943508222875079687365372222456778186655567720875215087517062784172594547271728515625

FLT_TRUE_MIN,最小的非零次正规是151位:

0.00000000000000000000000000000000000000000000140129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125

相比之下,FLT_MAX 只需要 39 位。

340282346638528859811704183484516925440

很少需要精确十进制表示的float。将它们打印到FLT_DECIMAL_DIG(通常为 9)有效数字足以唯一地显示它们。许多系统不会打印超过几十个有效数字的精确十进制表示。

我使用过的绝大多数系统都将float/double 打印到至少DBL_DIG 有效数字(通常为15+)。大多数系统至少对DBL_DECIMAL_DIG(通常为17+)有效数字这样做。

Printf width specifier to maintain precision of floating-point value 涉及这些问题。

printf('%.*e', FLT_DECIMAL_DIG - 1, value) 会将float 打印到足够多的小数位以扫描回来并获得相同的值 - (往返)。

【讨论】:

我想我明白了。例如,FLT_MIN 正好是 base10 中的 0.000000000000000000000000000000000000011754943508222875079687365372222456778186655567720875215087517062784172594547271728515625。但是没有其他单精度浮点数可以表示0.0000000000000000000000000000000000000117549435,即前 9 位有效数字。所以如果我只打印那些,我仍然可以取回 FLT_MIN 的原始确切位。 @Patrick Yes,: 注意:前面和后面的floats 是0.000...000117549421069...0.000...000117549449095... @Patrick 注意:printf('%.9e', value)value 打印到 10 位有效十进制数字。 这是为什么呢?有效十进制数字究竟是什么意思?你是说我只需要printf('%.8e', value) 因为小数点前总会有一个数字加上'%e',这很重要吗?【参考方案2】:

this Wikipedia article 中解释了 32 位浮点数的 IEEE-754 格式。

下表显示了每个位的位权重,假设指数为 0,表示1.0 <= N < 2.0。表中最后一个数是小于 2.0 的最大数。

从表格中可以看出,需要在小数点后打印至少 23 位才能从 32 位浮点数中得到准确的十进制数。

3f800000 1.0000000000000000000000000   (1)
3fc00000 1.5000000000000000000000000   (1 + 2^-1)
3fa00000 1.2500000000000000000000000   (1 + 2^-2)
3f900000 1.1250000000000000000000000   (1 + 2^-3)
3f880000 1.0625000000000000000000000   (1 + 2^-4)
3f840000 1.0312500000000000000000000   (1 + 2^-5)
3f820000 1.0156250000000000000000000   (1 + 2^-6)
3f810000 1.0078125000000000000000000   (1 + 2^-7)
3f808000 1.0039062500000000000000000   (1 + 2^-8)
3f804000 1.0019531250000000000000000   (1 + 2^-9)
3f802000 1.0009765625000000000000000   (1 + 2^-10)
3f801000 1.0004882812500000000000000   (1 + 2^-11)
3f800800 1.0002441406250000000000000   (1 + 2^-12)
3f800400 1.0001220703125000000000000   (1 + 2^-13)
3f800200 1.0000610351562500000000000   (1 + 2^-14)
3f800100 1.0000305175781250000000000   (1 + 2^-15)
3f800080 1.0000152587890625000000000   (1 + 2^-16)
3f800040 1.0000076293945312500000000   (1 + 2^-17)
3f800020 1.0000038146972656250000000   (1 + 2^-18)
3f800010 1.0000019073486328125000000   (1 + 2^-19)
3f800008 1.0000009536743164062500000   (1 + 2^-20)
3f800004 1.0000004768371582031250000   (1 + 2^-21)
3f800002 1.0000002384185791015625000   (1 + 2^-22)
3f800001 1.0000001192092895507812500   (1 + 2^-23)

3fffffff 1.9999998807907104492187500

关于这一点需要注意的是,1到2之间只有2^23(约800万)个浮点值。但是,有10^23个小数点后23位的数字,所以小数点很少数字具有精确的浮点表示。

举个简单的例子,数字 1.1有精确的表示。最接近 1.1 的两个 32 位浮点值是

3f8ccccc 1.0999999046325683593750000
3f8ccccd 1.1000000238418579101562500

【讨论】:

我不担心从 base10 到 base2。我注意到在列表中,除了最后一个,小数点后的非零数字不超过 17 个? 我认为混淆在哪里,例如在单点情况下,给定一个浮点数及其无符号表示(最接近的 9 位小数),将无符号表示更改为 1 只会更改6th 小数位。 (例如,123.456 (123.456001) 最接近的无符号是 1123477881。更改为 1,1123477882 会更改 123.456008911 的浮点数) @Patrick "...小数点后不超过 17 个非零数字?" 没错,但请记住,这些数字中的每一个都恰好设置了 1 位分数。表格左侧的数字是数字的十六进制表示。 3f8 表示指数为 0,其余位为小数。您可以在分数中包含任意位组合。例如,0x3f820001 将在小数点后有 22 个非零数字,您可以通过添加以 0x3f820000 和 0x3f800001 开头的两行来查看。结果是 1.01562511920928955078125。 我想我对以下语句感到困惑:“如果将 IEEE 754 单精度转换为具有至少 9 个有效十进制数字的十进制字符串,然后再转换回单精度,则最终数字必须匹配原件。” @Patrick 转换为十进制字符串时,会四舍五入到具有 9 个有效数字的最接近的十进制值。转换回二进制时,您四舍五入到最接近的具有 23 个有效位的二进制数。您引用的声明中的保证是舍入错误不会导致与您开始时的值不同。

以上是关于IEEE 浮点数到精确的 base10 字符串的主要内容,如果未能解决你的问题,请参考以下文章

IEEE 754 二进制浮点数不精确

精度浮点型数据精确到了几位小数呢?

深入理解计算机系统(2.7)------二进制小数和IEEE浮点标准

深入理解计算机系统(2.7)------二进制小数和IEEE浮点标准

将浮点数转换为字符串而不会丢失精度

将十进制数表示成ieee754标准的32浮点规格化数 27/64