# Recipe 2.3 Comparing Floating-Point Numbers

#### 2.3.1 Problem

Floating-point arithmetic isn't exact. You want to compare two floating-point numbers and know whether 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.

#### 2.3.2 Solution

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

```# equal(NUM1, NUM2, PRECISION) : returns true if NUM1 and NUM2 are
# equal to PRECISION 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.

#### 2.3.3 Discussion

You need the equal routine because computers' floating-point representations are just approximations of most real numbers, as we discussed in the Introduction to this chapter. Perl's normal printing routines display numbers rounded to 15 decimal places or so, but its numeric tests don't round. So sometimes you can print out numbers that look the same (after rounding) but do not test the same (without rounding).

This problem is especially noticeable in a loop, where round-off error can silently accumulate. For example, you'd think that you could start a variable out at zero, add one-tenth to it ten times, and end up with one. Well, you can't, because a base-2 computer can't exactly represent one-tenth. For example:

```for (\$num = \$i = 0; \$i < 10; \$i++) { \$num += 0.1 }
if (\$num != 1) {
printf "Strange, \$num is not 1; it's %.45f\n", \$num;
}```

prints out:

`Strange, 1 is not 1; it's 0.999999999999999888977697537484345957636833191`

The \$num is interpolated into the double-quoted string using a default conversion format of "%.15g" (on most systems), so it looks like 1. But internally, it really isn't. If you had checked only to a few decimal places, for example, five:

`!equal(\$num, 1, 5)`

then you'd have been okay.

If you have a fixed number of decimal places, as with currency, you can often 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 more than 15 decimal places, because you probably only have that many digits of precision in your computer's hardware.

The sprintf function in perlfunc(1) and Chapter 29 of Programming Perl; the entry on \$OFMT in the perlvar(1) manpage and Chapter 28 of Programming Perl; the documentation for the standard Math::BigFloat module (also in Chapter 32 of Programming Perl); we use sprintf in Recipe 2.2; Volume 2, Section 4.2.2 of The Art of Computer Programming

 Chapter 1. Strings
 Chapter 3. Dates and Times
 Chapter 4. Arrays
 Chapter 5. Hashes
 Chapter 6. Pattern Matching
 Chapter 7. File Access
 Chapter 8. File Contents
 Chapter 9. Directories
 Chapter 10. Subroutines
 Chapter 11. References and Records
 Chapter 12. Packages, Libraries, and Modules
 Chapter 13. Classes, Objects, and Ties
 Chapter 14. Database Access
 Chapter 15. Interactivity
 Chapter 16. Process Management and Communication
 Chapter 17. Sockets
 Chapter 18. Internet Services
 Chapter 19. CGI Programming
 Chapter 20. Web Automation
 Chapter 21. mod_perl
 Chapter 22. XML