Go %g中带有宽度和精度字段的fmt.Printf的行为异常

Go %g中带有宽度和精度字段的fmt.Printf的行为异常,go,printf,Go,Printf,我正在尝试使用fmt.Printf()将一些浮点数格式化为相同的宽度 例如,给定浮点值0.06060606、0.3333333、0.05、0.4和0.181818,我希望将每个值格式化为10个符文: 0.06060606 0.33333333 0.05 0.4 0.18181818 但我不明白是怎么做到的。文件上说, 对于浮点值,“宽度”设置字段的最小宽度 和精度设置小数点后的位数,如果 适当,但对于%g/%g,它设置了总位数。 例如,给定123.45,格式%6.2f

我正在尝试使用
fmt.Printf()
将一些浮点数格式化为相同的宽度

例如,给定浮点值0.06060606、0.3333333、0.05、0.4和0.181818,我希望将每个值格式化为10个符文:

0.06060606
0.33333333
      0.05
       0.4
0.18181818
但我不明白是怎么做到的。文件上说,

对于浮点值,“宽度”设置字段的最小宽度 和精度设置小数点后的位数,如果 适当,但对于%g/%g,它设置了总位数。 例如,给定123.45,格式%6.2f打印123.45,而%.4g打印 打印123.5。%e和%f的默认精度为6;对于%g,它是 识别值所需的最小位数 独一无二的

因此,如果我使用
%f
一个较大的数字将不适合10个字符的约束,因此需要
%g
。获得10的最小宽度是
%10g
,获得9位数的最大值(+1代表点)是
%.9g
,但将它们组合在
%10.9g
中并不像我预期的那样

0.0606060606
0.333333333
      0.05
       0.4
0.181818182
为什么我得到的字符串是10个符文,其他的是11个符文,其他的是12个符文


特别是,
%.9g
似乎总共不产生9位数字。参见示例:

是的,我同意:它优先于“精度字段”,而不是“宽度”。
因此,当我们需要修复打印列时,我们需要编写新的格式化函数。

首先,我们需要正确理解文档:

宽度设置字段的最小宽度,精度设置小数点后的位数(如果适用),但%g/%g设置的是总位数

这一行在语法上是正确的,但这句话最后一部分的it确实令人困惑:它实际上指的是精度,而不是宽度

因此,让我们看一些例子:

123.45
12312.2
1.6069
0.6069
0.0006069
您可以像
fmt.Printf(“%.4g”)
那样打印它,它会给您

123.5
1.231e+04
1.607
0.6069
0.0006069
只有4位,不包括所有小数点和指数。但是等等,最后2个例子会发生什么?你在开玩笑吧,那不是超过5位数吗

这是打印中容易混淆的部分:前导0不会被计算为数字,并且在少于4个零时不会缩小

让我们使用下面的示例来了解0的行为:

package main

import "fmt"

func main() {
    fmt.Printf("%.4g\n", 0.12345)
    fmt.Printf("%.4g\n", 0.012345)
    fmt.Printf("%.4g\n", 0.0012345)
    fmt.Printf("%.4g\n", 0.00012345)
    fmt.Printf("%.4g\n", 0.000012345)
    fmt.Printf("%.4g\n", 0.0000012345)
    fmt.Printf("%.4g\n", 0.00000012345)

    fmt.Printf("%g\n", 0.12345)
    fmt.Printf("%g\n", 0.012345)
    fmt.Printf("%g\n", 0.0012345)
    fmt.Printf("%g\n", 0.00012345)
    fmt.Printf("%g\n", 0.000012345)
    fmt.Printf("%g\n", 0.0000012345)
    fmt.Printf("%g\n", 0.00000012345)
}
以及输出:

0.1235
0.01235
0.001234
0.0001234
1.234e-05
1.234e-06
1.235e-07
0.12345
0.012345
0.0012345
0.00012345
1.2345e-05
1.2345e-06
1.2345e-07
因此,您可以看到,当前导0少于4时,它们将被计数,如果前导0多于4,它们将被缩小

好的,接下来是
宽度。从文档中,
width
仅指定最小宽度,包括小数位和指数。这意味着,如果您的位数超过指定的
宽度
,它将超出宽度

请记住,宽度将被视为最后一步,这意味着它需要首先满足精度字段的要求

让我们回到你的案子。您指定了
%10.9g
,这意味着您希望总数字为9,不包括前导的
0
,最小宽度为
10
,包括小数位和指数,精度应优先

0.060606
:不带前导0的9位数字将给出
0.0606
,因为它已经是12个宽度,它通过了最小宽度10

0.3333333
:不带前导0的9位数字将给出
0.333333
,因为它已经是11个宽度,它通过了最小宽度10

0.05
:不带前导0的9位数字将为您提供
0.05
,因为它小于宽度10,所以它将用另外6个宽度填充以获得宽度10

0.4
:同上

0.181818
:不带前导0的9位数字将给出
0.1818182
四舍五入,因为它已经是11个宽度,所以它通过了最小宽度10


因此,这就解释了为什么要进行有趣的打印。

精度(如
%.9g
)是科学符号中的位数(即忽略前导零)。这并没有很好的文档记录,我建议在github.com/golang/go上提交一个bug。宽度(如
%10g
)是根据文档输出的最小符文数。因此,输出看起来是正确的。啊,因此,
0.000123456789
仍然使用13位数字进行格式化,使用
%.9g
!该死,从文件上看不清楚。。。“总位数”有点误导人@AkiRoss您是对的,Go的官方文档在这方面确实令人困惑:(我相信这个问题会帮助很多人。我希望它会有用。总的来说,我觉得格式化不是一个简单的话题。以下是与问题报告相关的/可能重复的简单答案是“%10.5g”但是它不会返回浮点数的最大有效位数OP是在问为什么,而不是在寻找解决方案:pYes,我同意。我认为Printf的fmt库需要重写以提供更多有效位数。对于提供16位有效位数的float64,出于任何原因丢失有效位数都有点奇怪.并在%L.Dg中对长度提交多一点尊重