flipper has asked for the wisdom of the Perl Monks concerning the following question:

Hi monks,

I have a floating point number, and a certain number of characters to print it in. I want the number to use all of the available area (padded with spaces), and display the maximum precision that will fit.

From perldoc -f sprintf, it sounds like  %.${WIDTH}g is on the right tracks, though the decimal point may not be included in the calculation:

You can specify a precision (for numeric conversions) or a maximum width (for string conversions) by specifying a . followed by a number

...

For "g" and "G", this specifies the maximum number of digits to show, including thoe prior to the decimal point and those after it

However, when I use %.6g, I get output up to 8 characters long. Brute force script:

for $format (qw{ %.6g %6.6g %6g %.6f %6.6f %6f}){ print "\n$format\n"; for (12344.99, 1.12345678, 0.000000123, 0.12345678, 123.45678, + 123456, 12345.678901){ $str = sprintf "$format",$_; print length($str)." - '$str'\n"; } }

output

%.6g 5 - '12345' 7 - '1.12346' 8 - '1.23e-07' 8 - '0.123457' 7 - '123.457' 6 - '123456' 7 - '12345.7' %6.6g 6 - ' 12345' 7 - '1.12346' 8 - '1.23e-07' 8 - '0.123457' 7 - '123.457' 6 - '123456' 7 - '12345.7' %6g 6 - ' 12345' 7 - '1.12346' 8 - '1.23e-07' 8 - '0.123457' 7 - '123.457' 6 - '123456' 7 - '12345.7' %.6f 12 - '12344.990000' 8 - '1.123457' 8 - '0.000000' 8 - '0.123457' 10 - '123.456780' 13 - '123456.000000' 12 - '12345.678901' %6.6f 12 - '12344.990000' 8 - '1.123457' 8 - '0.000000' 8 - '0.123457' 10 - '123.456780' 13 - '123456.000000' 12 - '12345.678901' %6f 12 - '12344.990000' 8 - '1.123457' 8 - '0.000000' 8 - '0.123457' 10 - '123.456780' 13 - '123456.000000' 12 - '12345.678901'

Thanks!

Replies are listed 'Best First'.
Re: printf exact field width of floating point number
by BrowserUk (Patriarch) on Mar 28, 2012 at 09:35 UTC

    The format specifier %8.6f says place the number in an 8 char wide space and include 6 digits of decimals. One of the two extra spaces is occupied by the decimal point; the last is used for the integer part of the value. Which in this case will be restricted to a single digit before the field width will be overridden.

    A specifier of %6.6f doesn;t make much sense because it leaves no room for the decimal point or integer part of the number.

    For the most flexible representation, use %12.6g (or larger) which will make a reasonable fist of displaying a fairly wide range of values in a semi consistant fashion:

    [0] Perl> printf "%12.6g\n", $_ for map{ "1.23456e$_" } -12 .. +12;; 1.23456e-012 1.23456e-011 1.23456e-010 1.23456e-009 1.23456e-008 1.23456e-007 1.23456e-006 1.23456e-005 0.000123456 0.00123456 0.0123456 0.123456 1.23456 12.3456 123.456 1234.56 12345.6 123456 1.23456e+006 1.23456e+007 1.23456e+008 1.23456e+009 1.23456e+010 1.23456e+011 1.23456e+012

    The floating point formatting by sprintf has always -- going way back to the earliest of C compilers -- left a lot to be desired.

    You might also find the threads at Engineering FP notation & sprintf and Display floating point numbers in compact, fixed-width format of interest.


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

    The start of some sanity?

      Yuck!

      Thanks explains perfectly, thank you!

      The floating point formatting by sprintf has always -- going way back to the earliest of C compilers -- left a lot to be desired.

      Indeed. I don't use printf that often, so I tend to forget that the number preceding the decimal point specifies how many total characters there are, instead of how many digits before the decimal point, which seems more intuitive to me. So I too often try something like %3.2f, expecting that to handle numbers like 123.45, before remembering that the first digit has to include the length of the second (and the decimal point).

      Aaron B.
      My Woefully Neglected Blog, where I occasionally mention Perl.

Re: printf exact field width of floating point number
by Marshall (Canon) on Mar 28, 2012 at 09:32 UTC
    In a printf statement, the format spec specifies a minimum width - just like in 'C'. If you ask for something to be printed that exceeds that width, it will be printed. I don't often (well almost never use the %g) - the %f format spec takes a width (including the decimal place) and a number of decimal points.

    Your question implies that you are willing to lose precision in order to fit within the number of spaces? This is rather bizarre because a fixed field number format like you describe is rare - more common even in 'C' is a space separated number field.

    Perl does have the sprintf() function. That may help. Sounds like you want to print a number, if that number cannot "fit", then truncate digits, then if it still can't fit, then use exponential notation. Try sprintf() with %f format, if that is too long, then switch to %g format.

Re: printf exact field width of floating point number
by Anonymous Monk on Mar 28, 2012 at 16:42 UTC
    It is significant overkill, but Number::WithError can be coerced into writing floats to a fixed precision. Have Number::WithError "round" the number, stringize it, and split the string to extract the number.