user2836478 - 3 months ago 12
C++ Question

# Vector addition in C++ is not coming out exactly right

I am trying to add one vector to another vector in my C++ program and addition is coming out inexact.

This is the vector class (using cmath library):

``````class Vec{
float dir, mag;
public:
Vec(float dir, float mag){
this->dir = dir;
this->mag = mag;
}
float getX(){
return cos(dir)*mag;
}
Vec operator + (Vec v2){
float triangleX = cos(dir)*mag+cos(v2.dir)*v2.mag;
float triangleY = sin(dir)*mag+sin(v2.dir)*v2.mag;
return Vec(atan2(triangleY, triangleX), sqrt(pow(triangleX,2)+pow(triangleY,2)));
}
};
``````

And this is the main function:

``````int main(){
Vec v1(0, 3); // 0º
Vec v2(3.14159265/2, 3); // 90º
Vec v3(3.14159265, 3); // 180º
std::cout.precision(15);
std::cout<<"v1: "<<v1.getX()<<std::endl;
std::cout<<"v1+v2: "<<(v1+v2).getX()<<std::endl;
std::cout<<"v1+v3: "<<(v1+v3).getX()<<std::endl;
return 0;
}
``````

And this is the output:

``````v1: 3
v1+v2: 2.99999976158142
v1+v3: 1.98007097372034e-014
``````

As you can see, the first output v1 is fine.

The second output is the addition of 0 degrees and 90 degrees (an angle that was not supposed to affect the x component), his x component is close to 3, but not exactly 3.

The third output is the addition of 2 opposite vectors with the same magnitude, it was supposed to be 0, but it is not what shows here.

What is causing these strange additions and how do I make them exact?

The basic problem you're having is one of limited precision of `float` and the value of `pi` that you're using. Moving to a `double` will help, and since you're already including `<cmath>` you should use the value there which is `M_PI` and is accurate to at least the precision of `double`. With that said, you're still not going to get exact answers with this approach. (The answer from alain does a good job of explaining why.)

There are some improvements that could be made. One is a neat trick using a C++11 feature which is "user-defined string literals." If you add this definition to your code:

``````constexpr long double operator"" _deg(long double deg) {
return deg*M_PI/180;
}
``````

You can now append `_deg` to any long double literal and it will automatically be converted to radians at compile time. Your `main` function would then look like this:

``````int main(){
Vec v1(0.0_deg, 3);
Vec v2(90.0_deg, 3);
Vec v3(180.0_deg, 3);
// ...
}
``````

The next thing you could do would be to store the x and y coordinates and only do the trigonometric manipulations when needed. That version of `Vec` might look like this:

``````class Vec{
double x,y;
public:
Vec(double dir, double mag, bool cartesian=false) : x(dir), y(mag) {
if (!cartesian) {
x = mag*cos(dir);
y = mag*sin(dir);
}
}
double getX() const {
return x;
}
Vec operator + (const Vec &v2){
return Vec(x+v2.x, y+v2.y, true);
}
}
``````

Note that I've created a `bool` value for the constructor which tells whether the input is to be a magnitude and direction or an x and y value. Also note that the `getX()` is declared `const` because it doesn't alter the `Vec` and that the argument to `operator+` is also a `const` reference for the same reason. When I make those changes on my machine (a 64-bit machine), I get the following output:

``````v1: 3
v1+v2: 3
v1+v3: 0
``````