-2

Do I really need this Workaround to keep the numerical output format from changing for small numbers in perl?

for ( 77777, 10, -15, 132, 66, 444.33, 0.0002 ) {
    my $x = $_ / 1000 / 1000;
    $x = sprintf "%.12f", $x;    # Workaround: to guard against exponential notation
    $x =~ s/0+$//;               # Workaround: to clean up from previous line
    print "$_ mm is $x km\n";
}

Note this is a more general case of Workarounds to print .00001 in perl

Dan Jacobson
  • 490
  • 3
  • 14
  • 3
    You appear to be asking how change how interpolation stringifies numbers. [You've already asked that.](https://stackoverflow.com/q/75773212/589924) If you want a different stringification, you will need to tell Perl how to stringify it (or "use a workaround", as you misname it). – ikegami Mar 21 '23 at 05:20
  • 4
    If, on the other hand, you're asking how to format the numbers according to your needs, you will need to tell us how you want to format them. We can't read your mind and more than Perl can. Your trail of cryptic postings indicate you want some form of decimal notation (as opposed to exponential notation) with no trailing zeros, but what about rounding? Do you want the numbers to be rounded at all, and if so how? – ikegami Mar 21 '23 at 05:23
  • 1
    As it stands, your approach to telling Perl how to format the numbers ("workaround") produces `0.` or `-0.` for roughly half the numbers that can be represented by IEEE doubles. And every integer (which is nearly all the rest) ends with `.` Maybe that's fine. We have no way of knowing because, again, you haven't told us what you want. – ikegami Mar 21 '23 at 05:58
  • 3
    Why do you call it a "_workaround_"? It's a specification for a printing format, telling it how you _want it_ printed. If you don't care then just `print` ... But then the interpreter (perl) needs to decide on its own how to format. Then `3` and `0.01` of course _should not_ be printed with the same precision. And it should never print 100 digits, right? How about ... 16? 15? It decides somehow. If you don't like that then use `printf` (or `sprintf`), that's what it's for. What's the problem with that? – zdim Mar 21 '23 at 06:00
  • 2
    @zdim, Re "*And it should never print 100 digits, right?*", The IEEE doubles with the most decimal places have 1074 decimal places, the first 323 of which are zeroes. – ikegami Mar 21 '23 at 07:43
  • @ikegami Wow :). (I meant to say that we don't want `print` to dump all that, and it doesn't) – zdim Mar 21 '23 at 08:04
  • @zdim, yeah, just showing that it's possible to print more than 100 -- a lot more -- unless you do something different. – ikegami Mar 21 '23 at 08:14
  • @ikegami Right, a good point ... how do you even find out that? :) – zdim Mar 21 '23 at 08:15
  • @zdim, By playing with the number produced by `unpack "d", pack "Q<", 1`. Normal IEEE doubles are of the form (+/-)1.____ x 2^(___-1023). Instead, subnormals have the form (+/-)0.____ x 2^(-1022). The more leading zeroes in the fraction part, the smaller the magnitude of subnormals are (because of the leading `0.`), but the less precision they have (because of the constant exponent.) So they can be super tiny. The smallest IEEE doubles are +/-2^(-1074) (with one bit of precision instead of the normal 53). And from what I can tell, they also have the most decimal places of any doubles. Fun! – ikegami Mar 21 '23 at 08:34
  • @ikegami oh oh :) Now I'm going to go through that carefully tomorrow. Yes fun :) – zdim Mar 21 '23 at 09:08
  • I am saying what is the best way to fix the following program to be sure there is no "e"'s in the output: $ perl -wle 'for (0.00001, 0.001, 0.000001){print $_ + 0;}' – Dan Jacobson Mar 23 '23 at 07:30
  • 1
    @DanJacobson "_what is the best way to fix the following program_" -- Use `printf` (or `sprintf`) if you have specific formatting wants. That is the way. That is what they are provided for (that's what the "f" is for). If you use the plain `print` (or better `say`) then you are letting the interpreter do it as it sees it fit. – zdim Mar 24 '23 at 01:48

2 Answers2

0
use POSIX qw( isinf isnan );

# Works for any IEEE double-precision floating-point number
# including +Inf, -Inf, NaN values and subnormal values.
# The precision is measured in significant digits.
# If missing, undef or zero, no rounding is performed.
# Different NaN representations aren't differentiated.
sub to_decimal_form {
   my $n         = shift;
   my $precision = shift || 751;

   return "0"                     if !$n;
   return $n > 0 ? "Inf" : "-Inf" if isinf( $n );
   return "NaN"                   if isnan( $n );

   --$precision;
   my $s = sprintf( "%.*e", $precision, $n );
   my ( $sign, $digits, $e ) = $s =~ /^(-?)([^e]*)e(.*)/
      or die( "Unexpected format <$s>" );

   $digits =~ s/\.//;
   $digits =~ s/0*\z//;
   $e += length( $& ) - $precision;

   if ( $e >= 0 ) {
      $digits .= "0" x $e;
   } else {
      my $z = -$e - length( $digits );
      if ( $z >= 0 ) {
         $digits = "0." . ( "0" x $z ) . $digits;
      } else {
         substr( $digits, $e, 0, "." );
      }
   }

   return $sign . $digits;
}
ikegami
  • 367,544
  • 15
  • 269
  • 518
  • `to_decimal_form( 0.1, 0 )` and `to_decimal_form( 10000000000000000000000000, 0 )` show why rounding is necessary. `to_decimal_form( unpack( "d", pack( "Q", 1 ) ) )` is a fun test. – ikegami Mar 27 '23 at 20:24
-1
#!/usr/bin/perl
# Such a big problem to make perl print numbers consistently, as it
# decides when it wants to use scientific notation "without asking."
#
# Here are some "no trailing zeros and no scientific notation"
# solutions, all without using "printf"! (But be my guest and make
# your own printf solutions.)
#
use strict;
use warnings q(all);
for (qw/0.00001 0.0001 -0.00007 -0.50001 -6.00001 9/) {
    print "\n";
    print "Had:  $_\n";
    my $t = $_ + 0;
    print "Got:  $t\n";
    next unless $t =~ /e/;
    print "FixA: ", $t < 0 ? "-" : "", 0, substr( 1 + abs $t, 1 ), "\n";
    my $k = $t;
    $k =~ s/(\d+)e-(\d+)/"0." . (0 x ($2 - 1)) . $1/e;
    print "FixB: $k\n";
    $t =~ /^(-?)(\d+)e-(\d+)$/ or die "Uh oh";
    print "FixC: ", $1 . "0." . ( 0 x ( $3 - 1 ) ) . $2, "\n";
}

# Once again it is safe to live next to the equator or prime meridian
# without your kid's geocaching club membership card having their
# coordinates in a format that they cannot yet understand, or with
# ugly trailing zeros. Same goes for city workers reading street lamp
# or manhole data sheets. P.S., Not very tested so don't launch any
# ICBMs with it.
Dan Jacobson
  • 490
  • 3
  • 14