perl - int() decrementing an integer

116 views Asked by At

Before I get flamed, I want to say I do understand floating point numbers and things of the sort, but that doesn't seem to be my issue.

To simplify things, I'm trying to determine if a number has more than 2 decimal places. I'm doing this by multiplying the number by 100 (stored under variable "test1") and then truncating it with int() ($test2) and comparing it with an if.

$test1 = $number * 100;
$test2 = int($test1);
unless ($test1 == $test2) {
  die ("test1:$test1, test2:$test2");
}

The initial $number comes from a whole series of other functions and should realistically be only two decimals, hence I'm trying to catch those that aren't (as a few entries seem to have very many decimals).

However, I just got:

test1:15, test2:14

from my die().

Can someone explain how that would happen? How can int(15) be 14?

3

There are 3 answers

0
David On BEST ANSWER

From perldoc:

machine representations of floating-point numbers can sometimes produce counterintuitive results. For example, int(-6.725/0.025) produces -268 rather than the correct -269; that's because it's really more like -268.99999999999994315658 instead

So, the machine representation of "15" is probably something like 14.9999999999999999 and, therefore, int truncates it to 14.

Note that perldoc suggests using the POSIX functions floor or ceil instead.

0
brian d foy On

If I wanted to check if a number had more than two decimal places, I wouldn't do math on it.

my $more_than_two = $number =~ /\d+\.\d{2}\d+\z/;

Before I do that, I might use Scalar::Util's looks_like_a_number. This method will still fail with floating point squishiness if you were expecting 14.99999 to be 15.0.

However, you should tell us what you are trying to do instead of how you are trying to do that. It's easier to give better answers.

For your questions about int, I think it's documentation tell you what you need to know. The rest is answered in the first couple of questions in perlfaq4.

0
G. Cito On

In a simple, one off, case adding 0.5 to your value before int-ing it will give you what you want.

e.g.

 int(14.99 + 0.5) 
 15 

it becomes 15.49 and is int-ed "down" to 15, whereas:

 int( 14.45 + 0.5 ) 

still gets int'ed "down" to 14.0. This is a handy trick but doesn't self document as nicely as using floor and ceil.

As a side note, the Goldberg paper on floating point arithmetic always reminds me how useful it sometimes is to have brains that are not as mindlessly precise as a computer :-)