Click here to Skip to main content
15,882,055 members
Articles / Programming Languages / C++

Comparing doubles using Google's C++ testing framework

Rate me:
Please Sign up or sign in to vote.
4.58/5 (13 votes)
11 Aug 2014CPOL3 min read 24.8K   301   17   11
Using a portion of Google's C++ testing framework to compare doubles

Introduction

This article uses a portion of Google's C++ testing framework to compare double precision values.  This is useful for irrational numbers which can't be exactly represented in binary.

Background

The user is encouraged to read about the IEEE standard for floating point numbers, (IEEE 754).

Using the code

Use the class like you would the double primitive variable type.  For single parameter construction the code uses an explicit constructor to prevent the compiler from doing implicit conversions.  Please refer to the stackoverflow question and answer for using explicit in a constructor.

C++
CDouble d1;
d1 = 1.0;
CDouble d2(1.0);
CDouble d3 = CDouble(1.0);
CDouble d4 = 1.0; // COMPILATION ERROR!

At some point in time you will be faced with comparing double precision numbers.  For most of my programming career I took the "lah-lah-lah-lah" approach while plugging my ears with something.  A time came when my open source fractal software needed more accuracy in this department.  So I went digging around looking for the best way to compare 2 double precision numbers.  There is alot of information (and mis-information) about how to accomplish this task.  Some people recommend comparing the absolute value of the result from subtracting two numbers with an epsilon value and others run from the hills from this approach.  The explanation about why you don't want use an epsilon is outside of the scope of the article.  My search uncovered source code for the Google C++ testing framework and I dug into it and found the gem of information that I used to build the CDouble class. The explanation of the Google code is also outside the scope of this article but once you read about IEEE 754 then hopefully you can understand the code.  If not then any questions regarding that code should be sent straight to them :)

The following code demonstrates the instability of comparing double precision values.  Everyone knows that 3.41 equals 3.41, correct?  Everyone but your C++ compiler that is... The result is that 3.41 is not really equal to 3.41 and we get the expletive nag!

C++
double d1,d2,d3,d4;
d1 = 3.4;
d2 = 0.01;
d3 = d1 + d2;
d4 = 3.41;
if (d3 != d4)
	MessageBox(NULL,L"WTF!",L"Comparing...",MB_ICONSTOP);
else
	MessageBox(NULL,L"The universe is causal.",L"Comparing...",MB_ICONSTOP);

The following code uses the CDouble class to solve our problem.  Now we get the correct comparison of two double precision numbers.

C++
double d1,d2,d3;
d1 = 3.4;
d2 = 0.01;
d3 = d1 + d2;
CDouble d4(3.41);
if (d3 != d4)
	MessageBox(NULL,L"WTF!",L"Comparing...",MB_ICONSTOP);
else
	MessageBox(NULL,L"The universe is causal.",L"Comparing...",MB_OK);

Did the != comparison tickle something about what you know about C++ and operator overloading? The variable d3 is a double and d4 is a CDouble. The d4 variable is used like a double so how did the code know to do that? It knows how to do that because a double operator is defined in the class.

C++
operator double () const

Wait, you say, but it if returns a double and the left hand side of the comparison is also a double, then how does the compiler know how to avoid the error in the first scenario? Clearly we have arrived at the same scenario as above when we used just doubles.  It is then you remember friend functions in operator overloading. The code makes use of C++ operator overloading to let us compare CDouble just like a double.

C++
friend bool operator != (double d1,CDouble d2)

The use of chaining in the class allows for the creation of complex arithmetic and comparison logic.

C++
friend CDouble operator + (double d1,CDouble d2)
C++
// An example that uses chaining and the friend operator != shown above
CDouble d1,d2;
d1 = 3.4;
d2 = 0.01;
if (3.41 != d1 + d2)
	MessageBox(NULL,L"WTF!",L"Comparing...",MB_ICONSTOP);
else
	MessageBox(NULL,L"The universe is causal.",L"Comparing...",MB_ICONSTOP);

The explicit constructor sometimes makes more work for us.  Consider the example.

C++
CDouble e = CDouble(1.0);
if (e == 0) {} // COMPILATION ERROR!

This must be coded in the following manner. Removing the explicit keyword is not an option as it gives the compiler too many similar choices and thus the code won't compile.

C++
CDouble e = CDouble(1.0);
if (e == 0.0) {} // OK

What about the scenario when you must know if the value is absolutely equal to 0.0 or not, with no tolerance for error?  The following code demonstrates how to accomplish the task.

C++
CDouble e = CDouble(0.0);
if ((double)e == 0.0) {} // OK

 

And there you have it! I hope you can make use of the class and build onto it and share any improvements. I've never considered myself an expert at C++ or a real student of the language. I am a problem solver who likes to figure out aspects of the language when I need them to solve something in particular.

Points of Interest

The class for CDouble reminded me of why I love C++ and operator overloading.

History

7/30/2014 - Article submission

8/11/2014 - A note about the explicit constructor

License

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


Written By
Founder
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Ștefan-Mihai MOGA13-Aug-14 3:16
professionalȘtefan-Mihai MOGA13-Aug-14 3:16 
GeneralRe: My vote of 5 Pin
Andy Bantly13-Aug-14 12:12
Andy Bantly13-Aug-14 12:12 
QuestionCan't compare to 0 Pin
Stefan_Lang11-Aug-14 5:03
Stefan_Lang11-Aug-14 5:03 
AnswerRe: Can't compare to 0 Pin
Andy Bantly11-Aug-14 10:26
Andy Bantly11-Aug-14 10:26 
I think I understand what you are you are trying to point out. This is not about integer 0 vs. double 0. This is about quantities so close to 0 but not the same that the logic will falsely say they are equal.

The base class logic addresses this issue with comments. There is a parameter which controls comparison tolerance of units in the last place. Here is the excerpt from the code. If you must know exactly whether 2 values are absolutely equal to 0.0 then you should cast the CDouble to a double and compare it with 0.0.

C++
// How many ULP's (Units in the Last Place) we want to tolerate when
// comparing two numbers.  The larger the value, the more error we
// allow.  A 0 value means that two numbers must be exactly the same
// to be considered equal.
//
// The maximum error of a single floating-point operation is 0.5
// units in the last place.  On Intel CPU's, all floating-point
// calculations are done with 80-bit precision, while double has 64
// bits.  Therefore, 4 should be enough for ordinary use.
//
// See the following article for more details on ULP:
// http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm.
static const size_t kMaxUlps = 4;

GeneralRe: Can't compare to 0 Pin
Stefan_Lang11-Aug-14 20:57
Stefan_Lang11-Aug-14 20:57 
GeneralRe: Can't compare to 0 Pin
Andy Bantly12-Aug-14 3:35
Andy Bantly12-Aug-14 3:35 
GeneralI like it! 5 Stars for you! Pin
gh3k31-Jul-14 13:05
professionalgh3k31-Jul-14 13:05 
GeneralRe: I like it! 5 Stars for you! Pin
Andy Bantly31-Jul-14 14:08
Andy Bantly31-Jul-14 14:08 
QuestionVery Cool! Pin
koothkeeper31-Jul-14 9:32
professionalkoothkeeper31-Jul-14 9:32 
AnswerRe: Very Cool! Pin
Andy Bantly31-Jul-14 14:12
Andy Bantly31-Jul-14 14:12 
GeneralRe: Very Cool! Pin
koothkeeper29-Aug-14 2:27
professionalkoothkeeper29-Aug-14 2:27 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

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