【问题标题】:Does exist two numbers that multiplied (or divided) each other introduce error?是否存在两个相互相乘(或相除)的数字会引入错误?
【发布时间】:2019-07-15 11:37:14
【问题描述】:

这是我正在进行的测试的bank,了解FP 基本操作(+-*/)如何引入错误:

#include <iostream>
#include <math.h>

int main() {
    std::cout.precision(100);

    double a = 0.499999999999999944488848768742172978818416595458984375;

    double original = 47.9;
    double target = original * a;    
    double back = target / a;

    std::cout <<  original << std::endl;
    std::cout <<  back << std::endl;
    std::cout <<  fabs(original - back) << std::endl; // its always 0.0 for the test I did
}

你能告诉我两个值(originala),一次是 *(或 /),由于 FP 数学,引入错误?

如果它们存在,是否可以确定该错误是由* 还是/ 引入的?如何? (因为你需要两者来恢复价值;80 bit?)

使用+ 很容易(只需将0.499999999999999944488848768742172978818416595458984375 添加到0.5,您就会得到1.0,至于0.5 + 0.5)。

但我无法对 */ 做同样的事情。

【问题讨论】:

  • 0.499999999999999944488848768742172978818416595458984375 * 2?
  • 你的意思是被零除?或者你所说的“错误”是什么意思?
  • @NathanOliver:不,它的 0 coliru.stacked-crooked.com/a/c4f1b2d8b9861ad9
  • 另请注意,如果系统将 IEEE754 用于浮点值,那么对于双精度,精度只有小数点后 17 位,超出此范围的任何内容都会导致精度损失。跨度>
  • @Someprogrammerdude:我知道。我只是想证明这一点,因此将两个值相乘(并返回原​​始值)会引入错误(即在*/ 上出现偏差)。

标签: c++ floating-point floating-accuracy


【解决方案1】:

输出:

#include <cstdio>

int main(void)
{
    double a = 1000000000000.;
    double b = 1000000000000.;
    std::printf("a = %.99g.\n", a);
    std::printf("a = %.99g.\n", b);
    std::printf("a*b = %.99g.\n", a*b);
}

是:

a = 1000000000000。 a = 1000000000000。 a*b = 999999999999999983222784。

假设 IEEE-754 基本 64 位二进制浮点,正确舍入到最接近,与偶数相关。

显然,999999999999999983222784 与 1000000000000•1000000000000、1000000000000000000000000 的精确数学结果不同。

【讨论】:

  • 好的,这是 a*b 不返回预期结果的示例。第一个问题:任何低值和逗号的例子?即“浮点”?
  • @markzzz:逗号的存在不会使数字成为浮点数。
  • @rubenvb:当然!这就是我在"" 之间写它的原因。我的意思是曾经以十进制表示的值包含逗号。
  • @markzzz:浮点数刻度。对于二进制浮点数,它们可以重复乘以或除以 2 而不会改变关系(直到格式范围的限制)。因此,要将 1000000000000 更改为非整数示例,请将其除以 2^13。然后我们有 122070312.5•122070312.5 产生 14901161193847656,这显然不是精确的数学结果,因为精确的数学结果必须以“.25”结尾。
  • @markzzz:不是“逗号”使数字变成浮点数。有些人使用逗号来标记表示数字的小数(子整数)部分的数字的开头,有些人使用句点。但是非零子整数数字的存在仅意味着数字是非整数,而不是浮点数。对于浮点数,它必须用一个浮点数来表示。换句话说,它的表示必须包括乘以一个可变指数的幂次方,例如 M•2^e。
【解决方案2】:

将任意两个大数相乘,可能会出现错误,因为可表示的值在高值范围内的距离很大。

虽然这个误差在绝对值上可能很大,但相对于数字本身的大小来说仍然很小,所以如果你进行反向除法,第一次运算的误差会以相同的比例缩小,并且完全消失。因此,这一系列操作是稳定的。

如果乘法的结果大于可表示的最大值,那么它将溢出到无穷大(可能取决于配置),在这种情况下,反向除法不会产生原始值,而是保持无穷大。

同样,如果你用一个很大的数除,你可能会下溢最小的可表示值,导致零或低于正常值。

数字不一定要很大。在考虑巨大的价值时,更容易理解这个问题。该问题也适用于非常小的值。例如:

2.100000000000000088817841970012523233890533447265625 ×
2.100000000000000088817841970012523233890533447265625

正确结果:

4.410000000000000373034936274052605470949292688633679117285...

浮点结果示例:

4.410000000000000142108547152020037174224853515625

错误:

2.30926389122032568296724439173008679117285652827862296732064351090230047702789306640625
× 10^-16

【讨论】:

  • 所以这只发生在巨大的数字上?数字 [-50.0, 50.0] 不可能(例如)?为什么?
  • @markzzz 仅仅是因为每个可表示的浮点数之间都有无限多的有理数,并且可表示的数字之间的差距随着指数的增加而扩大。
  • 这就是“高级”动机。数学一是什么?它什么时候发生向上/向下“四舍五入”到另一个数字?你能给我举个例子吗?
  • @markzzz “舍入”发生在浮点处理器中。根本没有代表正确结果的位序列,所以它所能做的最好的就是返回最接近的答案。
【解决方案3】:

是否存在两个相乘(或相除)的数字会产生误差?

使用"%a" 更容易看到这一点。

当结果的精度不足时,会发生舍入。通常double 具有 53 位 二进制 精度。将下面的 2 个 27 位数字相乘得到精确的 53 位答案,但 2 个 28 位的数字不能形成 55 位有效答案。

除法很容易演示,试试1.0/n*n

int main(void) {
  double a = 1 + 1.0/pow(2,26);
  printf("%.15a,  %.17e\n", a, a);
  printf("%.15a,  %.17e\n", a*a, a*a);
  double b = 1 + 1.0/pow(2,27);
  printf("%.15a,  %.17e\n", b, b);
  printf("%.15a,  %.17e\n", b*b, b*b);

  for (int n = 47; n < 52; n += 2) {
    volatile double frac = 1.0/n;
    printf("%.15a,  %.17e %d\n", frac, frac, n);
    printf("%.15a,  %.17e\n", frac*n, frac*n);
  }
  return 0;
}

输出

//v-------v         27 significant bits.
0x1.000000400000000p+0,  1.00000001490116119e+00
//v-------------v   53 significant bits.
0x1.000000800000100p+0,  1.00000002980232261e+00
//v-------v         28 significant bits.
0x1.000000200000000p+0,  1.00000000745058060e+00
//v--------------v  not 55 significant bits.
0x1.000000400000000p+0,  1.00000001490116119e+00
//              ^^^ all zeros here, not the expected mathematical answer.

0x1.5c9882b93105700p-6,  2.12765957446808505e-02 47
0x1.000000000000000p+0,  1.00000000000000000e+00
0x1.4e5e0a72f053900p-6,  2.04081632653061208e-02 49
0x1.fffffffffffff00p-1,  9.99999999999999889e-01  <==== Not 1.0
0x1.414141414141400p-6,  1.96078431372549017e-02 51
0x1.000000000000000p+0,  1.00000000000000000e+00

【讨论】:

  • 所以 a = (a / b) * b 在 fp 中永远不会得到保证,对吧? a = (a * b) / b 也一样
猜你喜欢
  • 2011-01-23
  • 1970-01-01
  • 2011-03-14
  • 2018-04-08
  • 1970-01-01
  • 2010-10-01
  • 2020-12-17
  • 1970-01-01
  • 2010-12-24
相关资源
最近更新 更多