Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting strings to floats

Tags:

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.

like image 574
user897237 Avatar asked Oct 28 '25 14:10

user897237


2 Answers

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.
like image 97
bonsaiviking Avatar answered Oct 31 '25 13:10

bonsaiviking


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!

like image 25
Borodin Avatar answered Oct 31 '25 14:10

Borodin



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!