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.0e-999 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.0e-999',
-10 => '+1.00e-99',
-5 => '+1.000e-9',
-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.2345678e-99
}
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($eabs-1) . $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;
}
}
}
-
Are you posting in the right place? Check out Where do I post X? to know for sure.
-
Posts may use any of the Perl Monks Approved HTML tags. Currently these include the following:
<code> <a> <b> <big>
<blockquote> <br /> <dd>
<dl> <dt> <em> <font>
<h1> <h2> <h3> <h4>
<h5> <h6> <hr /> <i>
<li> <nbsp> <ol> <p>
<small> <strike> <strong>
<sub> <sup> <table>
<td> <th> <tr> <tt>
<u> <ul>
-
Snippets of code should be wrapped in
<code> tags not
<pre> tags. In fact, <pre>
tags should generally be avoided. If they must
be used, extreme care should be
taken to ensure that their contents do not
have long lines (<70 chars), in order to prevent
horizontal scrolling (and possible janitor
intervention).
-
Want more info? How to link
or How to display code and escape characters
are good places to start.