6

我有一个函数 getSlope ,它以 4 个双精度数作为参数,并返回另一个使用给定参数计算的双精度数,方法如下:

double QSweep::getSlope(double a, double b, double c, double d){
double slope;
slope=(d-b)/(c-a);
return slope;
}

问题是,当使用参数调用此函数时,例如:

getSlope(2.71156, -1.64161, 2.70413, -1.72219);

返回的结果是:

10.8557

这对我的计算来说不是一个好的结果。我已经使用 Mathematica 计算了斜率,相同参数的斜率结果是:

10.8452

或更多数字以提高精度:

10.845222072678331.

我的程序返回的结果在我进一步的计算中并不好。此外,我不明白程序如何从 10.845222072678331 开始返回 10.8557(假设这是除法的近似结果)?我怎样才能为我的部门取得好成绩?

提前谢谢你,马达琳娜


我使用命令行打印结果:

std::cout<<slope<<endl;

可能是我的参数可能不好,因为我从另一个程序中读取它们(它计算一个图表;在我从他的图表中读取这个参数之后,我刚刚显示它们以查看它们的值但可能显示的向量不一样计算值的内部精度..我不知道这真的很奇怪。出现了一些数值错误..)

当计算我从中读取参数的图形时,会使用一些用 C++(带有模板)编写的数值库。此计算不使用 OpenGL。

谢谢你,马达琳娜

4

8 回答 8

7

我尝试使用 float 而不是 double ,结果得到 10.845110 。它看起来仍然比madalina的结果好。

编辑:

我想我知道你为什么会得到这个结果。如果您从其他地方获得 a、b、c 和 d 参数并打印它,它会为您提供四舍五入的值。然后,如果你把它放到 Mathemtacia (或 calc ;) )它会给你不同的结果。

我试着改变一点你的参数。当我这样做时:

double c = 2.7041304;

我得到 10.845806。我只在 c 中添加 0.0000004!所以我认为你的“错误”不是错误。以更高的精度打印 a、b、c 和 d,然后将它们放入 Mathematica。

于 2009-03-30T10:47:41.180 回答
5

会不会是您在项目中使用了 DirectX 或 OpenGL?如果是这样,他们可以关闭双精度,你会得到奇怪的结果。

您可以检查您的精度设置

std::sqrt(x) * std::sqrt(x)

结果必须非常接近 x。我很久以前就遇到过这个问题,花了一个月的时间检查所有的公式。但后来我发现

D3DCREATE_FPU_PRESERVE
于 2009-03-30T09:34:38.953 回答
5

以下代码:

#include <iostream>
using namespace std;

double getSlope(double a, double b, double c, double d){
    double slope;
    slope=(d-b)/(c-a);
    return slope;
}

int main( ) {
    double s = getSlope(2.71156, -1.64161, 2.70413, -1.72219);
    cout << s << endl;
}

使用 g++ 给出 10.8452 的结果。您如何在代码中打印出结果?

于 2009-03-30T09:40:28.457 回答
3

这里的问题是 (ca) 很小,所以浮点运算中固有的舍入误差在这个例子中被放大了。一个通用的解决方案是修改你的方程,这样你就不会被一个小数除,不过我不确定你会怎么做。

编辑:

Neil 对这个问题的评论是正确的,我使用 Doubles 在 VB 中计算了答案,并得到了与数学相同的答案。

于 2009-03-30T09:36:19.730 回答
2

您得到的结果与 32 位算术一致。在不了解您的环境的更多信息的情况下,不可能建议该做什么。

假设显示的代码正在运行,即您没有将任何内容转换为字符串或浮点数,那么 C++ 中没有修复。它在您显示的代码之外,并且取决于环境。

由于 Patrick McDonald 和 Treb 提出了您输入的准确性和错误a-c,我想我会看一下。查看舍入误差的一种技术是区间算术,它使值表示的上限和下限显式(它们隐含在浮点数中,并且固定为表示的精度)。通过将每个值视为上限和下限,并通过表示中的误差扩展边界(对于双精度值 x 约为 x * 2 ^ -53),您会得到一个结果,该结果给出了值的准确性,考虑到最坏情况下的精度误差。

例如,如果您有一个在 [1.0, 2.0] 范围内的值并从中减去一个在 [0.0, 1.0] 范围内的值,那么结果必须在 [below(0.0),above(2.0)] 范围内因为最小值是 1.0-1.0,最大值是 2.0-0.0。below并且above等价于 floor 和 ceiling,但用于下一个可表示的值而不是整数。

使用代表最坏情况双舍入的间隔:

getSlope(
 a = [2.7115599999999995262:2.7115600000000004144], 
 b = [-1.6416099999999997916:-1.6416100000000002357], 
 c = [2.7041299999999997006:2.7041300000000005888], 
 d = [-1.7221899999999998876:-1.7221900000000003317])
(d-b) = [-0.080580000000000526206:-0.080579999999999665783]
(c-a) = [-0.0074300000000007129439:-0.0074299999999989383218]

to double precision [10.845222072677243474:10.845222072679954195]

因此,虽然与orc-a相比很小,但与双舍入相比仍然很大,所以如果您使用可想象到的最差的双精度舍入,那么您可以相信该值精确到 12 位数字 - 10.8452220727。您在双精度方面丢失了一些数字,但您仍在努力超越输入的重要性。ca

但是,如果输入仅精确到有效数字,而不是双精度值 2.71156 +/- eps,那么输入范围将为 [2.711555,2.711565],因此您会得到结果:

getSlope(
 a = [2.711555:2.711565], 
 b = [-1.641615:-1.641605], 
 c = [2.704125:2.704135], 
 d = [-1.722195:-1.722185])
(d-b) = [-0.08059:-0.08057]
(c-a) = [-0.00744:-0.00742]

to specified accuracy [10.82930108:10.86118598]

这是一个更广泛的范围。

但是您必须不遗余力地跟踪计算的准确性,并且浮点固有的舍入误差在此示例中并不显着 - 在最坏的情况下双精度舍入精确到 12 位数字。

另一方面,如果您的输入只知道 6 个数字,那么您得到 10.8557 还是 10.8452 实际上并不重要。两者都在 [10.82930108:10.86118598] 之内。

于 2009-03-30T12:31:31.993 回答
1

最好也打印出论点。正如我猜测的那样,当您以十进制表示法传输参数时,您将失去每一个参数的精度。问题在于 1/5 是二进制的无限级数,因此例如 0.2 变为 .001001001...。此外,在将二进制浮点数转换为十进制文本表示时,小数会被截断。

除此之外,有时编译器会选择速度而不是精度。这应该是记录在案的编译器开关。

于 2009-03-30T10:01:56.627 回答
0

帕特里克似乎是正确的(ca)是主要原因:

分贝 = -1,72219 - (-1,64161) = -0,08058

ca = 2,70413 - 2,71156 = -0,00743

S = (db)/(ca)= -0,08058 / -0,00743 = 10,845222

你从六位数的精度开始,通过减法你得到减少到三位数和四位数。我最好的猜测是你失去了额外的精度,因为数字 -0,00743 不能用双精度表示。尝试使用精度更高的中间变量,如下所示:

double QSweep::getSlope(double a, double b, double c, double d)
{
    double slope;
    long double temp1, temp2;

    temp1 = (d-b);
    temp2 = (c-a);
    slope = temp1/temp2;

    return slope;
}
于 2009-03-30T10:06:36.380 回答
-1

虽然正在进行的学术讨论非常有助于了解编程语言的局限性,但您可能会发现该问题的最简单解决方案是用于任意精度算术的数据结构。

这会产生一些开销,但是您应该能够找到具有相当可保证准确性的东西。

于 2009-05-27T19:10:22.930 回答