doujiang5211
2016-04-06 23:25 阅读 48
已采纳

具有%g中的width和precision字段的fmt.Printf表现异常

I am trying to get some floats formatted with the same width using fmt.Printf().

For example, given the float values 0.0606060606060606, 0.3333333333333333, 0.05, 0.4 and 0.1818181818181818, I would like to get each value formatted in, say, 10 runes:

0.06060606
0.33333333
      0.05
       0.4
0.18181818

But I can't understand how it's done. Documentation says that

For floating-point values, width sets the minimum width of the field and precision sets the number of places after the decimal, if appropriate, except that for %g/%G it sets the total number of digits. For example, given 123.45 the format %6.2f prints 123.45 while %.4g prints 123.5. The default precision for %e and %f is 6; for %g it is the smallest number of digits necessary to identify the value uniquely.

So, if I use %f a larger number will not fit in 10-character constraint, therefore %g is required. To get a minimum width of 10 is %10g and to get a maximum number of 9 digits (+1 for the dot) it's %.9g, but combining them in %10.9g is not behaving as I expect

0.0606060606
0.333333333
      0.05
       0.4
0.181818182

How come I get strings which are of 10 runes, others that are 11 runes and others that are 12 runes?

In particular, it seems that %.9g does not produce 9 digits in total. See for example: http://play.golang.org/p/ie9k8bYC7r

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享

2条回答 默认 最新

  • 已采纳
    ds122455 ds122455 2016-04-07 06:49

    Firstly, we need to understand the documentation correctly:

    width sets the minimum width of the field and precision sets the number of places after the decimal, if appropriate, except that for %g/%G it sets the total number of digits.

    This line is grammatically correct, but the it in the last part of this sentence is really confusing: it actually refers to the precision, not the width.

    Therefore, let's look at some examples:

    123.45
    12312.2
    1.6069
    0.6069
    0.0006069
    

    and you print it like fmt.Printf("%.4g"), it gives you

    123.5
    1.231e+04
    1.607
    0.6069
    0.0006069
    

    only 4 digits, excluding all decimal points and exponent. But wait, what happens to the last 2 example? Are you kidding me isn't that more than 5 digits?

    This is the confusing part in printing: leading 0s won't be counted as digits, and won't be shrunk when there are less than 4 zeros.

    Let's look at 0 behavior using the example below:

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

    and the output:

    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
    

    So you could see, when there are less than 4 leading 0s, they will be counted, and be shrunk if there are more than that.

    Ok, next thing is the width. From the documentation, width only specifies the minimum width, including decimal place and exponent. Which means, if you have more digits than what width specified, it will shoot out of the width.

    Remember, width will be taken account as the last step, which means it needs to first satisfy the precision field.

    Let's go back to your case. You specified %10.9g, that means you want a total digit of 9, excluding the leading 0, and a min width of 10 including decimal place and exponent, and the precision should take priority.

    0.0606060606060606: take 9 digits without leading 0 will give you 0.0606060606, since it's already 12 width, it passes the min width of 10;

    0.3333333333333333: take 9 digits without leading 0 will give you 0.333333333, since it's already 11 width, it passes the min width of 10;

    0.05: take 9 digits without leading 0 will give you 0.05, since it's less than width 10, it will pad with another 6 width to get width of 10;

    0.4: same as above;

    0.1818181818181818: take 9 digits without leading 0 will give you 0.181818182 with rounding, since it's already 11 width, it passes the min width of 10.

    So this explains why you got the funny printing.

    点赞 评论 复制链接分享
  • dongzhuandian3292 dongzhuandian3292 2016-04-07 02:00

    Yes, I agree: it gives precedence to the "precision fields" not to "width". So when we need fix columns for printing we need write new formatting func.

    点赞 评论 复制链接分享

相关推荐