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

问题描述 投票:0回答:1

我是 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;

例如,我想知道

的输出是否
(double) a * inv_a
对于 a 的任何可能的 int 值,

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

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

c++ floating-point precision
1个回答
5
投票

例如,我想知道

(double) a * inv_a
的输出是否始终大于或等于 1.000...0 作为 double

该关系不成立,因为使用 IEEE-754 二进制 64(“双精度”)算术,

1./49*49
产生 0.99999999999999988897769753748434595763683319091796875。

当倒数进入低于正常范围时,结果可能大于 1。例如,对于

a
= 21023+2971
1/a*a
为 1+2−52。这是因为次正常结果降低了精度,从而导致更大的误差。在正常范围内,不会出现这种情况。

当所有涉及的值(包括倒数)都在正常范围内时,则成立反比关系;

a * inv_a
始终小于或等于 1,有时更小。

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

这里有一个证明,只要我们保持在正常范围内并使用以二为基数的1浮点格式并四舍五入到最接近的值,结果就不可能大于1。考虑一些x。令 p 为 2 的幂,使得 xp 位于 [1, 2) 中。只要我们在正常范围内,

1/x*x
=
1/(xp)*xp
,其中
xp
=
x*p
,由于浮点表示的性质。因此,仅当 [1, 2) 中存在这样的 x 时,才存在 <
x
使得 11/x*x

由于使用了舍入到最接近的方法并且 ½ < 1/x ≤ 1,对于某些 𝜀 满足 −½

u
≤ 𝜀 ≤ +½u1/x = 1/x + 𝜀,其中 u是 [½, 1) 中浮点数的最小精度单位。那么
1/x
x = (1/x + 𝜀)x = 1 + 𝜀x。因为 x < 2, −½u ≤ 𝜀 ≤ +½u 意味着 −u < 𝜀x < +u。因此,
1/x
x 位于 1 的 u 范围内。观察到,[½, 1) 中数字的 ULP u 是 [1, 2) 中数字 ULP 的一半。因此,
1/x
x 比 [1, 2) 中下一个更大的数字更接近 1。因此,在计算
1/x*x
时,舍入到最接近的值不会产生大于 1 的结果。

为了完整起见,请注意,如果

a
为零、NaN 或无穷大,则
1/a*a
将生成 NaN,因此结果不会小于、等于或大于 1。并且,如果
a
是次正规的,
1/a
可以产生无穷大,因此
1/a*a
将是无穷大并且比较结果大于 1。

注意

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

© www.soinside.com 2019 - 2024. All rights reserved.