首页 > 解决方案 > 将双精度值乘以 10 时究竟发生了什么

问题描述

我最近一直想知道乘以浮点数。
假设我有一个数字,例如 3.1415,保证 3 位精度。
现在,我将此值乘以 10,得到 31.415X,其中 X 是由于精度有限而无法定义的数字。

现在,我可以确定,这五个 get 被转移到了精确的数字上吗?如果一个数字被证明精确到 3 位,我不希望这五个总是出现在那里,但是在研究了许多 c++ 案例之后,我注意到它总是会发生。

然而,从我的角度来看,这没有任何意义,因为浮点数是以二为底存储的,所以乘以十是不可能的,它总是乘以 10.something。

我问这个问题是因为我想创建一个计算类型精确度的函数。我想出了这样的事情:

template <typename T>
unsigned accuracy(){
        unsigned acc = 0;
        T num = (T)1/(T)3;
        while((unsigned)(num *= 10) == 3){
                acc++;
                num -= 3;
        }
        return acc;
}

现在,这适用于我使用过的任何类型,但我仍然不确定第一个不精确的数字是否总是以不变的形式结转。

标签: c++floating-pointnumbersprecision

解决方案


我将专门讨论 IEEE754 双打,因为我认为这是您所要求的。

双精度定义为一个符号位、一个 11 位指数和一个 52 位尾数,它们连接起来形成一个 64 位值:

sign|exponent|mantissa

指数位以偏置格式存储,这意味着我们存储实际指数 +1023(对于双精度数)。全零指数和全一指数是特殊的,所以我们最终能够表示从 2^-1022 到 2^+1023 的指数

一个常见的误解是整数值不能用双精度数精确表示,但我们实际上可以通过正确设置尾数和指数来精确地将任何整数存储在 [0,2^53)中,实际上是范围 [2^52,2 ^53)只能存储该范围内的整数值。所以 10 很容易准确地存储在一个双精度数中。

当谈到乘以双打时,我们实际上有两个这种形式的数字:

A = (-1)^sA*mA*2^(eA-1023)
B = (-1)^sB*mB*2^(eB-1023)

其中 sA,mA,eA 是 A 的符号、尾数和指数(对于 B 也是如此)。

如果我们将这些相乘:

A*B = (-1)^(sA+sB)*(mA*mB)*2^((eA-1023)+(eB-1023))

我们可以看到我们只是将指数相加,然后将尾数相乘。这实际上对精度来说还不错!我们可能会溢出指数位(从而得到无穷大),但除此之外,我们只需将中间尾数结果四舍五入回到 52 位,但这在最坏的情况下只会改变新尾数中的最低有效位。

最终,您将看到的误差将与结果的大小成正比。但是,无论如何,双打的误差与它们的大小成正比,所以这确实是我们能得到的最安全的。估算数字中的误差的方法是 |magnitude|*2^-53。在您的情况下,由于 10 是精确的,因此唯一的错误将出现在 pi 的表示中。它将有 ~2^-51 的错误,因此结果也是如此。

根据经验,在考虑精度问题时,我认为双精度数约为 15 位小数。


推荐阅读