Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/71.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 我可以计算双精度引入的误差吗?_C_Floating Point_Double_Ieee 754 - Fatal编程技术网

C 我可以计算双精度引入的误差吗?

C 我可以计算双精度引入的误差吗?,c,floating-point,double,ieee-754,C,Floating Point,Double,Ieee 754,假设我有一个无理数,如\sqrt{3}。因为它是非理性的,所以它没有十进制表示。因此,当您试图用IEEE 754 double表示它时,您将引入一个错误 具有大量数字的十进制表示法是: 1.7320508075688772935274463415058723669428052538103806280558069794519330169088 00037081146186757248575675... 现在,当我计算\sqrt{3}时,我得到1.732051: #include <st

假设我有一个无理数,如
\sqrt{3}
。因为它是非理性的,所以它没有十进制表示。因此,当您试图用IEEE 754 double表示它时,您将引入一个错误

具有大量数字的十进制表示法是:

1.7320508075688772935274463415058723669428052538103806280558069794519330169088
  00037081146186757248575675...
现在,当我计算
\sqrt{3}
时,我得到
1.732051

#include <stdio.h> // printf
#include <math.h>   // needed for sqrt

int main() {
    double myVar = sqrt (3);
    printf("as double:\t%f\n", myVar);
}
有了这一个,我可以根据需要将错误降低到
2.0*10^-18
。所以我认为这可能足够接近,可以很好地估计误差。我写道:

#include <stdio.h> // printf
#include <math.h>  // needed for sqrt
#include <float.h>

int main() {
    double myVar = sqrt (3);
    long double r = sqrtl(3.0L);
    long double error = abs(r-myVar) / r;
    printf("Double:\t\t%f\n", myVar);
    printf("Precision:\t%d digits; %.*Lg\n",LDBL_DIG,LDBL_DIG,r);
    printf("Error:\t\t%.*Lg\n", LDBL_DIG, error);
}

如何修复该错误?

您在这里打印
Double:1.732051
时出错
printf(“Double:\t\t%f\n”,myVar)

双myVar的实际值为

1.732050807568877281 //18 digits

因此,1.732050807568877281-1.732050807568877281为零

printf
在使用
%f
时,无精度时,轮数加倍到6位

e、 g

我的输出:
-0.0000000000000000 4445


如果结果为0,则您的
长双精度
双精度
是相同的。

根据C标准
printf(“%f”,d)
将默认为小数点后的6位数字。这不是你的双精度的全部精度


在您的体系结构中,double和longduble可能恰好是相同的。在我的体系结构中,它们有不同的大小,在您的示例代码中有一个非零错误。

每个程序员都应该了解Goldberg的浮点算法,这是您正在寻找的明确指南


在计算错误时,至少在使用C时,您希望使用
fabsl
而不是
abs
。(在C中,
abs
是整数)。通过此替换,我得到:

Double:     1.732051
Precision:  18 digits; 1.73205080756887729
Error:      5.79643049346087304e-17
(在Mac OS X 10.8.3和Apple clang 4.0上计算。)

使用
long double
估计
double
中的误差对于一些简单的计算来说是一种合理的方法,除了:

  • 如果您正在计算更精确的
    长双精度
    结果,为什么还要费心于
    双精度
  • 计算序列中的错误行为很难描述,并且可能发展到
    long double
    无法提供准确结果的准确估计的程度
  • 存在一些反常的情况,
    长双精度
    得到的结果不如
    双精度
    得到的结果准确。(大多数情况下,当有人构建一个例子来给学生上一课时会遇到,但他们仍然存在。)
一般来说,在一系列计算中,没有简单有效的方法来计算浮点结果中的误差。如果有,它将是一种有效的计算更精确结果的方法,我们将使用它,而不仅仅是浮点计算

在特殊情况下,例如在开发数学库例程时,会仔细研究特定代码序列导致的错误(并根据需要重新设计代码,以获得可接受的错误行为)。更常见的是,通过执行各种“实验”来估计误差,以了解结果随输入变化的程度,或者通过研究系统的一般数学行为来估计误差

你还问“我想得到一个函数,它给出任何数字的误差。”好吧,这很简单,给定任何数字x和计算结果x',误差正好是x'-x。实际的问题是,您可能没有可以用来轻松计算该表达式的x描述。在您的示例中,x是sqrt(3)。显然,那么,误差是sqrt(3)–x,x正好是1.732050807568871931766041234368485839023590087890625。现在您需要做的就是评估sqrt(3)。换句话说,数值计算误差与数值计算原始数值一样困难

您是否希望对某类数字执行此分析


另外,您是真的想计算错误还是仅仅计算错误的一个好界限?后者稍微容易一些,尽管对于计算序列来说仍然很困难。对于所有基本操作,IEEE 754要求生成的结果必须是最接近数学精确结果的结果(在所用舍入模式的适当方向上)。在“四舍五入”模式下,这意味着每个结果与精确结果之间的距离最多为1/2 ULP(最小精度单位)。对于标准数学库(正弦、对数等)中的运算,大多数库将在精确结果的几个ULP内生成结果。

获得保证包含计算实际值的区间的一种方法是使用。然后,将
double
结果与间隔进行比较,可以告诉您
double
计算与实际计算的最差距离

Frama-C的值分析可以通过选项
-所有舍入模式
为您实现这一点

double Frama_C_sqrt(double x);

double sqrt(double x)
{
  return Frama_C_sqrt(x);
}

double y;

int main(){
  y = sqrt(3.0);
}
使用以下工具分析程序:

frama-c -val t.c -float-normal -all-rounding-modes
[value] Values at end of function main:
      y ∈ [1.7320508075688772 .. 1.7320508075688774]
这意味着
sqrt(3)
的实际值,以及如果程序使用实数计算,则变量
y
中的值,都在
范围内
[1.732050807568772..1.732050807568774]


Frama-C的值分析不支持
long-double
类型,但如果我理解正确,您只是使用
long-double
作为参考来估计
double
的错误。这种方法的缺点是
长双精度
本身不精确。使用Frama-C值分析中实现的区间算法,可以保证计算的实际值在显示的范围内。

那么,您是在一个问题中问两个问题吗?你如何计算答案中的错误,以及第二段代码有什么问题?@迈克:是的,我问两个问题。我已经使第二个更精确了。所以如果第二个是
Double:     1.732051
Precision:  18 digits; 1.73205080756887729
Error:      5.79643049346087304e-17
double Frama_C_sqrt(double x);

double sqrt(double x)
{
  return Frama_C_sqrt(x);
}

double y;

int main(){
  y = sqrt(3.0);
}
frama-c -val t.c -float-normal -all-rounding-modes
[value] Values at end of function main:
      y ∈ [1.7320508075688772 .. 1.7320508075688774]