Re: Integer vs Float during addition
by Zaxo (Archbishop) on Jun 29, 2006 at 02:00 UTC
|
It's even simpler than that,
$str1 = 0.00;
print $str1, "\n";
__END__
0
Perl doesn't enforce any native strong typing, and it freely converts between types. Once experience tells you what to expect, it's liberating to forget about casts, atof and friends.
Zero is zero, and Perl has its own ideas about how to display it.
| [reply] [d/l] |
Re: Integer vs Float during addition
by blokhead (Monsignor) on Jun 29, 2006 at 02:08 UTC
|
You'd get the same output without the addition:
my $float = 0.00;
print "$float\n"; # prints "0"
Note that what you're doing when printing is coercing a number to a string. Perl's principle is to do this in the most basic way it can. If the number is technically a float internally, but its decimal expansion is .0000..., its string conversion will just look like an int. If you want it to look different, consider sprintf.
| [reply] [d/l] |
Re: Integer vs Float during addition
by chrism01 (Friar) on Jun 29, 2006 at 04:27 UTC
|
Actually, that was a cleaned-up example, the actual one was using
if( $cost)
{
....
}
but I was getting different results (ie if() was true/false) depending on whether the $cost of 0.00 was extracted via DBI::MYSQL and then left untouched, or extracted via DBI::MYSQL and then added to an integer zero.
Took me a while to figure out why the optional code path was /was not being followed.
ie not just an academic qn and nothing to do with printing as such.
Cheers
Chris | [reply] [d/l] |
|
|
Ok, you took it the opposite direction. Boolean context doesn't force anything. Print forces to string. 0+ forces to number.
The string '0.00' is true, but numeric zero is false.
| [reply] |
|
|
More specifically, string '0.00' is true but string '0' is false. This tends to surprise C-to-Perl newcomers. So if someone got a string and they weren't sure if it was natural or sprintf-formatted, it could lead to bugs in logic.
use Data::Dumper;
for (0, '', '0', '00', '0.0', '0.00', undef) {
print (($_? 'TRUE' : 'FALSE'), ': ', Dumper($_));
}
-- [ e d @ h a l l e y . c c ]
| [reply] [d/l] |
|
|
if ( NumericTrue( $cost ) ) {
...
}
sub NumericTrue{
return shift()*1.0;
}
(Updated to allow e.g. 99 cents to be a cost.)
| [reply] [d/l] |
|
|
| [reply] [d/l] |
Re: Integer vs Float during addition
by Ieronim (Friar) on Jun 29, 2006 at 12:07 UTC
|
According to Perl Cookbook, Perl interpolates the floating-point numbers in stings using the "%.15g" format; and
$str1 = 0.00;
print "str1 $str1\n";
is equal to
printf "str1 %.15g\n", 0.00;
and prints the same
str1 0
.
Updated to look cleaner. | [reply] [d/l] [select] |
Re: Integer vs Float during addition
by Moron (Curate) on Jun 29, 2006 at 11:44 UTC
|
Expect instead that Perl will behave according to Perl documentation not C documentation. The requirement you describe (conformance to a fixed number of decimal places, rather than significant digits) conforms to the notion of fixed point arithmetic - floating point means that only significant digits are shown stored, not any zeroes after the point. (COBOL, not C, is the legacy language that most readily springs to mind for supporting fixed point arithmetic.)
Perl is quite capable of handling such storage formats however. If you have such specific storage requirements for a Perl program, it is usually best to take charge of how data is stored and converted using the pack and unpack functions. But if it is just a matter of formatting, the printf and sprintf functions can do that for you.
| [reply] |
Re: Integer vs Float during addition
by chrism01 (Friar) on Jun 30, 2006 at 01:07 UTC
|
To Zaxo & Halley
Actually, I already knew all that 0 vs 0.0, true vs false stuff. :-)
My query was that given $cost starts as a float and then sometimes has an integer added to it, why is the result integer, not float? I'm sure that in the background int is promoted to float for the actual calc, and given the tgt is already a float, I'd expect a float result?
Surely that's a reduction of the variable?
As implied above, this code I inherited(!) doesn't print the value, it's just used for a boolean test.
Cheers
Chris
I'm just trying to understand the internals a bit more.
| [reply] |
|
|
I'm just trying to understand the internals a bit more
It's often enlightening (and fun) to use Devel::Peek to get a glimpse of what the scalar actually looks like:
use warnings;
use Devel::Peek;
$int = 17;
#print "\$int: $int\n";
Dump($int);
print "##############\n\n";
$double_1 = 1.23456789;
#print "\$double_1: $double_1\n";
Dump($double_1);
print "##############\n\n";
$double_2 = 1.0000;
#print "\$double_2: $double_2\n";
Dump($double_2);
print "##############\n\n";
$ul = 2 ** 31 + 16; # too big for an IV
#print "\$ul: $ul\n";
Dump($ul);
print "##############\n\n";
$double_3 = 2 ** 46 + 19; # too big for a UV or IV
#print "\$double_3: $double_3\n";
Dump($double_3);
print "##############\n\n";
$int += 0.0123; # $int no longer an IV
#print "\$int: $int\n";
Dump($int);
print "##############\n\n";
Which outputs: D:\pscrpt>perl try.pl
SV = IV(0x89c7cc) at 0x3f5d3c
REFCNT = 1
FLAGS = (IOK,pIOK)
IV = 17
##############
SV = NV(0x8afcb4) at 0x3f5d24
REFCNT = 1
FLAGS = (NOK,pNOK)
NV = 1.23456789
##############
SV = NV(0x8afcbc) at 0x8a3204
REFCNT = 1
FLAGS = (NOK,pNOK)
NV = 1
##############
SV = IV(0x89c7c8) at 0x8ba4d8
REFCNT = 1
FLAGS = (IOK,pIOK,IsUV)
UV = 2147483664
##############
SV = NV(0x8afcc4) at 0x8a2fdc
REFCNT = 1
FLAGS = (NOK,pNOK)
NV = 70368744177683
##############
SV = PVNV(0x3f8714) at 0x3f5d3c
REFCNT = 1
FLAGS = (NOK,pNOK)
IV = 17
NV = 17.0123
PV = 0
##############
Note that $double_2 (assigned a value of 1.000) is an NV (perl's idea of a double) as you expected, but the actual value in the NV slot has been truncated to simply '1' ... when you 'print $double_2;', it's the value in the NV slot that gets printed.
$ul is assigned a value that's too big to be a signed int, so it gets assigned to the UV slot, and the IsUV flag gets set. And $double_3 is assigned an integer value that can only fit into a double ... so, naturally, it has to be an NV.
Note also that $int, which starts out as an IV, becomes an NV as soon as you add a float to it - and that the original IV value remains in the IV slot. It's the fact that the NOK and pNOK flags are set that determines that the NV value (rather than the value in the IV slot) will be displayed if you then 'print $int;'.
Furthermore, if you enable those print statements that are currently commented out, you'll see that the various Dump() outputs change.
Devel::Peek doesn't really explain anything, but it does let you see how the scalars are changing - and from that you can often come to a "good enough" understanding of what's happening. If you want a thorough understanding then you probably need to examine the perl source code.
(And, of course, I always find that Devel::Peek throws up something unexpected - eg I don't know why doing '$int += 0.0123;' sets the PV slot to 0.)
Cheers, Rob | [reply] [d/l] [select] |