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
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.
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.