I am not a heavy perl user. I am just learning. However, recently I am working with an old script written by someone else.
Essentially, the script reads an input file from which the variable "index_a" is defined such that when doing a simple print:
print "$index_a";
00
My confusion comes from the following:
When a comparison to "zero" is made, it holds true:
if ($index_a == 0) { print "Found first index: 0" };
Found first index: 0
I was expecting the comparison to be false. Since the value of index_a = 00 is obtained from reading a line from an input file. Hence to my understanding index_a is a string which value is "00", therefore the if statement should not be executed.
Could some perl expert help me understand this? Also, could you let me know what is the proper way to check for data types in perl? I have experience with python and C and hence this is behavior is very confusing to me.
Thanks!
In Perl, the context is important when considering the value. A scalar variable can contain all sorts of data types, but what you are testing for determines what you find. For example:
if ("00" == 0) # string "00" is numerically identical to 0
if ("a" == 0) # string "a" is numerically identical to 0 (gives a warning)
# Argument "a" isn't numeric in numeric eq (==)
if ("00" eq "0") # string "00" is not stringwise identical to "0".
if (#a == 0) # if the array #a is empty (length 0)
In a numerical equality check with a string, Perl will try to cast the string to a number. If the string begins with a number, it will be cast to that number (for the duration of the test), else it will be cast to 0.
You have to (sort of) know what you have in a variable when you test what it contains. Or else you have to check more thoroughly. In your case, when reading from a file, you might want to use eq to test for string equality.
When you use a Perl string in a numeric context, such as a numeric comparison, it recognizes the starting part that looks like a decimal number and ignores the stuff after that. Whatever it recognizes becomes the number.
It follows these basic rules:
Ignore leading whitespace. This is handy when you extract a field out of columnar data and the number doesn't take up the entire column.
Allow for a single leading sign (+ or -)
Skip leading zeros (so, no octal)
Capture decimal ASCII digits (0,1,2,3,4,5,6,7,8,9), allowing for a single decimal point (so, no semantic versioning numbers)
Stop at the first non-decimal-digit character
Whatever you have so far is the number. If you have nothing, the number is 0.
There are some things to note, though:
Adding 0 to a string is a common way to force numeric mode.
Underscores in strings are just underscores. You can use underscores to separate digits in number literals but not in strings. So, 123_456 is 123456 because the perl parser handles the _, but '123_456' is just 123.
However, if the first characters (excluding the sign) are "Inf", in any case, the string converts to "Inf" and includes the - sign but not the + sign. This is the special IEEE value of Infinity.
However, if the first characters (excluding the sign) are "NaN", in any case, the string converts to "Nan" and excludes either sign. This is the special "Not a number" value.
Here are some examples:
#!perl
use v5.10;
use utf8;
use open qw(:std :utf8);
my #strings = qw(
-5 +7 +006 -01 00
+.1234 -.9876 .657 ..890 1.2.3
١٢٣
- + +-657
12a34 a987 123fred 0456barney
0x12 12_34 0177
NaN -NaN NAN
Inf +Inf -Inf INF Infamy Infinity
);
push #strings, " 432", "+ 123", " -987", " +063"," NaN", " Inf", '';
foreach my $string ( #strings ) {
printf "%-12s => %s\n", qq('$string'), 0 + $string;
}
And the output:
'-5' => -5
'+7' => 7
'+006' => 6
'-01' => -1
'00' => 0
'+.1234' => 0.1234
'-.9876' => -0.9876
'.657' => 0.657
'..890' => 0
'1.2.3' => 1.2
'١٢٣' => 0
'-' => 0
'+' => 0
'+-657' => 0
'12a34' => 12
'a987' => 0
'123fred' => 123
'0456barney' => 456
'0x12' => 0
'12_34' => 12
'0177' => 177
'NaN' => NaN
'-NaN' => NaN
'NAN' => NaN
'Inf' => Inf
'+Inf' => Inf
'-Inf' => -Inf
'INF' => Inf
'Infamy' => Inf
'Infinity' => Inf
' 432' => 432
'+ 123' => 0
' -987' => -987
' +063' => 63
' NaN' => NaN
' Inf' => Inf
'' => 0
Finally, there's another interesting thing about Perl scalars. When you use a string as a number, Perl converts the string to a number and remembers the conversion. It does not, however, change the string. Devel::Peek shows you the innards of Perl data structures.
#!perl
use v5.10;
use Devel::Peek;
select(STDERR); $|++; # just to order the output from Dump
my $string = '123fred';
say "string is <$string>";
Dump( $string );
say '-' x 40;
my $n = $string + 0;
say "string is <$string>";
Dump( $string );
Here's the output. At first, $string has a PV (pointer value) for a string. After the numeric operation, it also has IV and NV values for numbers.
string is <123fred>
SV = PV(0x7f8be980cab0) at 0x7f8bea0160f8
REFCNT = 1
FLAGS = (POK,IsCOW,pPOK)
PV = 0x7f8be9610ca0 "123fred"\0
CUR = 7
LEN = 10
COW_REFCNT = 1
----------------------------------------
string is <123fred>
SV = PVNV(0x7f8be980ac50) at 0x7f8bea0160f8
REFCNT = 1
FLAGS = (POK,IsCOW,pIOK,pNOK,pPOK)
IV = 123
NV = 123
PV = 0x7f8be9610ca0 "123fred"\0
CUR = 7
LEN = 10
COW_REFCNT = 1
Related
when I try to execute the following(01434.210 instead of 1434.210)
$val=22749.220-(21315.010+01434.210)
print $val
I get these output
output 638.207900000001
But according to me output must be 0.
What am I missing?
A leading 0 in a literal number makes Perl interpret the value I'm base 8:
123 # 123, in decimal
0123 # 123 in octal, but 83 in decimal
This isn't the same for strings converted to numbers. In those Perl ignores the leading 0s. The string-to-number conversion only deals in base-10:
"123" + 0 # 123
"0123" + 0 # still 123
In your example in the comment, you convert a literal number to a string with a leading zero. When you convert that string back to its numeric form you get the same value you started with:
$val=sprintf("%05d",1434); # converting 1434 to the string "01434"
print $val; print "\n"; # still a string
print $val+21315; # "01434" + 21315 => 1434 + 21315
print "\n";
print 01434+21315; # oct(1434) + 21315 => 796 + 21315
The leading zero notation helps with certain builtins that typically use octal numbers, such as those that deal with unix permissions:
chmod 0644, #files
I have to write a Perl script that converts a binary number, specified as an
argument, to a decimal number. In the question there's a hint to use the reverse function.
We have to assume that the binary number is in this format
EDIT: This is what I've progressed to (note this is code from my textbook that I've messed with):
#!/usr/bin/perl
# dec2.pl: Converts decimal number to binary
#
die("No arguments\n") if ( $#ARGV == -1 ) ;
foreach $number (#ARGV) {
$original_number = $number ;
until ($number == 0 ) {
$bit = $number % 2 ;
unshift (#bit_arr, $bit) ;
$number = int($number / 2 );
}
$binary_number = join ("", #bit_arr) ;
print reverse ("The decimal number of $binary_number is $original_number\n");
$#bit_arr = -1;
}
When executed:
>./binary.pl 8
The decimal number of 1000 is 8
I don't know how to word it to make the program know to add up all of the 1's in the number that is inputted.
You could just use sprintf to do the converting for you...
sprintf("%d", 0b010101); # Binary string 010101 -> Decimal 21
sprintf("%b", 21) # Decimal 21 -> Binary 010101 string
Of course, you can also just eval a binary string with 0b in front to indicate binary:
my $binary_string = '010101';
my $decimal = eval("0b$binary"); # 21
You don't have to use reverse, but it makes it easy to think about the problem with respect to exponents and array indices.
use strict;
use warnings;
my $str = '111110100';
my #bits = reverse(split(//, $str));
my $sum = 0;
for my $i (0 .. $#bits) {
next unless $bits[$i];
$sum += 2 ** $i;
}
First of all, you are suppose to convert from a binary to decimal, not the other way around, which you means you take an input like $binary = '1011001';.
The first thing you need to do is obtain the individual bits (a0, a1, etc) from that. We're talking about splitting the string into its individual digits.
for my $bit (split(//, $binary)) {
...
}
That should be a great starting point. With that, you have all that you need to apply the following refactoring of the formula you posted:
n = ( ( ( ... )*2 + a2 )*2 + a1 )*2 + a0
[I have no idea why reverse would be recommended. It's possible to use it, but it's suboptimal.]
In Perl, what is the difference between
$status = 500;
and
$status = '500';
Not much. They both assign five hundred to $status. The internal format used will be different initially (IV vs PV,UTF8=0), but that's of no importance to Perl.
However, there are things that behave different based on the choice of storage format even though they shouldn't. Based on the choice of storage format,
JSON decides whether to use quotes or not.
DBI guesses the SQL type it should use for a parameter.
The bitwise operators (&, | and ^) guess whether their operands are strings or not.
open and other file-related builtins encode the file name using UTF-8 or not. (Bug!)
Some mathematical operations return negative zero or not.
As already #ikegami told not much. But remember than here is MUCH difference between
$ perl -E '$v=0500; say $v'
prints 320 (decimal value of 0500 octal number), and
$ perl -E '$v="0500"; say $v'
what prints
0500
and
$ perl -E '$v=0900; say $v'
what dies with error:
Illegal octal digit '9' at -e line 1, at end of line
Execution of -e aborted due to compilation errors.
And
perl -E '$v="0300";say $v+1'
prints
301
but
perl -E '$v="0300";say ++$v'
prints
0301
similar with 0x\d+, e.g:
$v = 0x900;
$v = "0x900";
There is only a difference if you then use $var with one of the few operators that has different flavors when operating on a string or a number:
$string = '500';
$number = 500;
print $string & '000', "\n";
print $number & '000', "\n";
output:
000
0
To provide a bit more context on the "not much" responses, here is a representation of the internal data structures of the two values via the Devel::Peek module:
user#foo ~ $ perl -MDevel::Peek -e 'print Dump 500; print Dump "500"'
SV = IV(0x7f8e8302c280) at 0x7f8e8302c288
REFCNT = 1
FLAGS = (PADTMP,IOK,READONLY,pIOK)
IV = 500
SV = PV(0x7f8e83004e98) at 0x7f8e8302c2d0
REFCNT = 1
FLAGS = (PADTMP,POK,READONLY,pPOK)
PV = 0x7f8e82c1b4e0 "500"\0
CUR = 3
LEN = 16
Here is a dump of Perl doing what you mean:
user#foo ~ $ perl -MDevel::Peek -e 'print Dump ("500" + 1)'
SV = IV(0x7f88b202c268) at 0x7f88b202c270
REFCNT = 1
FLAGS = (PADTMP,IOK,READONLY,pIOK)
IV = 501
The first is a number (the integer between 499 and 501). The second is a string (the characters '5', '0', and '0'). It's not true that there's no difference between them. It's not true that one will be converted immediately to the other. It is true that strings are converted to numbers when necessary, and vice-versa, and the conversion is mostly transparent, but not completely.
The answer When does the difference between a string and a number matter in Perl 5 covers some of the cases where they're not equivalent:
Bitwise operators treat numbers numerically (operating on the bits of the binary representation of each number), but they treat strings character-wise (operating on the bits of each character of each string).
The JSON module will output a string as a string (with quotes) even if it's numeric, but it will output a number as a number.
A very small or very large number might stringify differently than you expect, whereas a string is already a string and doesn't need to be stringified. That is, if $x = 1000000000000000 and $y = "1000000000000000" then $x might stringify to 1e+15. Since using a variable as a hash key is stringifying, that means that $hash{$x} and $hash{$y} may be different hash slots.
The smart-match (~~) and given/when operators treat number arguments differently from numeric strings. Best to avoid those operators anyway.
There are different internally:)
($_ ^ $_) ne '0' ? print "$_ is string\n" : print "$_ is numeric\n" for (500, '500');
output:
500 is numeric
500 is string
I think this perfectly demonstrates what is going on.
$ perl -MDevel::Peek -e 'my ($a, $b) = (500, "500");print Dump $a; print Dump $b; $a.""; $b+0; print Dump $a; print Dump $b'
SV = IV(0x8cca90) at 0x8ccaa0
REFCNT = 1
FLAGS = (PADMY,IOK,pIOK)
IV = 500
SV = PV(0x8acc20) at 0x8ccad0
REFCNT = 1
FLAGS = (PADMY,POK,pPOK)
PV = 0x8c5da0 "500"\0
CUR = 3
LEN = 16
SV = PVIV(0x8c0f88) at 0x8ccaa0
REFCNT = 1
FLAGS = (PADMY,IOK,POK,pIOK,pPOK)
IV = 500
PV = 0x8d3660 "500"\0
CUR = 3
LEN = 16
SV = PVIV(0x8c0fa0) at 0x8ccad0
REFCNT = 1
FLAGS = (PADMY,IOK,POK,pIOK,pPOK)
IV = 500
PV = 0x8c5da0 "500"\0
CUR = 3
LEN = 16
Each scalar (SV) can have string (PV) and or numeric (IV) representation. Once you use variable with only string representation in any numeric operation and one with only numeric representation in any string operation they have both representations. To be correct, there can be also another number representation, the floating point representation (NV) so there are three possible representation of scalar value.
Many answers already to this question but i'll give it a shot for the confused newbie:
my $foo = 500;
my $bar = '500';
As they are, for practical pourposes they are the "same". The interesting part is when you use operators.
For example:
print $foo + 0;
output: 500
The '+' operator sees a number at its left and a number at its right, both decimals, hence the answer is 500 + 0 => 500
print $bar + 0;
output: 500
Same output, the operator sees a string that looks like a decimal integer at its left, and a zero at its right, hence 500 + 0 => 500
But where are the differences?
It depends on the operator used. Operators decide what's going to happen. For example:
my $foo = '128hello';
print $foo + 0;
output: 128
In this case it behaves like atoi() in C. It takes biggest numeric part starting from the left and uses it as a number. If there are no numbers it uses it as a 0.
How to deal with this in conditionals?
my $foo = '0900';
my $bar = 900;
if( $foo == $bar)
{print "ok!"}
else
{print "not ok!"}
output: ok!
== compares the numerical value in both variables.
if you use warnings it will complain about using == with strings but it will still try to coerce.
my $foo = '0900';
my $bar = 900;
if( $foo eq $bar)
{print "ok!"}
else
{print "not ok!"}
output: not ok!
eq compares strings for equality.
You can try "^" operator.
my $str = '500';
my $num = 500;
if ($num ^ $num)
{
print 'haha\n';
}
if ($str ^ $str)
{
print 'hehe\n';
}
$str ^ $str is different from $num ^ $num so you will get "hehe".
ps, "^" will change the arguments, so you should do
my $temp = $str;
if ($temp ^ $temp )
{
print 'hehe\n';
}
.
I usually use this operator to tell the difference between num and str in perl.
Has something changed in Perl or has it always been this way, that examples like the second ($number eq 'a') don't throw a warning?
#!/usr/bin/env perl
use warnings;
use 5.12.0;
my $string = 'l';
if ($string == 0) {};
my $number = 1;
if ($number eq 'a') {};
# Argument "l" isn't numeric in numeric eq (==) at ./perl.pl line 6.
Perl will be try to convert a scalar to the type required by the context where it is used.
There is a valid conversion from any scalar type to a string, so this is always done silently.
Conversion to a number is also done silently if the string passes a looks_like_number test (accessible through Scalar::Util). Otherwise a warning is raised and a 'best guess' approximation is done anyway.
my $string = '9';
if ( $string == 9 ) { print "YES" };
Converts the string silently to integer 9, the test succeeds and YES is printed.
my $string = '9,8';
if ( $string == 9 ) { print "YES" };
Raises the warning Argument "9,8" isn't numeric in numeric eq (==), converts the string to integer 9, the test succeeds and YES is printed.
To my knowledge it has always been this way, at least since v5.0.
It has been that way.
In the first if, l is considered to be in numeric context. However, l cannot be converted to a number. Therefore, a warning is emitted.
In the second if, the number 1 is considered to be in string context. Therefore the number 1 is converted to the string '1' before comparison and hence no warnings are emitted.
Did you use a lowercase "L" on purpose? It's often hard to tell the difference between a lowercase "L" and one. You would have answered your own question if you had used a one instead.
>perl -wE"say '1' == 0;"
>perl -wE"say 1 eq 'a';"
>
As you can see,
If one needs a number, Perl will convert a string to a number without warning.
If one needs a string, Perl will convert a number to a string without warning.
Very consistent.
You get a warning when you try to convert a lowercase L to a number, but how is that surprising?
could soemone help me with the following condition, please?
I'm trying to compare $price and $lsec.
if( (sprintf("%.2f", ($price*100+0.5)/100)*1 != $lsec*1) )
{
print Dumper($price,$lsec)
}
Sometimes the dumper prints same numbers(as strings) and jumps in.
Thought, that multiplying with 1 makes floats from them...
Here dumper output:
$VAR1 = '8.5';
$VAR2 = '8.5';
What am I doing wrong?
Thank you,
Greetings and happy easter.
There is a difference between what is stored in a Perl variable and how it is used. You are correct that multiplying by 1 forces a variable to be used as a number. It also causes the number to be stored in the SV data structure that represents the variable to the interpreter. You can use the Devel::Peek module to see what Perl has stored in each variable:
use Devel::Peek;
my $num = "8.5";
Dump $num;
outputs:
SV = PV(0xa0a46d8) at 0xa0c3f08
REFCNT = 1
FLAGS = (POK,pPOK)
PV = 0xa0be8c8 "8.5"\0
CUR = 3
LEN = 4
continuing...
my $newnum = $num * 1;
Dump $num;
Dump $newnum;
outputs:
SV = PVNV(0xa0a46d8) at 0xa0c3f08
REFCNT = 1
FLAGS = (PADMY,NOK,POK,pIOK,pNOK,pPOK)
IV = 8
NV = 8.5
PV = 0xa0be8c8 "8.5"\0
CUR = 3
LEN = 4
SV = NV(0x9523660) at 0x950df20
REFCNT = 1
FLAGS = (PADMY,NOK,pNOK)
NV = 8.5
The attributes we are concerned with are PV (string pointer), NV (floating-point number), and IV (integer). Initially, $num only has the string value, but using it as a number (e.g. in multiplication) causes it to store the numeric values. However, $num still "remembers" that it is a string, which is why Data::Dumper treats it like one.
For most purposes, there is no need to explicitly force the use of a string as a number, since operators and functions can use them in the most appropriate form. The == and != operators, for example, coerce their operands into numeric form to do numeric comparison. Using eq or ne instead forces a string comparison. This is one more reason to always use warnings in your Perl scripts, since trying to compare a non-numeric string with == will garner this warning:
Argument "asdf" isn't numeric in numeric eq (==) at -e line 1.
You are correct to say that multiplying a string by 1 will force it to be evaluated as a number, but the numeric != comparator will do the same thing. This is presumably a technique you have acquired from other languages as Perl will generally do the right thing and there is no need to force a cast of either operand.
Lets take a look at the values you're comparing:
use strict;
use warnings;
use Data::Dumper;
my $price = '8.5';
my $lsec = '8.5';
my $rounded_price = sprintf("%.2f", ($price * 100 + 0.5) / 100);
print "$rounded_price <=> $lsec\n";
if ( $rounded_price != $lsec ) {
print Dumper($price,$lsec);
}
output
8.51 <=> 8.5
$VAR1 = '8.5';
$VAR2 = '8.5';
So Perl is correctly saying that 8.51 is unequal to 8.5.
I suspect that your
($price * 100 + 0.5) / 100
is intended to round $price to two decimal places, but all it does in fact is to increase $price by 0.005. I think you meant to write
int($price * 100 + 0.5) / 100
but you also put the value through sprintf which is another way to do the same thing.
Either
$price = int($price * 100 + 0.5) / 100
or
$price = sprintf ".2f", $price
but both is overkill!
This part:
($price*100+0.5)/100)
If you put in 8.5, you get back 8.505. Which naturally is not equal to 8.5. Since you do not change $price, you do not notice any difference.
Perl handles conversion automatically, so you do not need to worry about that.
my $x = "8.5";
my $y = 8.5;
print "Equal" if $x == $y; # Succeeds
The nature of the comparison, == or in your case != converts the arguments to numeric, whether they are numeric or not.
You're doing nothing wrong. Perl converts it to a string before dumping it. For comparisons, use == and != for numeric comparisons and eq and ne for a string comparisons. Perl converts to strings and numbers as needed.
Example:
$ perl -MData::Dumper -e "my $a=3.1415; print Dumper($a);"
$VAR1 = '3.1415';