Fixed field width number output in perl - perl

I have a random number between 0.001 and 1000 and I need perl to print it with a fixed column width of 5 characters. That is, if it's too long, it should be rounded, and if it's too short, it should be padded with spaces.
Everything I found online suggested using sprintf, but sprintf ignores the field width if the number is too long.
Is there any way to get perl to do this?
What doesn't work:
my $number = 0.001 + rand(1000);
my $formattednumber = sprintf("%5f", $number);
print <<EOF;
$formattednumber
EOF

You need to define your sprintf pattern dynamically. The number of decimals depends on the number of digits on the left hand side of the decimal point.
This function will do that for you.
use strict;
use warnings 'all';
use feature 'say';
sub round_to_col {
my ( $number, $width ) = #_;
my $width_int = length int $number;
return sprintf(
sprintf( '%%%d.%df', $width_int, $width - 1 - $width_int ),
$number
);
}
my $number = 0.001 + rand(1000);
say round_to_col( $number, 5);
Output could be:
11.18
430.7
0.842

You could use pack after the sprintf. It may not be a computationally efficient approach, but it is relatively simple to implement and maintain:
my $formattednumber = pack ('A5', sprintf("%5f", $number));

The answer posted by simbabque does not cover all cases, so this is my improved version, just in case anyone also needs something like this:
sub round_to_col {
my ( $number, $width ) = #_;
my $width_int = length int $number;
my $sprintf;
print "round_to_col error: number longer than width" if $width_int > $width;
$sprintf = "%d" if $width_int == $width;
$sprintf = "% d" if $width_int == $width - 1;
$sprintf = sprintf( '%%%d.%df', $width_int, $width - 1 - $width_int )
if $width_int < $width -1;
return sprintf( $sprintf , $number );
}

Related

Using big numbers in Perl

I have a scenario where I take 2 very big binary strings (having 100 characters) and I need to add them.
The issue is that I am getting the answer in the form 2.000xxxxxxxxxxe+2, whereas I need the precise answer, as another 100 character long string.
chomp($str1=<STDIN>);
chomp($str2=<STDIN>);
print "Str 1 is $str1\n";
print "Str 2 is $str2\n";
$t = $str1 + $str2;
print "Sum is $t\n";
Sample Input
1001101111101011011100101100100110111011111011000100111100111110111101011011011100111001100011111010
1001101111101011011100101100100110111011111011000100111100111110111101011011011100111001100011111010
Sample Output
Str1 is
1001101111101011011100101100100110111011111011000100111100111110111101011011011100111001100011111010
Str2 is
1001101111101011011100101100100110111011111011000100111100111110111101011011011100111001100011111010
Sum is
2.0022022220202e+099
As already suggested, you can use Math::BigInt core module,
use Math::BigInt;
# chomp($str1=<STDIN>);
# chomp($str2=<STDIN>);
# print "Str 1 is $str1\n";
# print "Str 2 is $str2\n";
my $t = Math::BigInt->new("0b$str1") + Math::BigInt->new("0b$str2");
print $t->as_bin;
In order to perform arithmetic on your strings, Perl converts them to floating-point numbers, which are inherently imprecise. If you want to avoid that, use Math::BigInt as already suggested ... or roll your own.
######## WARNING/GUARANTEE: This is practically certain to be
# slower, buggier, less portable, and less secure than Math::BigInt.
# In fact, I planted a security hole just to prove a point. Enjoy.
use strict;
use warnings;
sub addition {
my ($int1, $int2) = #_;
my #int1 = reverse split //, $int1;
my #int2 = reverse split //, $int2;
my $len = scalar(#int1>#int2 ? #int1 : #int2);
my #result;
my $carry = 0;
for (my $i=0; $i < $len; ++$i)
{
$int1[$i] |= 0;
$int2[$i] |= 0;
my $sum = $carry + $int1[$i] + $int2[$i];
if ($sum >= 10)
{
$carry = int($sum / 10);
$sum %= 10;
}
push #result, $sum;
}
push #result, $carry if $carry;
return join ('', reverse #result);
}

How can I make integer division in Perl OR How can I make my binary search work?

I'm trying to implement binary search. This is my code:
#!/usr/bin/perl
#use strict;
use warnings;
#array = (1..100);
$number = <STDIN>;
$low = 0;
$high = $#array;
while($low < $high){
print "Searcing $low ---- $high \n";
$mid = $low + ($high - $low)/2;
if($array[$mid] == $number){
print "Found in index:" . $mid;
last;
}
elsif($array[$mid] < $number){
$low = $mid + 1;
}
else{
$high = $mid - 1;
}
}
But it does not work although it is a straight forward implementation (at least it would be in Java).
It seems that I get float values when dividing and can not search. If I provide as input 5 I get garbage:
5
Searcing 0 ---- 99
Searcing 0 ---- 48.5
Searcing 0 ---- 23.25
Searcing 0 ---- 10.625
Searcing 0 ---- 4.3125
Searcing 3.15625 ---- 4.3125
How can I make it use integer numbers so I can index the array?
Also if I uncomment use strict I get the following errors. What do they mean?
Global symbol "#array" requires explicit package name at D:\Development\Perl\chapter3\binarySearch.pl line 6.
Global symbol "$number" requires explicit package name at D:\Development\Perl\chapter3\binarySearch.pl line 9.
Global symbol "$low" requires explicit package name at
my $int = int 5 / 2;
print $int;
Prints 2.
Two esoteric solutions. Don't actually use these unless you are really bored or something.
use integer -- a pragma that forces Perl to treat all your numerical values as integers. It can be locally scoped so it won't ruin all the data in your program
while($low < $high){
print "Searcing $low ---- $high \n";
{
use integer;
$mid = $low + ($high - $low)/2;
}
if($array[$mid] == $number){
print "Found in index:" . $mid;
last;
}
elsif($array[$mid] < $number){
$low = $mid + 1;
}
else{
$high = $mid - 1;
}
}
Perl has some special variables $=, $-, and $% that will only hold integer values, no matter what you assign to them. Use them directly
$- = $low + ($high - $low) / 2;
if ($array[$-] == $number) { ...
or as intermediates
my $mid = $- = $low + ($high - $low) / 2;
if ($array[$mid] == $number) { ...
Using the magic variables like this is handy for code golf and irritating your co-workers, but not much else.
You should be using the function int.
Aside from that, you need to use strict; and use my to scope your variables. This will catch errors that you might miss. Just declare them like this:
my #array = (1..100);
chomp(my $number = <STDIN>);
my $low = 0;
my $high = $#array;
my $mid = int ($low + ($high - $low)/2);
You might consider using chomp too, to remove newlines from your input.
Use the good old >> 1 instead of /2.
Example:
perl -e 'BEGIN { print 5/2, " : ", 5>>1}'
Output:
2.5 : 2
And use (spare an substraction):
my $mid = ($low + $high) >> 1;
There are quite a few problems with your code.
You disabled strict.
Presumably because of the next problem.
You didn't declare any of your variables, with my or our or even use vars.
our #array = 1..100;
use vars qw'$mid $low $high';
my $number = <STDIN>
You worry too much about overflows. This isn't C.
If your number would overflow an integer, it just becomes a float.
my $mid = $low + ($high - $low)/2;
Should probably just be:
my $mid = ($high + $low)/2;
You expected an integer out of a division.
my $mid = ($high + $low)/2;
If you really want an integer just use int.
my $mid = int( ($high + $low)/2 );
You didn't remove the newline from the end of $number
chomp($number);
You have an off by one error.
while($low < $high){
Should really be
while($low <= $high){
This is really your main problem.
#! /usr/bin/env perl
use strict;
use warnings;
my #array = 1..100;
my $number = <STDIN>;
chomp $number;
my $low = 0;
my $high = $#array;
while($low <= $high){
my $mid = int( ($high + $low)/2 );
printf "Searching %2d .. (%2d) .. %2d\n", $low, $mid, $high;
if($array[$mid] == $number){
print "Found $number at index mid $mid\n";
last;
}elsif($array[$mid] < $number){
$low = $mid + 1;
}else{
$high = $mid - 1;
}
}
That's not really Perlish though.
#!/usr/bin/perl
use strict;
use warnings;
use List::MoreUtils qw'first_index';
my #array = 1..100;
my $number = <STDIN>;
chomp $number;
my $index = first_index { $_ == $number } #array;
print "Found $number at index ", $index if $index != -1;
Or more bizarrely
#! /usr/bin/env perl
use strict;
use warnings;
my #array = 1..100;
my $number = <STDIN>;
chomp $number;
my $char = join '', map chr, #array;
my $index = index $char, chr $number;
print "Found $number at index $index\n" if $index != -1;
This works with numbers up-to the lesser of UV max or (2 ** 72) - 1.
That is 18,446,744,073,709,551,615 on 64-bit builds, and 4,294,967,295 on 32-bit builds.
First of all,
my $mid = $low + ($high - $low)/2
can be simplified to
my $mid = ($high + $low)/2;
You can get the integral part using int
my $mid = int( ($high + $low)/2 );
Or you could take advantage of the fact that a bit shift is an integer division by 2.
my $mid = ($high + $low) >> 1;
See also: Flexible Binary Search Function

Converting base10 to base36 in perl [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
What’s the best way to do base36 arithmetic in Perl?
Hello is it possible to convert numbers from a base-10-to-base-36-conversion with perl script?
here's an example :
base 10 - 1234567890123 and outcome
base 36 - FR5HUGNF
Try using Math::Base36 CPAN library for base conversion.
Poking around the Math::Base36 source code shows how easy it is to enact the conversion:
sub encode_base36 {
my ( $number, $padlength ) = #_;
$padlength ||= 1;
die 'Invalid base10 number' if $number =~ m{\D};
die 'Invalid padding length' if $padlength =~ m{\D};
my $result = '';
while ( $number ) {
my $remainder = $number % 36;
$result .= $remainder <= 9 ? $remainder : chr( 55 + $remainder );
$number = int $number / 36;
}
return '0' x ( $padlength - length $result ) . reverse( $result );
}
It is quite straightforward to write a subroutine to do this. The code below does no value checking and assumes the numbers to be converted are always non-negative
If your version of Perl isn't sufficiently up-to-date to support the state keyword, then just declare $symbols as a my variable at the head of the program
use strict;
use warnings;
use feature 'state';
print base36(1234567890123);
sub base36 {
my ($val) = #_;
state $symbols = join '', '0'..'9', 'A'..'Z';
my $b36 = '';
while ($val) {
$b36 = substr($symbols, $val % 36, 1) . $b36;
$val = int $val / 36;
}
return $b36 || '0';
}
output
FR5HUGNF

First 1000 bytes of a variable

Is this usage of unpack correct if I would like to try this guessing subroutine with the variables first 1000 bytes?
#!/usr/bin/env perl
use warnings;
use 5.10.1;
my $var = ...;
my $part = unpack( 'b1000', $var ) ;
sub is_binary_data {
local $_ = shift;
( tr/ -~//c / length ) >= .3;
}
if ( is_binary_data( $part ) ) {
say "Binary";
}
else {
say "Text";
}
No it isn't since unpack will create a string of 0 and 1's (up to 1000 of them) which would certainly pass the ascii test (which I believe tr, -~,,c / length is)
I would suggest using just substr ($var, 0, 1000) instead.
Also, maybe \r and \n should appear in the tr//.

How can I do 64-bit hex/decimal arithmetic AND output a full number in HEX as string in Perl?

I need to do some arithmetic with large hexadecimal numbers below, but when I try to output I'm getting overflow error messages "Hexadecimal number > 0xffffffff non-portable", messages about not portable, or the maximum 32-bit hex value FFFFFFFF.
All of which imply that the standard language and output routines only cope with 32 bit values. I need 64-bit values and have done a lot of research, but I found nothing that BOTH enables the arithmetic AND outputs the large number in hex.
my $result = 0x00000200A0000000 +
( ( $id & 0xFFFFF ) * 2 ) + ( ( $id / 0x100000 ) * 0x40000000 );
So, for $id with the following values I should get $result:
$id = 0, $result = 0x00000200A0000000
$id = 1, $result = 0x00000200A0000002
$id = 2, $result = 0x00000200A0000004
How can I do this?
Here is my inconclusive research results, with reasons why:
How can I do 64-bit arithmetic in Perl?
How can I sum large hexadecimal values in Perl? Vague, answer not definitively precise and no example.
Integer overflow
non conclusive
Integer overflow
non conclusive
bigint
no info about assignment, arithmetic or output
bignum
examples not close to my problem.
How can I sprintf a big number in Perl?
example given is not enough info for me: doesn't deal with hex
assignment or arithmetic.
Re: secret code generator
Some examples using Fleximal, mentions to_str to output value of
variable but 1) I don't see how the
variable was assigned and 2) I get
error "Can't call method "to_str"
without a package or object
reference" when I run my code using
it.
String to Hex
Example of using Math::BigInt which
doesn't work for me - still get
overflow error.
Is there a 64-bit hex()?
Nearly there - but doesn't deal with
outputting the large number in hex,
it only talks of decimal.
CPAN Math:Fleximal
does the arithmetic, but there doesn't seem to be any means to actually
output the value still in hex
sprintf
Doesn't seem to be able to cope with
numbers greater than 32-bits, get the
saturated FFFFFFFF message.
Edit: Update - new requirement and supplied solution - please feel free to offer comments
Chas. Owens answer is still accepted and excellent (part 2 works for me, haven't tried the part 1 version for newer Perl, though I would invite others to confirm it).
However, another requirement was to be able to convert back from the result to the original id.
So I've written the code to do this, here's the full solution, including #Chas. Owens original solution, followed by the implementation for this new requirement:
#!/usr/bin/perl
use strict;
use warnings;
use bigint;
use Carp;
sub bighex {
my $hex = shift;
my $part = qr/[0-9a-fA-F]{8}/;
croak "$hex is not a 64-bit hex number"
unless my ($high, $low) = $hex =~ /^0x($part)($part)$/;
return hex("0x$low") + (hex("0x$high") << 32);
}
sub to_bighex {
my $decimal = shift;
croak "$decimal is not an unsigned integer"
unless $decimal =~ /^[0-9]+$/;
my $high = $decimal >> 32;
my $low = $decimal & 0xFFFFFFFF;
return sprintf("%08x%08x", $high, $low);
}
for my $id (0 ,1, 2, 0xFFFFF, 0x100000, 0x100001, 0x1FFFFF, 0x200000, 0x7FDFFFFF ) {
my $result = bighex("0x00000200A0000000");
$result += ( ( $id & 0xFFFFF ) * 2 ) + ( ( $id / 0x100000 ) * 0x40000000 );
my $clusterid = to_bighex($result);
# the convert back code here:
my $clusterid_asHex = bighex("0x".$clusterid);
my $offset = $clusterid_asHex - bighex("0x00000200A0000000");
my $index_small_units = ( $offset / 2 ) & 0xFFFFF;
my $index_0x100000_units = ( $offset / 0x40000000 ) * 0x100000;
my $index = $index_0x100000_units + $index_small_units;
print "\$id = ".to_bighex( $id ).
" clusterid = ".$clusterid.
" back to \$id = ".to_bighex( $index ).
" \n";
}
Try out this code at http://ideone.com/IMsp6.
#!/usr/bin/perl
use strict;
use warnings;
use bigint qw/hex/;
for my $id (0 ,1, 2) {
my $result = hex("0x00000200A0000000") +
( ( $id & 0xFFFFF ) * 2 ) + ( ( $id / 0x100000 ) * 0x40000000 );
printf "%d: %#016x\n", $id, $result;
}
The bigint pragma replaces the hex function with a version that can handle numbers that large. It also transparently makes the mathematical operators deal with big ints instead of the ints on the target platform.
Note, this only works in Perl 5.10 and later. If you are running an earlier version of Perl 5, you can try this:
#!/usr/bin/perl
use strict;
use warnings;
use bigint;
use Carp;
sub bighex {
my $hex = shift;
my $part = qr/[0-9a-fA-F]{8}/;
croak "$hex is not a 64-bit hex number"
unless my ($high, $low) = $hex =~ /^0x($part)($part)$/;
return hex("0x$low") + (hex("0x$high") << 32);
}
sub to_bighex {
my $decimal = shift;
croak "$decimal is not an unsigned integer"
unless $decimal =~ /^[0-9]+$/;
my $high = $decimal >> 32;
my $low = $decimal & 0xFFFFFFFF;
return sprintf("%08x%08x", $high, $low);
}
for my $id (0 ,1, 2) {
my $result = bighex("0x00000200A0000000");
$result += ( ( $id & 0xFFFFF ) * 2 ) + ( ( $id / 0x100000 ) * 0x40000000 );
print "$id ", to_bighex($result), "\n";
}
The comment by ysth is right. Short example of 64-bit arithmetics using Perl from Debian stretch without Math::BigInt aka "use bigint":
#!/usr/bin/perl -wwi
sub do_64bit_arith {
use integer;
my $x = ~2;
$x <<= 4;
printf "0x%08x%08x\n", $x>>32, $x;
}
do_64bit_arith();
exit 0;
The script prints 0xffffffffffffffffffffffffffffffd0.