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

Anyone any thoughts how to pursuade sprintf to format floats in Engineering notation rather than scientific notation?


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.
RIP PCW It is as I've been saying!(Audio until 20090817)

Replies are listed 'Best First'.
Re: Engineering FP notation & sprintf
by GrandFather (Saint) on Aug 27, 2009 at 02:12 UTC

    Inspired by toolic's code (toolic is of course free to use this in his module):

    use warnings; use strict; use POSIX; for my $num (0, -0.1, 0.1, 999.99, 1000, -999.99, -1000, 1.234e34, 1.2 +34e-34){ printf "%-10s %-10s\n", $num, asEng ($num); } sub asEng { my ($num) = @_; return '0' if +$num eq '0'; my $sign = ($num < 0) ? '-' : ''; $num = abs $num; my $e = floor( log($num) / log(1000) ); my $mult = 1000**$e; $e *= 3; $num = $num / $mult; return "$sign${num}e$e"; }

    Prints:

    0 0 -0.1 -100e-3 0.1 100e-3 999.99 999.99e0 1000 1e3 -999.99 -999.99e0 -1000 -1e3 1.234e+034 12.34e33 1.234e-034 123.4e-36

    True laziness is hard work
Re: Engineering FP notation & sprintf
by toolic (Bishop) on Aug 27, 2009 at 01:33 UTC
    I've had this code sitting around for a while. I've been contemplating pushing it out to CPAN one of these days:
    use warnings; use strict; use Carp; use POSIX; use Scalar::Util qw(looks_like_number); # this is just test junk... print "0xab --> ", engnot(0xab), "\n"; pp(7777); pp(0x55); pp( 2 / 3 ); pp(-0.04567); for my $n ( 0, 54321, 1, 10, 10_000, 0.9999, 666e27, 555e-30, 0xff ) { + print "$n --> ", engnot($n), "\n" } sub pp { my $n = shift; print "$n --> ", engnot($n), "\n" } # here's the actual code sub engnot { my $num = shift; looks_like_number($num) or croak "$num not a number\n"; my $sign = ($num < 0) ? '-' : ''; $num = abs $num; if ( ($num >= 1e27) or ($num <= 1e-27) ) { return sprintf '%e', $num; } my %prefix = ( '-8' => 'y', '8' => 'Y', '-7' => 'z', '7' => 'Z', '-6' => 'a', '6' => 'E', '-5' => 'f', '5' => 'P', '-4' => 'p', '4' => 'T', '-3' => 'n', '3' => 'G', '-2' => 'u', '2' => 'M', '-1' => 'm', '1' => 'k', '0' => '' ); my $e = floor( log($num) / log(1000) ); my $mult = 1000**$e; $num = $num / $mult; return $sign . $num . $prefix{$e}; } __END__ output... 0xab --> 171 7777 --> 7.777k 85 --> 85 0.666666666666667 --> 666.666666666667m -0.04567 --> -45.67m 0 --> 0.000000e+00 54321 --> 54.321k 1 --> 1 10 --> 10 10000 --> 10k 0.9999 --> 999.9m 6.66e+29 --> 6.660000e+29 5.55e-28 --> 5.550000e-28 255 --> 255

    Update: See also String numerifier with SI prefix support and Number::Format

      Hm. That's a different interpretation of "engineering notation". The basic requirement is that the characteristic (exponent) is always a power of 3. Eg. 6.66e+29 --> 6.660000e+29 should be 666.0e27.

      Other examples:

      • 1.23e2 -> 123 or (0.123e3).
      • 123.4 -> as is (or 12.34e3) (Thanks, ambrus for pointing out the error!).
      • 1234.56 -> 1.23456e3 (or as is).
      • 1234567 -> 1.234567e6 (or 123.4567e3).

      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.
        For future reference... I finally found some time to package up the ideas in this thread and upload to CPAN
        use warnings; use strict; use Number::FormatEng qw(format_eng); for my $num qw(1.23e2 1.234 1234.56 1234567 6.66e+29) { printf "%-10s -> %-10s\n", $num, format_eng($num); } __END__ 1.23e2 -> 123 1.234 -> 1.234 1234.56 -> 1.23456e3 1234567 -> 1.234567e6 6.66e+29 -> 666e27
        Comments and reviews are more than welcome.