Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/65.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++ 双舍入错误,即使使用DBL_DIG_C++_C_Floating Point_Double_Precision - Fatal编程技术网

C++ 双舍入错误,即使使用DBL_DIG

C++ 双舍入错误,即使使用DBL_DIG,c++,c,floating-point,double,precision,C++,C,Floating Point,Double,Precision,我正在尝试使用步骤0.3生成一个介于-10和10之间的随机数,尽管我希望这些值是任意值,并且在双精度浮点精度方面存在问题。Float.h的DBL_DIG是指不会出现舍入误差的最小精度[编辑:这是错误的,有关DBL_DIG的真实定义,请参阅Eric Postdischil的评论],但是当打印到这么多数字时,我仍然会看到舍入误差 #include <stdio.h> #include <float.h> #include <stdlib.h> int main(

我正在尝试使用步骤0.3生成一个介于-10和10之间的随机数,尽管我希望这些值是任意值,并且在双精度浮点精度方面存在问题。Float.h的DBL_DIG是指不会出现舍入误差的最小精度[编辑:这是错误的,有关DBL_DIG的真实定义,请参阅Eric Postdischil的评论],但是当打印到这么多数字时,我仍然会看到舍入误差

#include <stdio.h>
#include <float.h>
#include <stdlib.h>

int main()
{
  for (;;)
  {
    printf("%.*g\n", DBL_DIG, -10 + (rand() % (unsigned long)(20 / 0.3)) * 0.3);
  }
}

当然,一个简单的解决方案是只定义DBL_DIG 14,但我觉得这是在浪费准确性。为什么会发生这种情况?我如何防止这种情况发生?这不是重复的,因为我问的是DBL_DIG,以及如何找到不发生错误的最小精度。

对于问题中的特定代码,我们可以通过使用整数值直到最后一刻来避免过度舍入错误:

printf("%.*g\n", DBL_DIG,
    (-100 + rand() % (unsigned long)(20 / 0.3) * 3.) / 10.);
这是通过将原始表达式中的每个项乘以10得到的−10因为−100和.3变成3,然后将整个表达式除以10。所以我们在numerator1中关心的所有值都是整数,浮点表示的是精确到其精度范围内的整数

// reduce the _bit_ output precision by about the root of steps
#define LOG10_2 0.30102999566398119521373889472449
int digits_less = lround(sqrt(20 / 0.3) * LOG10_2);
for (int i = 0; i < 100; i++) {
  printf("%.*e\n", DBL_DIG - digits_less,
      -10 + (rand() % (unsigned long) (20 / 0.3)) * 0.3);
}

9.5000000000000e+00
-3.7000000000000e+00
8.6000000000000e+00
5.9000000000000e+00
...
-1.0000000000000e-01
8.0000000000000e-01
由于整数值将被精确计算,因此在最后除以10时只会有一个舍入误差,结果将是最接近所需值的两倍

在大多数情况下,为了避免舍入错误,我应该打印多少位?不仅仅是我上面的例子

仅仅使用更多的数字并不能解决一般情况。在大多数情况下,避免错误的一种方法是非常详细地学习浮点格式和算术,然后深思熟虑地、一丝不苟地编写代码。这种方法通常是好的,但并不总是成功的,因为它通常是由人类实施的,尽管做出了所有相反的努力,但人类仍会继续犯错误

脚注
1考虑到无符号long20/0.3是一个较长的讨论,涉及到对其他值和情况的意图和概括。

对于问题中的特定代码,我们可以通过使用整数值直到最后一刻来避免过度舍入错误:

printf("%.*g\n", DBL_DIG,
    (-100 + rand() % (unsigned long)(20 / 0.3) * 3.) / 10.);
这是通过将原始表达式中的每个项乘以10得到的−10因为−100和.3变成3,然后将整个表达式除以10。所以我们在numerator1中关心的所有值都是整数,浮点表示的是精确到其精度范围内的整数

// reduce the _bit_ output precision by about the root of steps
#define LOG10_2 0.30102999566398119521373889472449
int digits_less = lround(sqrt(20 / 0.3) * LOG10_2);
for (int i = 0; i < 100; i++) {
  printf("%.*e\n", DBL_DIG - digits_less,
      -10 + (rand() % (unsigned long) (20 / 0.3)) * 0.3);
}

9.5000000000000e+00
-3.7000000000000e+00
8.6000000000000e+00
5.9000000000000e+00
...
-1.0000000000000e-01
8.0000000000000e-01
由于整数值将被精确计算,因此在最后除以10时只会有一个舍入误差,结果将是最接近所需值的两倍

在大多数情况下,为了避免舍入错误,我应该打印多少位?不仅仅是我上面的例子

仅仅使用更多的数字并不能解决一般情况。在大多数情况下,避免错误的一种方法是非常详细地学习浮点格式和算术,然后深思熟虑地、一丝不苟地编写代码。这种方法通常是好的,但并不总是成功的,因为它通常是由人类实施的,尽管做出了所有相反的努力,但人类仍会继续犯错误

脚注 1考虑到无符号long20/0.3是一个较长的讨论,涉及到其他值和情况的意图和泛化

使用步骤0.3生成一个介于-10和10之间的随机数 我希望该程序能够处理任意的边界值和步长。 为什么会这样

问题的根源在于假设字符串0.3这样的典型实数可以精确地编码为双精度

一个double可以精确地编码大约264个不同的值。0.3不是其中之一

而是使用最接近的双精度。 精确值和2个最近值如下所示:

0.29999999999999993338661852249060757458209991455078125
0.299999999999999988897769753748434595763683319091796875  (best 0.3)
0.3000000000000000444089209850062616169452667236328125
所以OP的代码正在尝试-10和10,步骤0.2999。。。打印出-0.099999999996和0.79999999999999比-0.1和0.8更准确

。。。。如何防止这种情况发生

以更有限的精度打印

// reduce the _bit_ output precision by about the root of steps
#define LOG10_2 0.30102999566398119521373889472449
int digits_less = lround(sqrt(20 / 0.3) * LOG10_2);
for (int i = 0; i < 100; i++) {
  printf("%.*e\n", DBL_DIG - digits_less,
      -10 + (rand() % (unsigned long) (20 / 0.3)) * 0.3);
}

9.5000000000000e+00
-3.7000000000000e+00
8.6000000000000e+00
5.9000000000000e+00
...
-1.0000000000000e-01
8.0000000000000e-01
OP的代码实际上并不是在做步骤,因为这暗示了一个步骤为0.3的循环。以上数字_-less基于重复步骤,否则OP的方程保证减少约1位小数。精度的最佳降低取决于从0.3转换->双0.3 1/2位、除法1/2位、乘法1/2位和加法更复杂位估算所有计算的潜在累积误差

等待可能支持十进制浮点的下一版本C。 使用步骤0.3生成一个介于-10和10之间的随机数 我希望该程序能够处理任意的边界值和步长。 为什么会这样

问题的根源在于假设字符串0.3这样的典型实数可以精确地编码为双精度

一个double可以精确地编码大约264个不同的值。0.3不是其中之一

反而 使用最接近的双精度。 精确值和2个最近值如下所示:

0.29999999999999993338661852249060757458209991455078125
0.299999999999999988897769753748434595763683319091796875  (best 0.3)
0.3000000000000000444089209850062616169452667236328125
所以OP的代码正在尝试-10和10,步骤0.2999。。。打印出-0.099999999996和0.79999999999999比-0.1和0.8更准确

。。。。如何防止这种情况发生

以更有限的精度打印

// reduce the _bit_ output precision by about the root of steps
#define LOG10_2 0.30102999566398119521373889472449
int digits_less = lround(sqrt(20 / 0.3) * LOG10_2);
for (int i = 0; i < 100; i++) {
  printf("%.*e\n", DBL_DIG - digits_less,
      -10 + (rand() % (unsigned long) (20 / 0.3)) * 0.3);
}

9.5000000000000e+00
-3.7000000000000e+00
8.6000000000000e+00
5.9000000000000e+00
...
-1.0000000000000e-01
8.0000000000000e-01
OP的代码实际上并不是在做步骤,因为这暗示了一个步骤为0.3的循环。以上数字_-less基于重复步骤,否则OP的方程保证减少约1位小数。精度的最佳降低取决于从0.3转换->双0.3 1/2位、除法1/2位、乘法1/2位和加法更复杂位估算所有计算的潜在累积误差

等待可能支持十进制浮点的下一版本C。
这种说法是错误的:“Float.h的DBL_DIG是指没有舍入误差的最小精度……”@churill:这不是对这个问题的正确回答。请不要随意将浮点问题标记为该问题的副本。DBL_DIG的定义是小数位数的最大值,它保证将一个具有如此多有效小数位数的数字转换为两位数,再转换回一个具有如此多位数的小数位数,即可生成原始数字。结果是,对于更多的数字,往返转换可能会改变数字。此保证仅适用于“往返”的两次转换。它不能保证当您执行其他算术运算时,您将获得与十进制算术相同的结果。您的程序还有几个其他操作。@StavromulaBeta不可判定。对于每个操作,错误都会累积。例如,如果将循环中的0.0000001添加到一个更大的值上一百万次,它将不同于添加0.0000001*1000000。此语句为false:“Float.h的DBL_DIG是指不发生舍入错误的最小精度…”@丘里尔:这不是这个问题的正确答案。请不要随意将浮点问题标记为该问题的副本。DBL_DIG的定义是小数位数的最大值,它保证将一个具有如此多有效小数位数的数字转换为两位数,再转换回一个具有如此多位数的小数位数,即可生成原始数字。结果是,对于更多的数字,往返转换可能会改变数字。此保证仅适用于“往返”的两次转换。它不能保证当您执行其他算术运算时,您将获得与十进制算术相同的结果。您的程序还有几个其他操作。@StavromulaBeta不可判定。对于每个操作,错误都会累积。例如,如果将循环中的0.0000001添加到一个更大的值上一百万次,这将不同于添加0.0000001*1000000。这在这种情况下非常有效,但我希望程序能够处理边界和步长的任意值。这在这种情况下非常有效,但是我希望程序能够处理任意的边界值和步长。这是一个很好的答案。累积舍入误差的解释非常有用,我在以后编写代码时一定会记住它。这是一个很好的答案。对累积舍入误差的解释非常有用,将来编写代码时我一定会记住它。