我正在尝试在我的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,但它不是这里显示的。
导致这些奇怪的添加的原因是什么?如何使它们准确无误?
答案 0 :(得分:4)
v1 + v3
差不多是0.0
,代码运行正常。
为什么不完全是0.0
?
因为某些数字不能完全表示为双精度数。 有关解释,请参阅:C/C++: 1.00000 <= 1.0f = False。
另外:Pi是一个irrational数字,无法在任何基数中完全表示为自然数。
因此任何涉及Pi的计算都不会完全准确。
并且:sin
,cos
,sqrt
通常都是作为不能返回完全精确结果的算法实现的,例如作为近似数值算法。
答案 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