【问题标题】:Can you help me to understand what "significant digits" means in floating point math?你能帮我理解浮点数学中的“有效数字”是什么意思吗?
【发布时间】:2017-11-04 13:32:06
【问题描述】:

对于我正在学习的内容,一旦我将浮点值转换为十进制值,我需要的“有效数字”就是一个固定数字(例如,17 表示双精度数)。总计 17 个:小数点前后分隔符。

例如这段代码:

typedef std::numeric_limits<double> dbl;

int main()
{
    std::cout.precision(dbl::max_digits10);
    //std::cout << std::fixed;    

    double value1 = 1.2345678912345678912345;
    double value2 = 123.45678912345678912345;
    double value3 = 123456789123.45678912345;

    std::cout << value1 << std::endl;
    std::cout << value2 << std::endl;
    std::cout << value3 << std::endl;
}

将正确“显示”17 个值:

1.2345678912345679
123.45678912345679
123456789123.45679

但如果我提高cout(即std::cout.precision(100))的精度,我可以看到17 范围之后还有其他数字:

1.2345678912345678934769921397673897445201873779296875
123.456789123456786683163954876363277435302734375
123456789123.456787109375

为什么要忽略它们?它们也存储在 variables/double 中,因此它们稍后会影响整个“数学”(除法、乘法、求和等)。

“有效数字”是什么意思?还有其他...

【问题讨论】:

  • 意思和其他地方一样。如果您测量某物并确定它大约 1 英寸长,然后转换为公制并说它大约 2.54 厘米长,那么您就陷入了错误的精确度。原始值有 1 位有效数字;新的有3。第二个值的最后两位是废话。
  • 当然,但是处理器会“细化” 17° 之后的数字,而不是“截断”它们。 N步后有积累……

标签: c++ floating-point digit


【解决方案1】:

你能帮我理解浮点数学中的“有效数字”是什么意思吗?

对于 FP 数字,就像数学实数一样,有效数字是不以 0 开头的值的前导数字,然后根据上下文,到 1) 小数点, 2) 最后一个非零数字,或 3) 最后打印的数字。

123.         // 3 significant decimal digits
123.125      // 6 significant decimal digits
0.0078125    // 5 significant decimal digits
0x0.00123p45 // 3 significant hexadecimal digits
123000.0     // 3, 6, or 7 significant decimal digits depending on context

当关注十进制有效数字和像double这样的FP类型时。问题通常是“需要或关注多少十进制有效数字?”

几乎所有 C FP 实现都使用 二进制 编码,因此所有有限 FP 都是 精确 2 的幂和。每个有限 FP 精确。通用编码使大多数double 有 53 个二进制数字是有效位 - 所以 53 有效二进制数字。这如何显示为小数通常是混乱的根源。

// Example 0.1 is not an exact sum of powers of 2 so a nearby value is used.
double x = 0.1;
// x takes on the exact value of 
// 0.1000000000000000055511151231257827021181583404541015625
// aka 0x1.999999999999ap-4
// aka base2: 0.000110011001100110011001100110011001100110011001100110011010
// The preceding and subsequent doubles
// 0.09999999999999999167332731531132594682276248931884765625
// 0.10000000000000001942890293094023945741355419158935546875
//   123456789012345678901234567890123456789012345678901234567890

从上面可以看出,x 有超过 50 个十进制有效数字。然而,该值与预期的 0.1 到 16 个 十进制有效数字 匹配。或者由于前面和后面可能的 double 值在 17 位上不同,所以可以说 x 有 17 个十进制有效数字

“有效数字”是什么意思?

存在有效数字的各种含义,但对于C,2个常见的含义是:

  1. double 的文本值按预期为所有double 转换的十进制有效数字的数量。通常为 15。C 将其指定为 DBL_DIG,并且必须至少为 10。

  2. 需要打印文本值double 以区别于另一个double十进制有效数字的数量。通常为 17。C 将其指定为 DBL_DECIMAL_DIG,并且必须至少为 10。

为什么要忽略它们?

这取决于编码目标。很少需要所有数字的确切值。 (DBL_TRUE_MIN 可能有 752 个。)对于大多数应用程序,DBL_DECIMAL_DIG 就足够了。在某些应用程序中,DBL_DIG 可以。所以通常,忽略超过 17 的数字不会导致问题。

【讨论】:

  • 当我将 0.1 分配给双变量时,过去的 17 位是否存储在内存中?即 x 是否包含超过 50 位数字?我会说不...
  • @markzzz 0.1 在其编码中作为最接近的double 有一个 53 位二进制有效数字。准确地表达该数字需要 56 个十进制数字。这些数字是否重要取决于上面提到的上下文。下一个double 只有 17 个相同的十进制数字。
  • @markzzz "当我将 0.1 分配给双变量时,过去 17 位是否存储在内存中?"是的,但不是十进制数字,而是二进制数字。
  • “是但不是十进制数字”是什么意思? 0.10.1000000000000000055511151231257827021181583404541015625,而不是 0.10000000000000001。如果我评估两个不同的十进制数(但前 17 位数字相同),它们仍然不同:coliru.stacked-crooked.com/a/c7d1b34533c5ad5b
  • "下一个双精度数只有 17 个相同的十进制数字。"是的,就像我的例子一样,但它的评估方式不同。因此,即使得到相似的 17 位数字,它也是不同的。两者都有相同的 17 位数字,可以,但是数字不同 :O “17 位有效数字”是什么意思?
【解决方案2】:

请记住,浮点值不是实数。值之间存在差距,所有这些额外的数字虽然对实数有意义,但并不反映浮点值的任何差异。将浮点值转换为文本时,std::numeric_limits&lt;...&gt;::max_digits10 数字确保您可以将文本字符串转换回浮点并获取原始值。多余的数字不影响结果。

当您要求更多数字时看到的额外数字是转换算法尝试执行您所要求的结果的结果。该算法通常只是不断提取数字,直到达到所需的精度。它可以在写入max_digits10 digits 后开始输出零,但这是一个没有人关心的额外复杂性。这真的没有帮助。

【讨论】:

  • 那么您是说double value1 = 1.2345678912345678912345; 将只使用(一旦转换为十进制)1.2345678912345679?没有其他数据将在(比方说)乘法中处理?
  • @markzzz — 试试看!或者做一些更简单的事情:用digits10 写出适当的字符串,这样你就有了一个起点。然后编写一个程序,用这些数字后跟更多不同的数字来初始化两个浮点值。然后比较两个得到的浮点值。
  • @markzzz 它比这更复杂。 doublefloat 不存储基数为 10 位的数字。它们存储一个二进制值。该值通常没有精确的等价物,可以用一定数量的数字以基数 10 打印
  • @bolow:好的,但是一旦转换它们就会超过有效数字,不是吗?所以他们也在存储这些数字......
  • @markzzz:这是一个例子:2^-40。它很容易以(二进制)浮点格式存储。但它的十进制值为:9.094947017729282379150390625e-13,很多位数。
【解决方案3】:

只是为了添加 Pete Becker 的答案,我认为您将找到二进制尾数的精确十进制表示的问题与以下问题混淆了找到一些十进制表示唯一表示二进制尾数(给定一些固定的舍入方案)。

现在,关于第一个问题,您总是需要有限数量的十进制数字来精确表示二进制尾数(因为 2 除以 10)。

例如,您需要 18 位十进制数字准确表示二进制 1.0000000000000001,即十进制的 1.00000762939453125

但您只需要 17 位数字即可将其唯一地表示为 1.0000076293945312,因为没有其他数字具有精确值 1.0000076293945312xyz... 其中 01.0000076293945314720446049250313080847263336181640625 和 1.0000076293945310279553950749686919152736663818359375 )。

当然,这不是意味着给定一些十进制数字,您可以忽略 17 位之后的所有数字;这只是意味着,如果您应用用于在第 17 位产生小数的相同舍入方案并将其分配回双精度数,您将得到相同的原始双精度数。

【讨论】:

  • 所以我可以表示一个最多 17 位的小数? (在我放置逗号的地方漠不关心)。如果我有一个 20 位的小数,我总是会用双精度丢失 3 位?还有,转换可以四舍五入吗?
  • @markzzz 你不能代表所有个小数,与最大位数无关;例如,没有能够表示十进制“0.1”的双精度数,将四舍五入为“0.1000000000000000055511151231257827021181583404541015625”,而后者又由“0.10000000000000001”唯一表示。
  • @markzz,无论如何,是的,我们可以说双精度数最多有 17 个十进制有效数字
  • 但 1.0000076293945314720446049250313080847263336181640625 是小数。你是说我只会存储该小数的前 17 位吗?剩下的会亏吗?
  • @markzzz 是的,但是一旦转换它将产生相同的精确值(给定相同的编译器);所以你最终不会失去任何东西......
猜你喜欢
  • 2011-04-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-17
  • 1970-01-01
  • 2014-10-13
相关资源
最近更新 更多