回 帖 发 新 帖 刷新版面

主题:请教C++语言计算精度的问题

源代码如下:
#include<iostream.h>
#include<math.h>

int turnType = 0;
double temp2 = 0;
double temp3 = 0;
double temp4 = 0;
double temp5 = 0;
double temp6 = 0;
double temp7 = 0;
double temp8 = 0;
double toolRadius = 0;
double X_previous = 0;
double Y_previous = 0;
double X_current = 0;
double Y_current = 0;
double X_next = 0;
double Y_next = 0;
double i = 0;
double j = 0;

double lineHorizontalVector(double m, double n, double i, double j)
{
    double temp = 0;

    temp = (i - m) / sqrt(pow((i - m),2) + pow((j - n),2));

    return temp;
}

double lineVerticalVector(double m, double n, double i, double j)
{
    double temp = 0;

    temp = (j - n) / sqrt(pow((i - m),2) + pow((j - n),2));

    return temp;
}

double circleHorizontalVector(double m, double n)
{
    double temp = 0;

    temp = -1 * n / sqrt(pow(m, 2) + pow(n, 2));

    return temp;
}

double circleVerticalVector(double m, double n)
{
    double temp = 0;

    temp = m / sqrt(pow(m, 2) + pow(n, 2));

    return temp;
}

int sign(double m)
{
    int temp = 0;

    if (m > 0)
        temp = 1;
    else if (m < 0)
        temp = -1;
    else
        temp = 0;

    return temp;
}

int judgeTurnType(double m, double n, double i, double j)
{
    int temp = 0;
    int k = 0;

    k = sign(toolRadius);
    if (k * (j * m - i * n) >= 0)
        temp = 1;
    else
    {
        if ((m * i + n * j) >= 0)
            temp = 2;
        else
            temp = 3;
    }

    cout << "结果为:" << k * (j * m - i * n) << endl;

    return temp;
}

int main()
{
    cout << "X_previous = ";
    cin >> X_previous;
    cout << "Y_previous = ";
    cin >> Y_previous;
    cout << "X_current = ";
    cin >> X_current;
    cout << "Y_current = ";
    cin >> Y_current;
    cout << "X_next = ";
    cin >> X_next;
    cout << "Y_next = ";
    cin >> Y_next;
    cout << "i = ";
    cin >> i;
    cout << "j = ";
    cin >> j;
    cout << "toolRadius = ";
    cin >> toolRadius;

    temp2 = lineHorizontalVector(X_current, Y_current, X_next, Y_next);
    temp3 = lineVerticalVector(X_current, Y_current, X_next, Y_next);
    temp4 = X_previous + i - X_current;
    temp5 = Y_previous + j - Y_current;
    temp6 = circleHorizontalVector(temp4, temp5);
    temp7 = circleVerticalVector(temp4, temp5);
    temp8 = sqrt(pow(i, 2) + pow(j, 2));

    turnType = judgeTurnType(temp6, temp7, temp2, temp3);

    cout << "temp2=" << temp2 << endl;
    cout << "temp3=" << temp3 << endl;
    cout << "temp6=" << temp6 << endl;
    cout << "temp7=" << temp7 << endl;
    cout << "temp8=" << temp8 << endl;
    cout << "turnType=" << turnType << endl;

    return 0;
}

该程序用于计算数控机床C刀补之圆弧接直线转接点坐标

输入(原本小数点后有很多位,我按照实际操作的情况精确到了0.0001):

X_previous = 126.8328
Y_previous = 3.6656
X_current = 100
Y_current = -50
X_next = 0
Y_next = 0
i = -13.4164
j = -26.8328
toolRadius = -10

结果导致输出结果为:

结果为:-2.77556e-016
temp2=-0.894427
temp3=0.447214
temp6=-0.894427
temp7=0.447214
temp8=30
turnType=2


按照结果中的数据
“temp2=-0.894427
  temp3=0.447214
  temp6=-0.894427
  temp7=0.447214”
在子函数judgeTurnType(temp6, temp7, temp2, temp3)中k * (j * m - i * n)应该等于0,
可是输出“结果为:-2.77556e-016”为什么呢?
该怎么办才能让temp2、temp3、temp6、temp7、temp8精确到一定的位数,而不是系统默认的位数呢?
我期待的结果是等于0,该怎么修改程序呢?

[img]F:\请教的问题\运行界面.bmp[/img]

回复列表 (共3个回复)

沙发

基本上,这个不是计算问题,而是输出问题。

在计算过程中,中间结果的精度越高越不容易产生误差,因此没有必要对中间结果的精度做出限制,所有的计算都照此办理即可。double类型一般可以支持14位有效数字,因此如果要求的有效数字在10位以内,并且计算过程不是特别复杂的话,完全可以胜任了。
楼主的计算结果:-2.77556e-016,是一个绝对值很小的数(-0.000000000000000277556),如果精确到10位有效数字的话,其实已经是等于零了。

然后来说输出。
C++的输出格式控制,主要是需要<iomanip>这个头文件。(旧式的C++采用<iomanip.h>。顺便说一句,您应该写#include <iostream>,然后用using namespace std;,而不是写#include <iostream.h>。您这样的写法现在已经过时,新版的GCC、Visual Studio都不再支持了,请记得更正自己的编程习惯)
对于浮点数输出来讲,主要需要了解的是三个概念:
(1) 浮点数有三种输出方式。第一种是普通计数法,第二种是科学计数法,第三种是从普通计数法和科学计数法两者选较短的一个为准。默认是第三种方式输出。
(2) 输出宽度。如果指定了宽度,并且输出的字符数没有达到指定宽度,则会用一些字符去填充,默认用空格填充。
(3) 输出精度(这个就是您现在最应该关心的)。对于普通计数法,输出精度就是小数位数。对于科学计数法,输出精度就是有效数字位数。

现在看代码:
cout << resetiosflags(ios::floatfield); // 将浮点数输出方式恢复为默认值
cout << setiosflags(ios::fixed);        // 将浮点数输出方式设置为普通计数法
cout << setiosflags(ios::scientific);   // 将浮点数输出方式设置为普通计数法
cout << setw(20);                       // 设置输出宽度为20
cout << setprecision(3);                // 设置输出精度为3

可以连起来写:
    cout << resetiosflags(ios::floatfield) << setw(20) << setiosflags(ios::fixed) << setprecision(3) << -2.77556e-16 << endl;
    cout << resetiosflags(ios::floatfield) << setw(20) << setiosflags(ios::scientific) << setprecision(3) << -2.77556e-16 << endl;

输出结果为(注意下面的空格也是输出结果):
              -0.000
         -2.776e-016

第一行的输出已经很接近我们想要的结果了。但是这里还是有一点瑕疵。0被输出为-0了。因为浮点数的0本来就有正负之分,这是没有办法的事情。

如何解决这样的问题呢?最彻底的办法当然是让要输出的值完完全全的等于零,然后输出,这样就可谓四平八稳了。
采用的方法也很简单:要把一个数精确到n位小数,则只需要把它乘以10的n次方,然后(如果需要四舍五入的话)加上0.5,再取它的整数部分,再除以10的n次方,即可。

程序代码:
void output(double value, int precision)
{
    double power = 1.0;
    for (int i = 0; i < precision; ++i) {
        power *= 10.0;
    }

    value *= power;
    value += 0.5;
    value = floor(value);
    value /= power;

    cout << value << endl;
}

然后调用:
    output(-2.77556e-16, 6);
    output(1.2345678901, 6);

输出为
0
1.23457

其中第一行就是楼主想要的结果。

板凳

很谢谢楼上的大哥

3 楼

那如果用的计算机是64位的,同样是双精度浮点数运算,精度会提高吗?

我来回复

您尚未登录,请登录后再回复。点此登录或注册