Description: 
I have yet to find a display format for arbitrary floating point numbers that I like much. I wanted something closer to "Engineering notation" (exponents are always a multiple of 3) but this works for now.
 Number always take up 9 characters
 Perl doesn't handle any numbers too large or too small to fit (you'd have to go smaller than 1.0e999 or larger than 9.99e999)
 Numbers of similar magnitude line up their decimal points
The code is tested but could probably be improved a great deal.
I'd like to be able to make the width a parameter (instead of fixed at 9). Being able to go as low as 7 (or even 6) would be way cool.

#!/usr/bin/perl w
use strict;
Main();
exit( 0 );
{
my( %fmt4exp, @exps2fmt, $fullfmt );
BEGIN {
%fmt4exp= (
100 => '+1.0e999',
10 => '+1.00e99',
5 => '+1.000e9',
1 => '+0.000000',
+3 => ' +0.000',
+5 => ' +0.0',
+7 => ' +0',
+9 => '+1.000e+9',
+99 => '+1.000e99',
+99999 => '+1.00e999',
);
@exps2fmt= sort {$a<=>$b} keys %fmt4exp;
$fullfmt= '%+14.7e'; # %+1.2345678e99
}
sub Num2Str {
my( $num )= @_;
my $full= sprintf $fullfmt, $num;
my( $sign, $one, $rest, $esign, $eabs )=
$full =~ m<
^\s*
([+]?)(\d)\.(\d*)
[eE]([+]?)(\d+)
\s*$
>x;
my $exp= $esign . $eabs;
my $fmt;
for my $exp2fmt ( @exps2fmt ) {
if( $exp <= $exp2fmt ) {
$fmt= $fmt4exp{$exp2fmt};
last;
}
}
my $str= $fmt;
if( $fmt =~ /e/ ) {
$str =~ s/\+/$sign/;
$str =~ s/1/$one/;
$str =~ s{\.(0+)(?=[eE])}{
( sprintf( "%14.".length($1)."e", $num )
=~ /(\.\d+)/
)[0]
}e;
$str =~ s/(9+)/sprintf "%0".length($1)."d", $eabs/e;
} else {
if( $exp < 0 ) {
$str =~ s/\+/$sign/;
$rest= '0'x($eabs1) . $one . $rest;
} else {
$one= substr( $one . $rest, 0, 1+$exp );
substr( $rest, 0, $exp )= '';
$str =~ s/([\s+0]+)/sprintf "%+".length($1)."s", $sign
+.$one/e;
}
$str =~ s{\.(0+)}{
( sprintf( "%14.".length($1)."f", $num )
=~ /(\.\d+)$/
)[0]
}e;
$str =~ s{\.(0+)$}{
( sprintf( "%14.".length($1)."f", $num )
=~ /(\.\d+)/
)[0]
}e;
$str =~ s/(\.?0+)$/' ' x length($1)/e
if $fmt =~ /\./;
}
return $str;
}
}
sub Main {
for my $exp ( 101..98, 11, 10..11, 98..101 ) {
for my $sign ( '', '' ) {
my $num= 0 + ( $sign . "5.555555555e" . $exp );
printf "%20s (%s)\n", $num, Num2Str($num);
}
}
for my $exp ( 10..11 ) {
for my $sign ( '', '' ) {
my $num= 0 + ( $sign . "1e" . $exp );
printf "%20s (%s)\n", $num, Num2Str($num);
printf "%20s (%s)\n", 0, Num2Str(0)
if 1 == $num;
}
}
}

Re: Display floating point numbers in compact, fixedwidth format by BrowserUk (Pope) on Sep 24, 2003 at 08:22 UTC 
...
0.05555555555 (+0.055556)
0.05555555555 (0.055556)
0.5555555555 ( +0.556)
0.5555555555 ( 0.556)
...
I realise that the answer could just be, "Because that's what I want!", but why suddenly start throwing away precision in favour of whitespace? And why at this point?
Examine what is said, not who speaks.
"Efficiency is intelligent laziness." David Dunham
"When I'm working on a problem, I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong." Richard Buckminster Fuller
If I understand your problem, I can solve it! Of course, the same can be said for you.
 [reply] [d/l] 

2 => '+0.000000',
to
1 => '+0.000000',
and added some more test cases (which found another bug that I've fixed  stripping trailing '0's when I shouldn't). Thanks for catching that.
At which points to change format are partially a matter of taste. The first three formats have very little room for matters of taste:
100 => '+1.0e999',
10 => '+1.00e99',
5 => '+1.000e9',
(the most likely change would be to change "5", probably to something a bit closer to zero).
The last three have only slightly more room:
+9 => '+1.000e+9',
+99 => '+1.000e99',
+99999 => '+1.00e999',
but the middle ground leaves lots of room for tradeoffs. In particular, I wanted small integers to line up nicely (I chose 9999..9999 for my definition of "small") and I wanted a +0.0 format so that we can tell nearintegers from nonintegers for as long as possible. The +0.000000 and +0 formats allow us to delay going to 'e' format for as long as possible.
Finally, add in a desire for fewer formats so that numbers are more likely to line up at their (perhaps implied) decimal points, and I'm stuck with what I used above. (:
 tye  [reply] [d/l] [select] 
Re: Display floating point numbers in compact, fixedwidth format by particle (Vicar) on Sep 26, 2003 at 14:01 UTC 
I wanted something closer to "Engineering notation".... I'd like to be able to make the width a parameter (instead of fixed at 9). Being able to go as low as 7 (or even 6) would be way cool.
this code should meet those requirements. at least, the mantissa width is fixed, the exponent width varies. it should be easy enough to modify this code to suit your needs, if you wish.
i used your test suite, too.
#!/usr/bin/perl
use strict;
use warnings;
$++;
Main();
exit;
## adapted from code found at: http://www.cs.tut.fi/~jkorpela/c/eng.ht
+ml
sub eng
{
my( $num, $digits )= @_;
## default to smallest number of digits allowing fixed width manti
+ssa (4)
$digits= defined $digits && 3 < $digits
? $digits
: 4;
my $neg;
if( 0 > $num )
{
$neg= 'true';
$num= $num;
}
0 == $num and return sprintf '+%.*fe+%s' => $digits  1, $num, 0;
my $exp= 0 != $num
## perl's log() is natural log, convert to common log
? int( log($num) / log(10) )
## shortcircuit: can't do log(0)
: 0;
## tricky integer casting ahead...
$exp= 0 < $exp
? int( ( int( $exp / 3 ) ) * 3 )
: int( int( ( $exp + 3 ) / 3 ) * 3 );
$num *= 10 ** $exp;
if( 1000 <= $num )
{
$num /= 1000;
$exp += 3;
}
elsif( 100 <= $num )
{
$digits = 2;
}
elsif( 10 <= $num )
{
$digits = 1;
}
0 <= $exp
and $exp= '+' . $exp;
return ( $neg ? '' : '+' )
. sprintf '%.*fe%s' => $digits  1, $num, $exp;
}
sub Main
{
my $digits= 2;
for my $exp ( 101..98, 11, 10..11, 98..101 )
{
for my $sign ( '', '' )
{
my $num= 0 + ( $sign . "5.555555555e" . $exp );
printf "%20s (%s)\n", $num, eng( $num, $digits );
}
}
for my $exp ( 10..11 )
{
for my $sign ( '', '' )
{
my $num= 0 + ( $sign . "1e" . $exp );
printf "%20s (%s)\n", $num, eng( $num, $digits );
printf "%20s (%s)\n", 0, eng( 0, $digits )
if 1 == $num;
}
}
}
~Particle *accelerates*  [reply] [d/l] 
Back to Snippets Section

