12,550,766 members (31,195 online)
Add your own
alternative version

101.8K views
19 bookmarked
Posted

# Reliable Floating Point Equality Comparison

, 4 Dec 2006 CPOL
 Rate this:
Please Sign up or sign in to vote.
Compare floating point numbers for equality at programmer-specified precision.

## Introduction

A discussion in the Subtle Bugs forum concerning floating point number comparisons caused me to recall some code I wrote about 15 years ago. Back then, I was writing code that calculated federal and stats estate/inheritance taxes, as well as charitable giving scenarios. We needed to compare floating point values at various levels of precision depending on what we were doing and where in the calculation cycle we happened to be.

## The Snippet

Anyone that's been writing code for any length of time knows what a pain in the butt it is to compare floating point numbers because they're mere approximations of their true value. For this reason, I wrote, and present you now with my AlmostEqual() function.

My approach centers around converting the floating point values to something a bit more reliable where comparisons are concerned - strings. In the example shown in this article, I use CStrings because I'm a fan of MFC. You could certainly use stdio functions or even STL for your strings if you don't (or can't) use MFC. I admit that this isn't some fancy thing that magically evaluates all of the parts of a floating point number, but it does work and it's handy if you need to compare floating point values to a specific level of precision.

bool AlmostEqual(double nVal1, double nVal2, int nPrecision)
{
CString sVal1;
CString sVal2;

nPrecision = __max(__min(16, nPrecision), 0);
sVal1.Format("%.*lf", nPrecision, nVal1);
sVal2.Format("%.*lf", nPrecision, nVal2);

bool bRet = (sVal1 == sVal2);
return bRet;
}

There's really not much to say about the function. You merely pass in the two values that need to be compared along with the desired precision value.

## Article Update: 12/08/2006

After being hounded about speed issues, I decided to try different methods for comparison. Here's a function similar to the first, but that uses stdio functions for the string formatting.

bool AlmostEqualStdIO(double nVal1, double nVal2, int nPrecision)
{
char sVal1[40];
char sVal2[40];

nPrecision = __max(__min(16, nPrecision), 0);
sprintf_s(sVal1, sizeof(sVal1), "%.*lf", nPrecision, nVal1);
sprintf_s(sVal2, sizeof(sVal2), "%.*lf", nPrecision, nVal2);

bool bRet = (strcmp(sVal1, sVal2) == 0);
return bRet;
}

The function above requires less than half the time the CString version needs to perform the same task. Keep in mind that this is using VS2005, and that (I think) the MFC CString class is actually using STL now.

Finally, here a version of the function that doesn't do any string manipulation at all.

bool AlmostEqualDoubles(double nVal1, double nVal2, int nPrecision)
{
nPrecision = __max(__min(16, nPrecision), 0);
double nEpsilon = 1.0;
for (int i = 1; i <= nPrecision; i++)
{
nEpsilon *= 0.1;
}
bool bRet = (((nVal2 - nEpsilon) < nVal1) && (nVal1 < (nVal2 + nEpsilon)));
return bRet;
}

When I watched this function work in the debugger, I noticed that performing ANY math of the nEpsilon value caused it to become impure. The very last digit of the mantissa was some random value. This almost guarantees that at some point, the value will be such that it returns an incorrect result. Given the aim of this article (RELIABLE equality comparisons), this is not adequate.

Finally, here's a version of the function that accepts a direct value for nEpsilon in the form of an appropriate value. For instance, if you want a precision of 3, you would pass in 0.001.

bool AlmostEqualDoubles2(double nVal1, double nVal2, double nEpsilon)
{
bool bRet = (((nVal2 - nEpsilon) < nVal1) && (nVal1 < (nVal2 + nEpsilon)));
return bRet;
}

I didn't watch all 250,000 iterations in my test app, but none of the ones I traced through presented any value other than 0.0010000000000000, but that does not preclude the possibility that it could happen (given the nature of floating point values). Again - given the aim of this article (RELIABLE equality comparisons), this is not adequate.

Finally, the non-string versions of the function were admittedly very fast. My 250,001 iteration tests revealed an execution time of ZERO seconds (obviously more than 0 seconds but less than 1 second) compared to two seconds for the stdio and five seconds for the CString version. Again, my admittedly chunky timing code should probably be replaced by something with a far higher resolution, but you get the idea. So, here we are with four versions of the comparison function. I still have to say that the string comparison is far more consistent than the non-string versions. Take that for what it's worth.

Is everyone happy now?

## Disclaimers

It's apparent that the original single disclaimer (the last one in this list) was not sufficient , so I've added a couple more:

• This article is not about rounding floating point values (although I do have a function for that as well). It is about truncating the specified values at a specific precision via string formatting for a direct comparison. Therefore, passing 1.2345 and 1.2346 with a precision of anything less than 4 will generate a return value of true. while passing a precision of anything higher than 3 will result in a return value of false. With this in mind, please don't post comments about how the function is rounding the values being compared, because it's not.
• Yes, I'm aware that using strings is somewhat inefficient, but they are used for the sake of accuracy regarding the function's desired result. It is impossible to reliably compare two floating point numbers for equality. String comparisons are not subject to the flaws, and are therefore the comparison vehicle of choice if you want to be sure the comparison is consistent. Like I said in the article, you certainly don't have to use CString and can instead use either stdio functions or even STL if you're so inclined. It would be an interesting test to see which of the three are the best performers in that regard.

UPDATE:I ran a test iterating 250,001 calls to AlmostEqual using a version of the function that used CString, and another function that used stdio sprintf_s. The CString method took 5 seconds, and the stdio version took 2 seconds. Granted, using COleDateTime isn't as precise as one would like to get, and if the difference wasn't so pronounced, I'd look for something more accurate. (Test system is a P4 3.6 with 1GB of RAM.)
• I recognize that this article is fairly short, and there's no sample code to download or fancy screen shots, but my view is that a lot of code we use comes down to one or two functions that fits a specific need and simply doesn't need a screen shot to illustrate its functionality. By its very definition, a snippet is usually not worthy of being made into a full-blown class with exception handling and/or serialization. So, use it if you want to.

## License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

## About the Author

 Software Developer (Senior) United States
I've been paid as a programmer since 1982 with experience in Pascal, and C++ (both self-taught), and began writing Windows programs in 1991 using Visual C++ and MFC. In the 2nd half of 2007, I started writing C# Windows Forms and ASP.Net applications, and have since done WPF, Silverlight, WCF, web services, and Windows services.

My weakest point is that my moments of clarity are too brief to hold a meaningful conversation that requires more than 30 seconds to complete. Thankfully, grunts of agreement are all that is required to conduct most discussions without committing to any particular belief system.

## Comments and Discussions

 First PrevNext
 A simpler derivative B. Clay Shannon3-Oct-13 9:00 B. Clay Shannon 3-Oct-13 9:00
 My vote of 5 Michael A. Barnhart12-Dec-10 14:48 Michael A. Barnhart 12-Dec-10 14:48
 My vote of 5 Henry Minute12-Dec-10 2:39 Henry Minute 12-Dec-10 2:39
 Re: My vote of 5 John Simmons / outlaw programmer12-Dec-10 2:44 John Simmons / outlaw programmer 12-Dec-10 2:44
 My vote of 1 Mr Nukealizer11-Dec-10 18:30 Mr Nukealizer 11-Dec-10 18:30
 Re: My vote of 1 John Simmons / outlaw programmer12-Dec-10 2:11 John Simmons / outlaw programmer 12-Dec-10 2:11
 Re: My vote of 1 Marcus Kramer12-Dec-10 3:15 Marcus Kramer 12-Dec-10 3:15
 My vote of 2 richarno22-Apr-09 6:33 richarno 22-Apr-09 6:33
 Re: My vote of 2 [modified] John Simmons / outlaw programmer8-Jan-10 4:41 John Simmons / outlaw programmer 8-Jan-10 4:41
 richarno wrote:The method used is not very useful, and is very heavy on the CPU. I feel that your vote isn't justified. The article merely presents a number of methods for performing *accurate* floating point comparisons. As I've already stated, this is about accuracy - not performance. If you want performance, you should use a decimal type if it's available in your chosen framework/SDK, at which point, this article would be of no use to you anyway. Also, don't forget that in order to have a measurable performance metric, you have to perform the comparison several times. If you're performing such comparisons once or twice in a calculation cycle, the CPU drag would be negligible. Considering all of the above, I urge you to change your vote. .45 ACP - because shooting twice is just silly-----"Why don't you tie a kerosene-soaked rag around your ankles so the ants won't climb up and eat your candy ass..." - Dale Earnhardt, 1997-----"The staggering layers of obscenity in your statement make it a work of art on so many levels." - J. Jystad, 2001 modified on Sunday, December 12, 2010 8:22 AM
 IEEE Standard 754 dibeas2-Aug-07 11:57 dibeas 2-Aug-07 11:57
 The MOST solution of a MOST problem... Kochise17-Apr-07 22:15 Kochise 17-Apr-07 22:15
 Re: The MOST solution of a MOST problem... John Simmons / outlaw programmer4-Jun-07 2:37 John Simmons / outlaw programmer 4-Jun-07 2:37
 Re: The MOST solution of a MOST problem... Kochise9-Jan-14 22:11 Kochise 9-Jan-14 22:11
 Re: The MOST solution of a MOST problem... John Simmons / outlaw programmer10-Jan-14 0:58 John Simmons / outlaw programmer 10-Jan-14 0:58
 Re: The MOST solution of a MOST problem... Kochise10-Jan-14 6:31 Kochise 10-Jan-14 6:31
 Re: The MOST solution of a MOST problem... rainer erdmann29-Dec-10 6:36 rainer erdmann 29-Dec-10 6:36
 Re: The MOST solution of a MOST problem... Andy Allinger9-Jan-14 15:45 Andy Allinger 9-Jan-14 15:45
 Re: The MOST solution of a MOST problem... Kochise9-Jan-14 22:08 Kochise 9-Jan-14 22:08
 CString::Format is always slower Michael Dunn8-Dec-06 7:29 Michael Dunn 8-Dec-06 7:29
 Re: CString::Format is always slower John Simmons / outlaw programmer8-Dec-06 8:21 John Simmons / outlaw programmer 8-Dec-06 8:21
 If two numbers are really small, [modified] Dr. No8-Dec-06 7:10 Dr. No 8-Dec-06 7:10
 Re: If two numbers are really small, John Simmons / outlaw programmer8-Dec-06 7:18 John Simmons / outlaw programmer 8-Dec-06 7:18
 Re: If two numbers are really small, Dr. No8-Dec-06 8:02 Dr. No 8-Dec-06 8:02
 Re: If two numbers are really small, John Simmons / outlaw programmer8-Dec-06 8:20 John Simmons / outlaw programmer 8-Dec-06 8:20
 Re: If two numbers are really small, Nitron11-Dec-06 11:15 Nitron 11-Dec-06 11:15
 Re: If two numbers are really small, PaulC197212-Dec-06 17:43 PaulC1972 12-Dec-06 17:43
 Good article. Chris Meech8-Dec-06 5:06 Chris Meech 8-Dec-06 5:06
 I tried without using strings, and... [modified] John Simmons / outlaw programmer7-Dec-06 12:57 John Simmons / outlaw programmer 7-Dec-06 12:57
 Re: I tried without using strings, and... Rob Caldecott8-Dec-06 3:04 Rob Caldecott 8-Dec-06 3:04
 Never ever CliffStanford5-Dec-06 13:47 CliffStanford 5-Dec-06 13:47
 Re: Never ever John Simmons / outlaw programmer5-Dec-06 23:22 John Simmons / outlaw programmer 5-Dec-06 23:22
 Re: Never ever [modified] dibeas2-Aug-07 11:53 dibeas 2-Aug-07 11:53
 Re: Didn't your mother . . . Chris Meech8-Dec-06 5:07 Chris Meech 8-Dec-06 5:07
 Incorrect viceroy5-Dec-06 1:24 viceroy 5-Dec-06 1:24
 Re: Incorrect [modified] John Simmons / outlaw programmer5-Dec-06 1:52 John Simmons / outlaw programmer 5-Dec-06 1:52
 Re: Incorrect Nitron5-Dec-06 2:43 Nitron 5-Dec-06 2:43
 Re: Incorrect - another method Warren Stevens5-Dec-06 5:05 Warren Stevens 5-Dec-06 5:05
 Re: Incorrect - another method John Simmons / outlaw programmer5-Dec-06 23:16 John Simmons / outlaw programmer 5-Dec-06 23:16
 Re: Incorrect - another method Warren Stevens6-Dec-06 3:40 Warren Stevens 6-Dec-06 3:40
 Re: Incorrect - another method itskumaranand27-Mar-08 4:26 itskumaranand 27-Mar-08 4:26
 Re: Incorrect viceroy5-Dec-06 17:20 viceroy 5-Dec-06 17:20
 Re: Incorrect John Simmons / outlaw programmer5-Dec-06 23:21 John Simmons / outlaw programmer 5-Dec-06 23:21
 Re: Incorrect [modified] viceroy6-Dec-06 1:20 viceroy 6-Dec-06 1:20
 Re: Incorrect John Simmons / outlaw programmer6-Dec-06 2:05 John Simmons / outlaw programmer 6-Dec-06 2:05
 Naive x-b6-Dec-06 7:34 x-b 6-Dec-06 7:34
 Re: Incorrect Daniel Grunwald8-Dec-06 3:35 Daniel Grunwald 8-Dec-06 3:35
 Re: Incorrect John Simmons / outlaw programmer8-Dec-06 3:38 John Simmons / outlaw programmer 8-Dec-06 3:38
 Re: Incorrect Daniel Grunwald8-Dec-06 3:51 Daniel Grunwald 8-Dec-06 3:51
 Re: Incorrect John Simmons / outlaw programmer8-Dec-06 6:00 John Simmons / outlaw programmer 8-Dec-06 6:00
 Re: Incorrect Daniel Grunwald8-Dec-06 6:03 Daniel Grunwald 8-Dec-06 6:03
 Last Visit: 31-Dec-99 18:00     Last Update: 23-Oct-16 15:24 Refresh 12 Next »

General    News    Suggestion    Question    Bug    Answer    Joke    Praise    Rant    Admin

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.161021.1 | Last Updated 5 Dec 2006
Article Copyright 2006 by John Simmons / outlaw programmer
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid