C++ 中算术运算和转换的机器值

p d*_*p d 2 c++ floating-point precision

我是 C++ 编程新手。我想知道 C++ 中以下简单操作的结果会获得什么可能的机器值:

int a = 3;
double inv_a;

inv_a =  (double) 1 / (double) a;
std::cout << "a*(1/a) = " << std::setprecision(30) << (double) a * inv_a << std::endl;
Run Code Online (Sandbox Code Playgroud)

例如,我想知道是否输出

(double) a * inv_a
Run Code Online (Sandbox Code Playgroud)

对于 a 的任何可能的 int 值,作为双精度值始终大于或等于 1.000...0。是否有可能获得严格小于 1 但非常接近它的值,例如 0.999...?当我将结果转换为 int 时,我正在考虑这个问题。先感谢您。

我的实验总是给出准确的 1。

Eri*_*hil 6

\n

例如,我想知道 的输出是否(double) a * inv_a始终大于或等于 1.000...0 作为双精度

\n
\n

该关系不成立,因为使用 IEEE-754 二进制 64 (\xe2\x80\x9cdouble precision\xe2\x80\x9d) 算术,1./49*49产生 0.99999999999999988897769753748434595763683319091796875。

\n

当倒数进入低于正常范围时,结果可能大于 1。例如,对于a= 2 1023 +2 9711/a*a是 1+2 \xe2\x88\x9252。这是因为次正常结果降低了精度,从而导致更大的误差。在正常范围内,不会出现这种情况。

\n

当所有涉及的值(包括倒数)都在正常范围内时,则成立反比关系;a * inv_a总是小于或等于 1,有时甚至更小。

\n

在这个答案中,代码字体中的表达式表示浮点计算。非代码字体的表达式表示普通实数算术。

\n

这里证明,只要我们保持在正常范围内并使用以 2 为基数的1浮点格式并四舍五入到最接近的值,结果就不可能大于 1。考虑一些x。令p为 2 的幂,使得xp在 [1, 2) 中。只要我们在正常范围内,1/x*x= 1/(xp)*xp,其中xp= x*p,由于浮点表示的性质。因此,仅当 [1, 2) 中存在这样的x时,才存在满足 1<x 。1/x*x

\n

由于使用舍入到最接近的值,并且 \xc2\xbd < 1/ x \xe2\x89\xa4 1, 1/x= 1/ x + 对于一些令人满意的 \xe2\x88\x92\xc2\xbd u \xe2\x89\xa4 \xe2\x89\xa4 +\xc2\xbd u,其中u是 [\xc2\xbd, 1) 中浮点数的最小精度单位。那么1/x\xe2\x80\xa2 x = (1/ x + ) x = 1 + x。由于x < 2, \xe2\x88\x92\xc2\xbd u \xe2\x89\xa4 \xe2\x89\xa4 +\xc2\xbd u意味着 \xe2\x88\x92 u < x < + u。因此1/x\xe2\x80\xa2 xu和 1 之间。观察到,[\xc2\xbd, 1) 中数字的ULP是 [1, 2) 中数字 ULP 的一半。因此1/x\xe2\x80\xa2 x比 [1, 2) 中下一个更大的数字更接近 1。因此,在计算 时1/x*x,舍入到最接近的值不会产生大于 1 的结果。

\n

为了完整起见,请注意,如果a是零、NaN 或无穷大,则 将会产生 NaN 1/a*a,因此结果不会小于、等于或大于 1。并且,如果a是次正规的,1/a可以产生无穷大,所以1/a*a将是无穷大并且比较结果大于 1。

\n

笔记

\n

1我预计这也适用于其他基地,但手头没有证据。

\n

  • @Ripi2:不,它们不是垃圾。它们是通过数学确定的。IEEE-754标准精确定义了浮点数的值,1以下的双精度数正好是0.99999999999999988897769753748434595763683319091796875。此外,将其四舍五入到 15 位将恰好产生 1,这将掩盖该问题所询问的事实。 (8认同)
  • @alias:正如答案所说,“当倒数进入低于正常范围时,结果可能大于 1。” “x”的倒数约为 8.895•10^−309(十六进制格式,正好是 0x1.995b5c7804bbcp-1024),处于次正常范围内。当后面的文字说“所有值都在正常范围内”时,倒数就是所涉及的值之一。 (2认同)