views:

1185

answers:

4

Consider the following code and its output:

Code

#!/usr/bin/perl -w
use strict;
use Data::Dumper;
my $HOURS_PER_DAY = 24.0 * 1.0;
my $BSA = 1.7 * 1.0;
my $MCG_PER_MG = 1000.0 * 1.0;
my $HOURS_DURATION    = 20.0 * $HOURS_PER_DAY;
my $dummy = $HOURS_PER_DAY * $BSA * $MCG_PER_MG * $HOURS_DURATION;
print Dumper($HOURS_PER_DAY);
print Dumper( $BSA);
print Dumper( $MCG_PER_MG);
print Dumper( $HOURS_DURATION );

Output

$VAR1 = 24;
$VAR1 = '1.7';
$VAR1 = 1000;
$VAR1 = 480;


As you can see, the second variable is treated as strings, while the first and the forth ones are treated as numbers. Anybody has any idea what is the underlying logic?

Edit arithmetic calculations that were added do not completely solve the problem (see the $BSA variable).


$ perl -v

This is perl, v5.10.0 built for cygwin-thread-multi-64int
(with 6 registered patches, see perl -V for more detail)

Copyright 1987-2007, Larry Wall
+3  A: 

Data::Dumper is using a simple pattern on the string representation of the variable to determine if it is a number. From the source code:

...
elsif ($val =~ /^(?:0|-?[1-9]\d{0,8})\z/) { # safe decimal number
  $out .= $val;
}
else {      # string
...

This doesn't match numbers that have a decimal point which explains the behavior you observed.

Robert Gamble
Perl does NOT remember the last use of a variable. It does remember when a variable is both a valid integer, float or string when either of those is used. *However this does not affect the semantics of the variable* (except in two cases, bitwise operators and syscall).
Leon Timmermans
You are correct, I misremembered how this actually worked, thanks.
Robert Gamble
A: 

Quick dirty way to force a numeric context:

print Dumper( $HOURS_DURATION + 0.0 );

If your concern is how the data will be displayed then the clean way is:-

printf "%5.2d",$HOURS_DURATION;
James Anderson
+5  A: 

Your whole concept of Perl treating variables as strings or numbers is flawed. Perl will treat your variables the right way when you need it to, for example, arithmetic operators always take treat their argument as numbers (assuming you aren't abusing operator overloading or some such).

You shouldn't worry about this: Perl knows what it's doing.

Leon Timmermans
Thanks. I wasn't worrying. I was just curious.
bgbg
+5  A: 

Data::Dumper's job is to serialize data and you can't tell much about what perl is doing internally with the data based on its output. The Devel::Peek module can dump the underlying flags and values stored in the variables. The Devel::Peek POD explains the significance of the flags.

#!/usr/bin/perl

use warnings;
use strict;
use Devel::Peek;

my $HOURS_PER_DAY = 24.0 * 1.0;
my $BSA = 1.7 * 1.0;
my $MCG_PER_MG = 1000.0 * 1.0;
my $HOURS_DURATION    = 20.0 * $HOURS_PER_DAY;
my $dummy = $HOURS_PER_DAY * $BSA * $MCG_PER_MG * $HOURS_DURATION;

Dump($HOURS_PER_DAY);
Dump($BSA);
Dump($MCG_PER_MG);
Dump($HOURS_DURATION);

__END__
SV = PVNV(0xd71ff0) at 0xd87f90
  REFCNT = 1
  FLAGS = (PADBUSY,PADMY,IOK,NOK,pIOK,pNOK)
  IV = 24
  NV = 24
  PV = 0
SV = PVNV(0xd71fc8) at 0xd87f60
  REFCNT = 1
  FLAGS = (PADBUSY,PADMY,NOK,pIOK,pNOK)
  IV = 1
  NV = 1.7
  PV = 0
SV = PVNV(0xd72040) at 0xd87f40
  REFCNT = 1
  FLAGS = (PADBUSY,PADMY,IOK,NOK,pIOK,pNOK)
  IV = 1000
  NV = 1000
  PV = 0
SV = IV(0xd8b408) at 0xd87f30
  REFCNT = 1
  FLAGS = (PADBUSY,PADMY,IOK,pIOK)
  IV = 480
# compare the above output to output without the assignment to $dummy:
SV = IV(0x7b0eb8) at 0x7adf90
  REFCNT = 1
  FLAGS = (PADBUSY,PADMY,IOK,pIOK)
  IV = 24
SV = NV(0x7c7c90) at 0x7adf60
  REFCNT = 1
  FLAGS = (PADBUSY,PADMY,NOK,pNOK)
  NV = 1.7
SV = IV(0x7b13d8) at 0x7adf40
  REFCNT = 1
  FLAGS = (PADBUSY,PADMY,IOK,pIOK)
  IV = 1000
SV = IV(0x7b1408) at 0x7adf30
  REFCNT = 1
  FLAGS = (PADBUSY,PADMY,IOK,pIOK)
  IV = 480
converter42