Comparing Floating-Point Numbers

Problem

Floating-point arithmetic isn’t precise. You want to compare two floating-point numbers and know if they’re equal when carried out to a certain number of decimal places. Most of the time, this is the way you should compare floating-point numbers for equality.

Solution

Use sprintf to format the numbers to a certain number of decimal places, then compare the resulting strings:

# equal(NUM1, NUM2, ACCURACY) : returns true if NUM1 and NUM2 are
# equal to ACCURACY number of decimal places

sub equal {
    my ($A, $B, $dp) = @_;

    return sprintf("%.${dp}g", $A) eq sprintf("%.${dp}g", $B);
  }

Alternatively, store the numbers as integers by assuming the decimal place.

Discussion

You need the equal routine because most computers’ floating-point representations aren’t accurate. See the Introduction for a discussion of this issue.

If you have a fixed number of decimal places, as with currency, you can sidestep the problem by storing your values as integers. Storing $3.50 as 350 instead of 3.5 removes the need for floating-point values. Reintroduce the decimal point on output:

$wage = 536;                # $5.36/hour
$week = 40 * $wage;         # $214.40
printf("One week's wage is: \$%.2f\n", $week/100);


                  One week's wage is: $214.40

It rarely makes sense to compare to more than 15 decimal places.

See Also

The sprintf function in perlfunc (1) and Chapter 3 of Programming Perl ; the entry on $# in the perlvar(1) manpage and Chapter 2 of Programming Perl; the documentation for the standard Math::BigFloat ...

Get Perl Cookbook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.