Floating-point formatting

Arjen Markus 2003-02-11: I had a question from a customer about numbers that were displayed with too many digits, like 28.000 instead of 28.0. So, this raised the question: what can we do to make the appearance of (floating-point) numbers more elegant?

As it turns out, format has a lot of options, one of them is the # attribute to %g. Now, if all you have is the textual description of the effects, you will be very puzzled indeed.

So, here is a little script that examines the effects:

# Check the effects of the various formats for floating-point numbers
#
foreach value {1.0 -1.0 1.01 -1.01 0.00001 -0.00001 1000000.0 -1000000.0} {
    set result {} 
    foreach form {%g %-g %6g %6.4g %#.4g %06g %-6g} {
        append result [format " >$form<" $value]
    }
    puts "Value $value: $result"
}

I have reformatted the output a bit, to get the following table:

Formats: %g %-g %6g %6.4g %#.4g %06g %-6g
Value 1.0: >1< >1< > 1< > 1< >1.000< >000001< >1 <
Value -1.0: >-1< >-1< > -1< > -1< >-1.000< >-00001< >-1 <
Value 1.01: >1.01< >1.01< > 1.01< > 1.01< >1.010< >001.01< >1.01 <
Value -1.01: >-1.01< >-1.01< > -1.01< > -1.01< >-1.010< >-01.01< >-1.01 <
Value 0.00001: >1e-05< >1e-05< > 1e-05< > 1e-05< >1.000e-05< >01e-05< >1e-05 <
Value -0.00001: >-1e-05< >-1e-05< >-1e-05< >-1e-05< >-1.000e-05< >-1e-05< >-1e-05<
Value 1000000.0: >1e+06< >1e+06< > 1e+06< > 1e+06< >1.000e+06< >01e+06< >1e-06 <
Value -1000000.0: >-1e+06< >-1e+06< >-1e+06< >-1e+06< >-1.000e+06< >-1e+06< >-1e+06<

However, there's more! The full format specification is huge and very, very complex. Here it is, bit by bit, aimed at people doing float formatting...

(AM That complexity is precisely the reason to have this page - so you can see the effects :))

  • Start with a % symbol.
  • Add any optional positional specifier:
  1. If you don't want to use the next value off the argument list, add argListIndex and $ here. This is mainly useful for localized formats.
  • Add any optional flags:
  1. If you are going to make the space taken up by the field (potentially) larger than required to always hold the value and you want the field left-justified, add a - here.
  2. If you always want a sign, add a + here.
  3. If you want a space when the number isn't negative, add a space character here. Don't mix with the +.
  4. If you want to pad out with zeroes, not spaces, add a 0 here.
  5. If you want to use the alternate form, add a # here. This guarantees you get a decimal point and, for the g conversion guarantees you a full set of zeroes after the decimal point.
  • Add any optional field width:
  1. If you want to specify a minimum field width (as used in many of the examples above) put the (decimal) width of the field here. You can also take this value as an argument to format using a * instead.
  • Add any optional precision in either the form .number or .* (the latter if you want to take the value as an argument to format, as with the field width above):
  1. If you are using the g conversion type, you can say the maximum total number of digits to appear (excluding the exponent)
  2. If you are using the e or f conversion types, you can say the number of digits to appear after the decimal point.
  • Floats don't use length modifiers in Tcl. They're only used for integer values.
  • Finally, add the conversion specifier which marks the end of the format.
  1. f uses normal notation.
  2. e uses scientific notation.
  3. E is just like e but uses a capital letter for the exponent char.
  4. g uses either normal or scientific notation, depending on the scale of the number.
  5. G is just like g but uses a capital letter for the exponent char (if printed).

So, if we want a left formatted normal float in a gap at least 8 characters wide and to not have the start of the number jump around when going between positive and negative numbers while taking the number of values after the decimal point from a variable, you should use the format "%- 8.*f" (and yes, the space is significant.)

Shimmer-Free Formatting

In the Tcl Chatroom, 2014-07-17, AM pointed out that the following operation can be used to set the string value of a floating point number to a precision of 2 without losing its internal numeric representation:

expr {int(100*$x+0.5)/100.0}

An equivalent operation using round:

expr {round(100*$x)/100.0}

Generalized and implemented as an extension to expr:

proc ::tcl::mathfunc::precision {precision float}  {
    expr {round( 10 ** $precision * $float) / (10.0 ** $precision)} 
}

See Also

Arts and crafts of Tcl-Tk programming