p1-stats
Comparisons
This tutorial covers comparing signed and unsigned integers, and comparing floating point numbers (double
).
We’ll explain how to fix errors like this:
error: comparison between signed and unsigned integer expressions
Signed and unsigned integer comparisons
Example from stats.cpp
:
double sum(vector<double> v) {
double total = 0;
for (int i = 0; i < v.size(); ++i) {
total += v[i];
}
return total;
}
Compile and get this error:
$ make stats_tests.exe
g++ -Wall -Werror -pedantic -g --std=c++17 stats_tests.cpp stats.cpp -o stats_tests.exe
stats.cpp: In function 'double sum(std::vector<double>)':
stats.cpp:17:21: error: comparison between signed and unsigned integer expressions [-Werror=sign-compare]
for (int i = 0; i < v.size(); ++i) {
~~^~~~~~~~~~
cc1plus: all warnings being treated as errors
make: *** [stats_tests.exe] Error 1
The problem is v.size()
returns a size_t
type, which is an alias for an unsigned integer type. The loop variable i
is an int
type. The types don’t match.
Solution 1: size_t
Change int i
to size_t i
. Now, the types match and the compiler is happy.
double sum(vector<double> v) {
double total = 0;
// primer-spec-highlight-start
for (size_t i = 0; i < v.size(); ++i) {
// primer-spec-highlight-end
total += v[i];
}
return total;
}
Solution 2: static_cast<>()
Cast v.size()
to an int
. Again, the types match and the compiler is happy.
double sum(vector<double> v) {
double total = 0;
// primer-spec-highlight-start
for (int i = 0; i < static_cast<int>(v.size()); ++i) {
// primer-spec-highlight-end
total += v[i];
}
return total;
}
Floating point comparisons
Another comparison error you may encounter occurs when you compare two floating point numbers, like double
s. Floating point numbers have limited precision. Due to rounding errors, two floating point numbers we expect to be equal may be slightly different.
For example:
//test.cpp
#include <iostream>
using namespace std;
int main() {
double x = 1.0 / 3.0;
double y = 1.0 - (2.0 / 3.0);
cout << "x=" << x << endl;
cout << "y=" << y << endl;
if (x == y) {
cout << "equal" << endl;
} else {
cout << "not equal" << endl;
}
}
Compile and run. The two numbers look the same, but when we compare them, they are not equal! Notice that x
and y
are rounded to 6 decimal places by default.
$ g++ test.cpp -o test.exe
$ ./test.exe
x=0.333333
y=0.333333
not equal
Let’s look at the full precision. Modify your program to look like this.
//test.cpp
#include <iostream>
#include <limits>
using namespace std;
int main() {
double x = 1.0 / 3.0;
double y = 1.0 - (2.0 / 3.0);
// primer-spec-highlight-start
cout.precision(std::numeric_limits<double>::max_digits10);
// primer-spec-highlight-end
cout << "x=" << x << endl;
cout << "y=" << y << endl;
if (x == y) {
cout << "equal" << endl;
} else {
cout << "not equal" << endl;
}
}
Compile and run. Notice that x
and y
are no longer rounded to 5 decimal places. We can see that they are slightly different.
$ g++ test.cpp -o test.exe
$ ./test.exe
x=0.33333333333333331
y=0.33333333333333337
not equal
Next, we’ll compare within a tolerance epsilon
, instead of an exact comparison. Again, modify your program:
- Use an expression like
if (abs(x - y) < epsilon)
in the example below. - Note that the
#include <cmath>
is required to ensure the proper version ofabs()
is available. (Your code will not compile on the autograder without this.)
//test.cpp
#include <iostream>
// primer-spec-highlight-start
#include <cmath>
// primer-spec-highlight-end
#include <limits>
using namespace std;
// Precision for floating point comparison
const double epsilon = 0.00001;
int main() {
double x = 1.0 / 3.0;
double y = 1.0 - (2.0 / 3.0);
cout.precision(std::numeric_limits<double>::max_digits10);
cout << "x=" << x << endl;
cout << "y=" << y << endl;
// primer-spec-highlight-start
if (abs(x - y) < epsilon) {
// primer-spec-highlight-end
cout << "equal" << endl;
} else {
cout << "not equal" << endl;
}
}
Compile and run. Notice that the comparison now reports equal.
$ g++ test.cpp -o test.exe
$ ./test.exe
x=0.33333333333333331
y=0.33333333333333337
equal