Converting strings to floats - perl

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';

Related

Why do `join` and/or `JSON::to_json` convert my data silently from integer to string?

I don't understand why join changes the output of JSON::to_string in the following example:
#!/usr/bin/perl
use v5.26;
use Data::Dumper;
use JSON;
my #version = (1, 2, 3, 4);
say "version: ", join ".", #version; # comment this line out
$Data::Dumper::Terse = 1;
$Data::Dumper::Indent = 0;
say Dumper(\#version);
say to_json(\#version);
The output with the line containing join:
version: 1.2.3.4
[1,2,3,4]
["1","2","3","4"]
But commenting out the line with join the output of to_json suddenly shows integers instead of strings although the output of Data::Dumper is still the same:
[1,2,3,4]
[1,2,3,4]
When you stringify a number, the stringification is stored in the scalar along with the origin number. (You can see a demonstration at the bottom of my answer.)
When you numify a string, the numification is stored in the scalar along with the origin number.
This is an optimization since one often stringify or numify a scalar more than once.
This isn't a problem for Perl since Perl has coercing operators rather than polymorphic operators. But it puts the authors of JSON serializers in the difficult positions of either requiring additional information or guessing which of the values a scalar contains should be used.
You can force a number using $x = 0 + $x;.
You can force a string using $x = "$x";.
More detailed answer follows.
Perl is free to change internal format a scalar as it sees fit. This is usually done as part of modifying the scalar.
$x = 123; # $x contains a signed integer
$x += 0.1; # $x contains a float
$x = 2147483647; # $x contains a signed integer
++$x; # $x contains an unsigned integer (on a build with 32-bit ints)
$x = "123"; # $x contains a downgraded string
$x += 0; # $x contains a signed integer
$x = "abc"; # $x contains a downgraded string
$x .= "\x{2660}"; # $x contains an upgraded string
But sometimes, Perl adds a second value to an scalar as an optimization.
$x = 123; # $x contains a signed integer
$x * 0.1; # $x contains a signed integer AND a float
$x = 123; # $x contains a signed integer
"$x"; # $x contains a signed integer AND a downgraded string
$x = "123"; # $x contains a downgraded string
$x+0; # $x contains a signed integer AND a downgraded string
These aren't the only double (or triple) vars you'll encounter.
my $x = !!0; # $x contains a signed integer AND a float AND a downgraded string
"$!"; # $! contains a float (not a signed integer?!) AND a downgraded string
This isn't a problem in Perl because we use type-coercing operators (e.g. == works on numbers, eq works on strings). But many other languages rely on polymorphic operators (e.g. == can be used to compare strings and to compare numbers).[1]
But it does present a problem for JSON serializers which are forced to assign a single type to a scalar. If $x contains both a string a number, which one should be used?
If the scalar is the result of stringification, using the number would be ideal, but if the scalar is the result of numification, the string would be ideal. There's no way to tell which of these origins pertains to a scalar (if any), so the module's author was left with a tough choice.
Ideally, they would have provided a different interface, but that could have added complexity and a performance penalty.
You can view the internals of a scalar using Devel::Peek's Dump. The relevant line is the FLAGS line.
IOK without IsUV: contains a signed integer
IOK with IsUV: contains an unsigned integer
NOK: contains a float
POK without UTF8: contains a downgraded string
POK with UTF8: contains an upgraded string
ROK: contains a reference
$ perl -MDevel::Peek -e'$x=123; Dump($x); "$x"; Dump($x);' 2>&1 |
perl -M5.014 -ne'next if !/FLAGS/; say join ",", /\b([INPR]OK|IsUV|UTF8)/g'
IOK
IOK,POK
$ perl -MDevel::Peek -e'$x="123"; Dump($x); 0+$x; Dump($x);' 2>&1 |
perl -M5.014 -ne'next if !/FLAGS/; say join ",", /\b([INPR]OK|IsUV|UTF8)/g'
POK
IOK,POK
Well, Perl doesn't have separate operators for the different numeric types, which can cause issues (e.g. -0 exists a float, but not as an int), but these problems are seldom encountered.
Another issue is that the stringification of floats often results in a loss of information.
This is one of the very few times where you must maintain data purity in Perl. Once you create a variable of some type, you must never use it in a context of any other type. If you do need to, copy it to a new variable first to preserve the original.
use feature 'say';
use Data::Dumper;
use JSON;
my #version = (1, 2, 3, 4);
{ say "version: ", join ".", my #copy = #version; }
$Data::Dumper::Terse = 1;
$Data::Dumper::Indent = 0;
say Dumper(\#version);
say to_json(\#version);
Prints:
version: 1.2.3.4
[1,2,3,4]
[1,2,3,4]
I would also recommend using Cpanel::JSON::XS because this is one area where pedantism is called for! It tries pretty hard to get the data types right. It also has some discussion of the conversion issue.
HTH

How to enhance the operator "+" in perl

Would like to enhance the operator "+" in perl so that it will be regular add operation if both operands are numbers
$a = 11;
$b = 12;
$x = $a + $b; #expect it to be 23
If one operand is a string, then convert all operands as string and treat them as concatenation.
$a = 11;
$b = " cars";
$x = $a + $b; #expect it to be "11 cars"
If this is not possible, wonder if it's possible for me to create a new operator to do it.
Perl makes no distinction between variable types. This is pretty much in line with how shell scripts and awk scripts work. A variable might be a number in one place in your program and a string in another place. Perl hopes that you know what you're doing:
#! /usr/bin/env perl
#
use strict;
use warnings;
use feature qw(say);
my $vendor_id = "344";
my $inventory_item = "23942";
print "You want to order item ";
print $vendor_id + $inventory_item;
print "\n";
This will print:
You want to order item 24286
Which added my $vendor_id to my $inventory_item. What you probably wanted to do was to concatenate them.
You want to order item 34423942
There's no way Perl can tell if you meant a variable to be a string or number, so Perl forces you to use operators. The + is a numeric addition while . is a string concatenation. In many languages, this would be the same operator, but in Perl, we need two distinct operators. To do what you want, you would have done this:
print $vendor_id . $inventory_item;
vs.
print $vendor_id + $inventory_item;
This is why Perl has two completely different sets of boolean operators: One set for numeric comparisons and one for strings. This gives Perl great flexibility because a variable can be treated as a number in one place, then as a string in another. However, it also means you better know what your variable is suppose to represent.
In Python, Java, Swift, and in many other languages, you declare a variable as either a string or a number. Beware if you declare a variable as a string and try to add them, or declare them as some form of numeric data and try to concatenate them. In most languages, you have to cast variables from one form to another using either methods (like int2sting) or a declaration.
If you truly don't know your data, and are not sure what it represents, you can use looks_like_number from Scalar::Util. This isn't a builtin function, but Scalar::Util is a standard module, and is probably already in your Perl distribution:
use strict;
use warnings;
use feature qw(say);
use Scalar::Util qw(looks_like_number);
my $first = "11";
my $second = " of cars";
if ( looks_like_number($first) and looks_like_number($second) ) {
say $first + $second; # Add the two numbers together
}
else {
say $first . $second; # At least one is a string, so concatenate them
}
Now, if both $first and $second look like numbers, they'll be added. However, if one or the other doesn't look like a number, they'll be concatenated.
There are Perl modules (and I can't remember them) that will turn Perl into a declarative language where variables are told they're a string, integer, floating point number, etc. Most of these attempts are incomplete for many reasons. For example, you can't stop someone from using the more ambiguous built in syntax.
If you have more advanced Perl knowledge, you can create your own objects types, and use the overload pragma to overload the operation definition for each class type:
use strict;
use warnings;
use feature qw(say);
use Object::Perl; # Hypothetical module
my $first = Object::Perl::String->new(11);
my $second = Object::Perl::String->new(" cars");
say $first + $second; # Overloaded "+" to concatenate two Object::Perl::String types
$first = Object::Perl::Int->new(11);
$second = Object::Perl::Int->new(12);
say $first + $second; $ Overloaded "+" to add two Object::Perl::Int types
However, if you're going through all that trouble, you might as well learn Python.
That makes no sense. For example, what should the following output?
my $x = 11; print("$x\n");
my $y = 12; print("$y\n");
print($x+$y, "\n");
my $m = "11"; print(0+$m, "\n");
my $n = "12"; print(0+$n, "\n");
print($m+$n, "\n");
Keep in mind that $x and $m are identical (they both contain a number and a string), and so are $y and $n.
SV = PVIV(0x16f2dd8) at 0x16fe8b0 $x
REFCNT = 1
FLAGS = (PADMY,IOK,POK,pIOK,pPOK)
IV = 11
PV = 0x16f7b30 "11"\0
CUR = 2
LEN = 16
SV = PVIV(0x16f2e20) at 0x16fea60 $m
REFCNT = 1
FLAGS = (PADMY,IOK,POK,pIOK,pPOK)
IV = 11
PV = 0x17086b0 "11"\0
CUR = 2
LEN = 16
SV = PVIV(0x16f2df0) at 0x16fe8f8 $y
REFCNT = 1
FLAGS = (PADMY,IOK,POK,pIOK,pPOK)
IV = 12
PV = 0x171eee0 "12"\0
CUR = 2
LEN = 16
SV = PVIV(0x16f2e08) at 0x170eee8 $n
REFCNT = 1
FLAGS = (PADMY,IOK,POK,pIOK,pPOK)
IV = 12
PV = 0x17320e0 "12"\0
CUR = 2
LEN = 16

Difference between $var = 500 and $var = '500'

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.

Assigning a string to Perl substr?

I am looking at Perl script written by someone else, and I found this:
$num2 = '000000';
substr($num2, length($num2)-length($num), length($num)) = $num;
my $id_string = $text."_".$num2
Forgive me ignorance, but for an untrained Perl programmer the second line looks as if the author is assigning the string $num to the result of the function substr. What does this line exactly do?
Exactly what you think it would do:
$ perldoc -f substr
You can use the substr() function as an lvalue, in which case
EXPR must itself be an lvalue. If you assign something shorter
than LENGTH, the string will shrink, and if you assign
something longer than LENGTH, the string will grow to
accommodate it. To keep the string the same length, you may
need to pad or chop your value using "sprintf".
In Perl, (unlike say, Python, where strings, tuples are not modifiable in-place), strings can be modified in situ. That is what substr is doing here, it is modifying only a part of the string. Instead of this syntax, you can use the more cryptic syntax:
substr($num2, length($num2)-length($num), length($num),$num);
which accomplishes the same thing. You can further stretch it. Imagine you want to replace all instances of foo by bar in a string, but only within the first 50 characters. Perl will let you do it in a one-liner:
substr($target,0,50) =~ s/foo/bar/g;
Great, isn't it?
"Exactly", you ask?
Normally, substr returns a boring string (PV with POK).
$ perl -MDevel::Peek -e'$_="abcd"; Dump("".substr($_, 1, 2));'
SV = PV(0x99f2828) at 0x9a0de38
REFCNT = 1
FLAGS = (PADTMP,POK,pPOK)
PV = 0x9a12510 "bc"\0
CUR = 2
LEN = 12
However, when substr is evaluated where an lvalue (assignable value) is expected, it returns a magical scalar (PVLV with GMG (get magic) and SMG (set magic)).
$ perl -MDevel::Peek -e'$_="abcd"; Dump(substr($_, 1, 2));'
SV = PVLV(0x8941b90) at 0x891f7d0
REFCNT = 1
FLAGS = (TEMP,GMG,SMG)
IV = 0
NV = 0
PV = 0
MAGIC = 0x8944900
MG_VIRTUAL = &PL_vtbl_substr
MG_TYPE = PERL_MAGIC_substr(x)
TYPE = x
TARGOFF = 1
TARGLEN = 2
TARG = 0x8948c18
FLAGS = 0
SV = PV(0x891d798) at 0x8948c18
REFCNT = 2
FLAGS = (POK,pPOK)
PV = 0x89340e0 "abcd"\0
CUR = 4
LEN = 12
This magical scalar holds the parameters passed to susbtr (TARG, TARGOFF and TARGLEN). You can see the scalar pointed by TARG (the original scalar passed to substr) repeated at the end (the SV at 0x8948c18 you see at the bottom).
Any read of this magical scalar results in an associated function to be called instead. Similarly, a write calls a different associated function. These functions cause the selected part of the string passed to substr to be read or modified.
perl -E'
$_ = "abcde";
my $ref = \substr($_, 1, 3); # $$ref is magical
say $$ref; # bcd
$$ref = '123';
say $_; # a123e
'
Looks to me like it's overwriting the last length($num) characters of $num2 with the contents of $num in order to get a '0' filled number.
I imagine most folks would accomplish this same task w/ sprintf()

check if a number is int or float

In perl, I want to check if the given variable holds a floating point number of not. To check this I am using,
my $Var = 0.02 # Floating point number
if (int($Var) != $Var) {
# floating point number
}
But the above code will not work for 0.0,
How can I achieve this?
This is a FAQ. See How do I determine whether a scalar is a number/whole/integer/float?
You can also use Scalar::Util::Numeric.
Interestingly, while
#!/usr/bin/perl
use strict; use warnings;
use Scalar::Util::Numeric qw(isint);
my $x = 0.0;
print "int\n" if isint $x;
prints int (not surprising if you look at the source code), based on #tchrist's comment, it should be possible to look at the information supplied in the variables structure to distinguish 0 from 0.0:
#!/usr/bin/perl
use strict; use warnings;
use Devel::Peek;
my $x = 0.0; print Dump $x;
my $y = 0; print Dump $y;
$y *= 1.1; print Dump $y;
Output:
SV = NV(0x18683cc) at 0x182c0fc
REFCNT = 1
FLAGS = (PADMY,NOK,pNOK)
NV = 0
SV = IV(0x182c198) at 0x182c19c
REFCNT = 1
FLAGS = (PADMY,IOK,pIOK)
IV = 0
SV = PVNV(0x397ac) at 0x182c19c
REFCNT = 1
FLAGS = (PADMY,NOK,pNOK)
IV = 0
NV = 0
PV = 0
It seems to me that the check would have to just look at if the NOK flag is set. It takes me ages to write even the simplest XS so I won't provide an implementation.
($Var - int($Var))?'float':'int'
You can use autobox::universal module wich is part of autobox.
#! /usr/bin/perl
use autobox::universal qw(type);
my $x = 42;
print type($x), "\n";;
$x = 42.0;
print type($x), "\n";
Output:
INTEGER
FLOAT
Why not use regex?
print "Is INT\n" if ($test =~/-*\d+/);
print "Is FLOAT\n" if ($test =~/-*\d+\.\d+/);
#$test = "1234" = INT
#$test = "12.3" = FLOAT
Re-edited 2019-12-24 to work correctly in Perl (MHN)
Perl5 return true or false if a variable string is a float:
You have to either roll your own by being clever with regexes and handle edge cases, or else defer to a 3rd party library and catch and suppress exceptions thrown by them.
sub does_this_variable_look_like_a_perl_float {
$number_of_arguments = 0 + #_;
#If you passed too many arguments, exit program, it's certainly not a float
if ($number_of_arguments != 1){
print "ArgumentException, you passed an incorrect number of parameters.\n";
return 0;
}
$variable = $_[0];
$first_argument = shift;
if ( ref(\$first_argument) eq 'ARRAY') {
#arrays are not floats
print("arrays are not floats");
return 0;
}
if ($variable =~ m/^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/){
return 1;
}
return 0;
}
sub is_convertable_to_float__casting_and_killing_canaries {
$number_of_arguments = 0 + #_;
if ($number_of_arguments != 1){
print "ArgumentException, you passed an incorrect number of parameters.\n";
return 0;
}
my $result = 0;
$variable = $_[0];
use Error qw(:try);
try {
use Scalar::Util qw(blessed dualvar isdual readonly refaddr reftype
tainted weaken isweak isvstring looks_like_number
set_prototype);
$numeric_code = looks_like_number( $variable );
if ($numeric_code != 0){
$result = 1;
}
else{
$result = 0;
}
}
catch Error with { $result = 0; };
return $result;
}
sub is_float {
#Darkwing Duck says "I like Perl5's duck typing, so it's a float when I
#look at it with my regex goggles, and it overlays yes"
return does_this_variable_look_like_a_perl_float(#_);
#Beta-7 insists on a practical solution, the variable is a float when I ask
#a 3rd party library if it is a float, and the 3rd party classifies it
#as a float, without any major malfunctions or trapped bubble up implimentationitis.
#return is_convertable_to_float__casting_and_killing_canaries(#_);
}
#NO:
print(is_float("")); #blankstring
print(is_float("yeah")); #strings with ascii letters
print(is_float(" ")); #space whitespace
print(is_float("\t\n")); #tabs and newlines
print(is_float(" 58 ")); #whitespace on either side
print(is_float('e')); #e by itself
print(is_float('0xf')); #0x hexidecimal
print(is_float('\xf')); #\x hexidecimal
print(is_float("1,234.56")); #commas as thousands separator
print(is_float("35,5")); #European, East oriental comma
print(is_float(undef)); #undef and variants
print(is_float("NaN")); #nan and variants
print(is_float("inf")); #nan and variants
print(is_float("infinity")); #inf and variants
print(is_float("null")); #null and variants
print(is_float("12.34.56")); #double dots
print(is_float("四")); #unicode, oriental japanese 4
print(is_float("#56")); #Pound sign
print(is_float("56%")); #percent sign
print(is_float("56^3")); #arithmatic expressions not interpreted
print(is_float("+1e1.5")); #decimals in exponent
print(is_float("+-1")); #make up your mind
print("\n");
#YES:
print(is_float(35)); #bare integer
print(is_float(35.5)); #decimal numeric, typical float
print(is_float(35.00000)); #superfluous zeros to the right
print(is_float("35")); #bare integer packaged as string
print(is_float(0)); #integer zero
print(is_float(07)); #leading zero makes for octal
print(is_float(000)); #stealth octal zero
print(is_float(-13)); #negative numbers
print(is_float(12e2)); #integers with e scientific notation
print(is_float(12.2e2)); #float with scientific notation
print(is_float(12.E4)); #float with e after period and scientific notation
print(is_float(.4)); #mantissa only
print(is_float(6e7777777777777)); #huge number
print(is_float(1.797693e+308)); #max value
print(is_float("0E0")); #zero in exponential
print(is_float(0**0)); #exponentiation
print(is_float("-5e-5")); #raise a negative to a negative
print(is_float("+5e+5")); #raise a positive with plus to a positive
print(is_float(0xfade)); #valid hexidecimal converted before pass by value
print(is_float(0b1100_0000)); #valid binary converted before pass by value
print("\n");
#ERROR:
print(is_float(5,6,7))
my #s = (10,20,30);
print(is_float(#s));
$arr = (5, 'foo', 7);
print(is_float($arr)); #an array containing integers and strings
print(is_float((1, 2, 3))); #error, array is not float
print(is_float(35,5)); #error, comma is not a decimal here, that's 2 parameters
Results of 'does_this_variable_look_like_a_perl_float():
0000000000000000000000
11111111111111111111
Results of 'is_convertable_to_float__casting_and_killing_canaries():
0000100000011100000000
11111111111111111111
I'm unsure if you are wishing to fish a number out of the variable or whether you wish to check if the variable IS (well, can be) a float or int.
However, to build on the regex example above, its mostly correct, minus a couple tweaks.
#original code snippet
print "Is INT\n" if ($test =~/-*\d+/);
print "Is FLOAT\n" if ($test =~/-*\d+\.\d+/);
#$test = "1234" = INT
#$test = "12.3" = FLOAT
if test is this however...
$test = "123.asd23aa"; # IS AN INT!!!!
try this instead:
$test = "123.asd23aa";
print "Is INT\n" if ($test =~/-*\d+/); #NO ANCHORS, -* allows for multiple --
print "Is INT\n" if ($test =~/^-?\d+$/); # WITH ANCHORS, fixed -?
print "Is FLOAT\n" if ($test =~/^-?\d*\.\d+$/); #AS ABOVE, with \d* instead of \d+ (floats need not have a leading 0 . can start with .5 or -.5 etc)
and here are some easy subs you can drop into whatever tool box you might have
sub isInt{
return ($_[0] =~/^-?\d+$/)?1:0;
}
sub isFloat{
return ($_[0] =~/^-?\d*\.\d+$/)?1:0;
}
sub isNum{
return (isInt($_[0]) || isFloat($_[0]) )?1:0;
}
This should do what you need:
my $var = 0.02 # Floating point number
if ($var > int($var)) {
# is floating point number because int() always rounds down
}
If you need to test for 0.00 in your application, you could use:
my $var = 0.00 # Floating point number
if ($var > int($var) || length($var) > length(int($var)) {
# is floating point number because int() always rounds down
# or string length is longer than integer string
}