How can I write in scientific notation using Perl formats? - perl

I've always used printf, and I've never used write/format. Is there any way to reproduce printf("%12.5e", $num) using a format? I'm having trouble digesting the perlform documentation, but I don't see a straightforward way of doing this.
EDIT: based on the answers I got, I'm just gonna keep on using printf.

Short answer, don't use formats.
Unresearched answer, sure, just use sprintf:
#!/usr/bin/perl
use strict;
use warnings;
our $num = .005;
write;
format STDOUT =
#>>>>>>>>>>>>>>>>>
sprintf("%12.5e", $num)
.
Seriously, if you need something like Perl 5 formats, take a look at Perl6::Form (note, this is a Perl 5 module, it just implements the proposed Perl 6 version of formats).

I totally agree with Chas. Owens on formats in general. Format was really slick 15 years ago, but format has not kept up with the advancements of the rest of Perl.
Here is a technique for line oriented output that I use time to time. You can use formline which is one of the public internal functions used by format. Format is page oriented. It is very hard to do things like span columns or change the format by line depending on the data. You can format a single line using the same text formatting logic used by format and then output that result yourself.
A (messy) example:
use strict; use warnings;
sub print_line {
my $pic=shift;
my #args=#_;
formline($pic,#args);
print "$^A\n";
$^A='';
}
my ($wlabel, $wlow, $whigh, $wavg)=(0,0,0,0);
my ($plabel,$plow,$phigh, $pavg);
my ($s_low,$s_high,$s_avg)=qw(%.2f %.2e %.2f);
my #results=( ["Label 1", 3.445, 0.00006678, .025],
["Label 2", 12.5555556, 55.112, 1.11],
["Wide Label 3", 1231.11, 1555.0, 66.66] );
foreach (#results) {
my $tmp;
$tmp=length($_->[0]);
$wlabel=$tmp if $tmp>$wlabel;
$tmp=length(sprintf($s_low,$_->[3]));
$wlow=$tmp if $tmp>$wlow;
$tmp=length(sprintf($s_high,$_->[2]));
$whigh=$tmp if $tmp>$whigh;
$tmp=length(sprintf($s_avg,$_->[1]));
$wavg=$tmp if $tmp>$wavg;
}
print "\n\n";
my #a1=("Label", "Rate - Operations / sec");
my #a2=("Text", "Average", "High", "Low");
my #a3=("----------", "-------", "----", "---");
my $l1fmt="#".'|' x $wlabel." #".'|'x($whigh+$wavg+$wlow+6);
my $l2fmt="#".'|' x $wlabel." #".'|' x $wavg." #".'|' x $whigh .
" #".'|' x $wlow;
print_line($l1fmt,#a1);
print_line($l2fmt,#a2);
print_line($l2fmt,#a3);
$plabel="#".'>' x $wlabel;
$phigh="#".'>' x $whigh;
$pavg="#".'>' x $wavg;
$plow="#".'<' x $wlow;
foreach (#results) {
my $pic="$plabel $pavg $phigh $plow";
my $mark=$_->[0];
my $avg=sprintf($s_avg,$_->[1]);
my $high=sprintf($s_high,$_->[2]);
my $low=sprintf($s_low,$_->[3]);
print_line($pic,$mark,$avg,$high,$low);
}
print "\n\n";
Outputs this:
Label Rate - Operations / sec
Text Average High Low
---------- ------- ---- ---
Label 1 3.44 6.68e-05 0.03
Label 2 12.56 5.51e+01 1.11
Wide Label 3 1231.11 1.56e+03 66.66
Notice that the width of the columns is set based on the width of the data as formatted by the sprintf format string. You can then left, center, right justify that result. The "Low" data column is left justified, the rest of the data are right justified. You can change this by the symbol used in the scalar $plow and it is the same as format syntax. The labels at the top are centered and the "Rate - Operations / sec" label spans 3 columns.
This is obviously not "production ready" code, but you get the drift I think. You would need to further check the total width of the columns against desired width, etc. You have to manually do some of the work that format does for you, but you have far more flexibility with this approach. It is very easy to use this method for several sections of a line with sprintf for example.
Cheers.

Related

How do I display a large number in scientific notation?

Using AutoIt, when I multiply 1 by 10^21, I get 1e+021. But in separate steps, such as multiplying 1 by 10^3 seven times, I get the overflow value of 3875820019684212736.
It appears AutoIt cannot handle numbers with more than eighteen digits. Is there a way around this? For example, can I multiply 10,000,000,000,000,000 by 1000 and have the result displayed as 1e+019?
Try this UDF : BigNum UDF
Example :
$X = "9999999999999999999999999999999"
$Y = "9999999999999999999999999999999"
$product = _BigNum_Mul($X, $Y)

How can I format a number as currency in Perl?

How I can format this for example:
100000 as 1000.00
and
90 as 0.90
using Perl?
I found and try with some modules like Locale::Currency::Format and Number::Format, but nothing work :/
With Locale::Currency::Format I try:
use Locale::Currency::Format;
my $total = 19750;
my $convert = currency_format('USD',$total,'FMT_COMMON');
print $convert;
I've expect 197.50 but the print are "19,750.00 USD".
my $formatted = sprintf '%.2f', $number/100.0;
In US currency we count dollars, not cents. If you want to use integers representing pennies to avoid rounding errors, that's great; good practice. But it's up to you to divide by 100 to get human-legible output in the standard format.
The sprintf function (string print formatted, originally from the C standard library and now found in many programming languages) will format numbers in a variety of ways. In this case, the format string %.2f requests output of a floating-point (i.e. decimal, fractional, real, non-integer) number with 2 digits after the decimal point.
To convert cents into dollars, divide by 100!
sprintf("%.2f", $total/100) # 197.50
or
currency_format('USD', $total/100, 'FMT_COMMON') # 197.50 USD
Update for late 2022:
Using this method:
use Locale::Currency::Format;
...
$dollars = currency_format('USD',1,624.98,FMT_SYMBOL);
Outputs this:
$1.00
I have just run into this issue and had to insert this before the format:
use Locale::Currency::Format;
...
$dollars =~ s/\,//g; #<----------
$dollars = currency_format('USD',$dollars,FMT_SYMBOL);

How to convert PDL image to GdkPixbuf

I'm trying to display a graph generated by PDL (using PLplot) inside a Gtk3 app. When I try the following code, I see two problems:
$pdlImg isn't a GdkPixbuf so new_from_pixbuf() doesn't work.
$pdlImg appears to be empty as because the error message prints out the 10x10x3 array as a string and they're all zeroes.
Code:
#!/usr/bin/perl -w
use strict;
use PDL;
use PDL::Graphics::PLplot;
use Gtk3 -init;
my $pdlImg = zeroes(byte, 10, 10, 3);
my $pl = PDL::Graphics::PLplot->new(DEV => 'mem', MEM => $pdlImg);
my $x = sequence(10);
my $y = $x**2;
$pl->xyplot($x, $y);
$pl->close;
my $win = Gtk3::Window->new;
my $img = Gtk3::Image->new_from_pixbuf($pdlImg);
$win->add($img);
$win->show_all;
Gtk3::main();
To answer your first question, you are having PLplot put the plot into a piddle that is 10 pixels wide and 10 pixels high. I'm not sure if you're just going to get one corner of the normal plot in that case, or if you're getting the whole plot sampled into those 10x10 pixels. But in either case it's no surprise that $pdlImg is entirely zeroes. Try passing in a piddle with larger size (perhaps 1000, 1000, 3), or perhaps even using MEM => $pdlImg=null when you create the PLplot plot object.
I can't help with your second question, I have no experience with Gtk3, sorry.

How to read elevation from USGS NED DEM GridFloat file in Perl

I have downloaded a large set of GridFloat (.flt, .hdr) DEM files from USGS NED (1") in order to implement my own elevation service on my website. I would like to be able to look up an elevation from this fileset, given latitude and longitude as inputs. I use Perl for my website development. The files have a conventional naming scheme, and I am able to get the appropriate tile filename using the lat/lng. Howevever, accessing the internals of the file is where I'm having an issue.
I know the file is in a fairly straightforward format (.flt, apparently called "Gridfloat"), but I could use some help figuring out the magic numbers for calculating where in the file I need to seek to for a given lat/lng, and how to handle byte order and so on so that I end up with an elevation. From what I understand, apparently row ordering can be an issue, as well as byte ordering. I am looking for a recipe that does not involve use of any third party libraries such as GDAL, which I think are overly complicated and slow for what I want to do. I think it should be possible to just open the file, seek to a position based on some calculation, read some bytes and then unpack them into the correct byte order. Here is an example .hdr file that accompanies floatn48w097_1.flt, I think it has the necessary info. There are a bunch of other files that come with the .zip, including .prj, but I believe those are for a commercial program like ArcInfo. I think everything I need should be in the following .hdr file.
ncols 3612
nrows 3612
xllcorner -97.00166666667
yllcorner 46.99833333333
cellsize 0.000277777777778
NODATA_value -9999
byteorder LSBFIRST
What I'm really hoping for is a formula for calculating the row and column from the lat/lng, then another formula for translating the row/column into a position for seek, how many bytes to read, and how to convert those raw bytes into an integer (or whatever it is these files contain). I feel that this could be a very fast operation, without all the overhead involved with the larger libraries which seem to be focused on doing a lot of stuff that I don't need.
I don't need Perl code, just pseudocode showing the calculations for row/col offsets etc would be more than enough. I believe the files are binary format, a straightforward grid of 4-byte numbers. The file example that goes with the .hdr file above has a size of 52186176, and when you multiply the ncols by nrows (from the .hdr), you get 13046544. which divides nicely into the file size by 4. So I assume it's just a matter of getting the right formula for row/col based on lat/lng, and then getting the bytes swizzled into the right order. I've just not done this much.
I found some reference to the Gridfloat format here: coolutils.com/formats/flt so apparently the file consists of a grid of 64-bit floating point values.
Thanks!
Ok, I think I have an answer. The following is Perl routine, which seems to give back reasonable looking elevation values when tested with the USGS NED1 .flt files. The script takes latitude and longitude as command line arguments, looks up the file and indexes into the grid.
#!/usr/bin/perl
use strict;
use POSIX;
use Math::Round;
sub get_elevation
{
my ($lat, $lng) = #_;
my $lat_degree = ceil ($lat);
my $lng_degree = floor ($lng);
my $lat_letter = ($lat >= 0) ? 'n' : 's';
my $lng_letter = ($lng >= 0) ? 'e' : 'w';
my $lng_tilenum = abs($lng_degree);
my $lat_tilenum = abs($lat_degree);
my $tilename = $lat_letter . sprintf('%02d', $lat_tilenum) . $lng_letter . sprintf('%03d',$lng_tilenum);
my $path = "/data/elevation/ned1/$tilename/float${tilename}_1.flt";
print "path = $path\n";
die "No such file" if (!-e($path));
my ($lat_fraction, $lat_integral) = modf (abs($lat));
my $row = floor ((1 - $lat_fraction) * 3600);
my ($lng_fraction, $lng_integral) = modf (abs($lng));
my $col = floor ((1 - $lng_fraction) * 3600);
open(FILE, "<$path");
my $pos = (3612 * 4 * 6) + (3612 * 4 * $row) + (4 * 6) + ($col * 4);
seek (FILE, $pos, SEEK_SET);
my $buffer;
read (FILE, $buffer, 4);
close (FILE);
my ($elevation) = unpack('f', $buffer);
if ($elevation == -9999)
{
return 'undefined';
}
return $elevation;
}
my $lat = $ARGV[0];
my $lng = $ARGV[1];
my $elevation = get_elevation ($lat, $lng);
print "Elevation for ($lat, $lng) = $elevation meters (", $elevation * 3.28084, " feet)\n";
Hope this might be useful to anyone else trying to do the same kind of thing... I've tested this method now and it seems to produce good looking elevation profiles which are smoother than those from the 3" SRTM data.
Neil put me on the right track but I think there's a few problems with his original answer. I've added some fixes and improvements including on-the-fly download of the needed tile from the 1/3 arc second (10 meter) dataset, proper parsing of the header file, and what I believe is corrected indexing.
This is still mostly illustrative and should be improved before production use, particularly, hanging on to the header information and the file handle for repeated queries.
https://gist.github.com/biomiker/32fe34e1fa1bb49ae1135ab6652f596d

sprintf/printf right pad float with zeros in fixed width field

I am using PERL (for legacy reasons) and I would like to format fixed width columns in a CSV file. How do I format the following values:
1.0001
10.0001
100.0001
1000.0001
1000000.1
100000001
into fixed width of 8 by right padding floats with zeros or truncating, BUT if a large integer is encountered the field width must grow to accomodate:
1.000100
10.00010
100.0001
1000.000
1000000.
100000001
I am not performing any operations, so they could possibly be treated as strings or other. I've tried about every combination in the sprintf documentation.
Thanks.
[The question was changed after this was posted. This no longer answers the question.]
substr(sprintf("%.6f", $x), 0, 8)
or
substr($x.("0"x5), 0, 8)
There's probably a neater way, but this example should work:
my #array = qw(1.0001 10.0001 100.0001 1000.0001);
for my $nums (#array) {
$nums .= '0' while length $nums < 8;
print "$nums\n";
}
1.000100
10.00010
100.0001
1000.0001