C ++中的向量添加并不完全正确

时间:2014-05-04 13:04:58

标签: c++ floating-accuracy

我正在尝试在我的C ++程序中将一个向量添加到另一个向量中,并且添加的内容不准确。

这是矢量类(使用cmath库):

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)));
    }
};

这是主要功能:

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;
}

这是输出:

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

如您所见,第一个输出v1很好。

第二个输出是0度和90度(一个不应该影响x分量的角度)的加法,他的x分量接近3,但不完全是3。

第三个输出是添加了两个具有相同幅度的相反矢量,它应该是0,但它不是这里显示的。

导致这些奇怪的添加的原因是什么?如何使它们准确无误?

2 个答案:

答案 0 :(得分:4)

v1 + v3差不多是0.0,代码运行正常。

为什么不完全是0.0

因为某些数字不能完全表示为双精度数。 有关解释,请参阅:C/C++: 1.00000 <= 1.0f = False

另外:Pi是一个irrational数字,无法在任何基数中完全表示为自然数。

因此任何涉及Pi的计算都不会完全准确。

并且:sincossqrt通常都是作为不能返回完全精确结果的算法实现的,例如作为近似数值算法。

答案 1 :(得分:1)

您遇到的基本问题是float的精确度有限以及您正在使用的pi的值。移至double会有所帮助,因为您已经包含<cmath>,所以您应该使用M_PI的值,并且精确到{{1}的精度}}。话虽如此,你仍然不会用这种方法得到确切的答案。 (alain的答案很好地解释了原因。)

可以做出一些改进。一个是使用C ++ 11特性的巧妙技巧,它是用户定义的字符串文字。&#34;如果将此定义添加到代码中:

double

现在,您可以将constexpr long double operator"" _deg(long double deg) { return deg*M_PI/180; } 附加到任何长双精度文本,它将在编译时自动转换为弧度。您的_deg函数将如下所示:

main

您可以做的下一件事是存储x和y坐标,并且只在需要时进行三角函数操作。该版本的int main(){ Vec v1(0.0_deg, 3); Vec v2(90.0_deg, 3); Vec v3(180.0_deg, 3); // ... } 可能如下所示:

Vec

请注意,我为构造函数创建了一个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); } } 值,该值指示输入是大小和方向还是x和y值。另请注意,bool已声明为getX(),因为它不会更改const,并且Vec的参数也是operator+的引用同样的道理。当我在我的机器(64位机器)上进行这些更改时,我得到以下输出:

const