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

Machine values for arithmetic operations and castings in C++

提问人:p d 提问时间:10/9/2023 最后编辑:Paul Floydp d 更新时间:10/9/2023 访问量:130

问:

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

对于任何可能的 int 值,始终大于或等于 1.000...0 作为双精度值或不双精度值。是否有可能获得严格小于 1 但非常接近 0.999 的值......?当我将结果转换为 int 时,我担心这种情况。先谢谢你。

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

C++ 浮点 精度

评论

0赞 Sam Varshavchik 10/9/2023
是的,这是可能的。请参阅重复的问题。
7赞 PaulMcKenzie 10/9/2023
我是 C/C++ 编程的新手——没有这样的语言。要么是,要么是.话虽如此,浮点问题几乎出现在每一种高级语言中。这就是重复链接的全部意义所在。C/C++CC++
1赞 273K 10/9/2023
不要使用标记 C 标记 C++。没有这样的编程语言 C/C++。几乎任何语言都能够调用 CRT 绑定,但您不能将其称为 Python/C 或 C#/C。std::cout
0赞 Eric Postpischil 10/9/2023
@SamVarshavchik:这不是这个问题的重复。这个问题一般是浮点运算近似于实数算术这一事实的解决。这篇文章提出了一个在另一个问题中没有解决的具体问题,除非它的一些答案偶然提到了它(这仍然不能将其定性为重复,因为一些被埋没的偶然提及不是一个有用的重复)。
1赞 Basile Starynkevitch 10/9/2023
阅读更多关于 2023 年浮点表示的事实标准IEEE754

答:

6赞 Eric Postpischil 10/9/2023 #1

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

这种关系不成立,因为使用 IEEE-754 binary64(“双精度”)算术产生 0.999999999999999988897769753748434595763683319091796875。1./49*49

当倒数进入低于正常范围时,结果可能大于 1。例如,with = 21023+2971,则为 1+2−52。这是因为次正态结果会降低精度,从而导致更大的误差。在正常范围内,不会发生这种情况。a1/a*a

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

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

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

由于使用四舍五入到最接近,并且 1/2 < 1/x ≤ 1,对于满足 −1/2 u ≤ ε ≤ +1/2 u 的某些ε,= 1/x + ε,其中 u 是 [1/2, 1] 中浮点数的最低精度单位。则 •x = (1/x + ε)x = 1 + ε x由于 x < 2,−1/2 u ≤ ε ≤ +1/2u 意味着 −u < εx < +u因此 •x 在 1 的 u 范围内。请注意,u 是 [1/2, 1] 中数字的 ULP,是 [1, 2] 中数字的 ULP 的一半。因此,•x 比 [1, 2] 中下一个较大的数字更接近 1。因此,在计算时,四舍五入到最接近不会产生大于 1 的结果。1/x1/x1/x1/x1/x*x

For completeness, note that if is zero, a NaN, or an infinity, a NaN will be produced by , so that result will not be less than, equal to, or greater than 1. And, if is subnormal, can produce infinity, so will be an infinity and will compare as greater than 1.a1/a*aa1/a1/a*a

Note

1 I expect this holds for other bases but do not have a proof at hand.

评论

0赞 Ripi2 10/9/2023
Please, truncate (or round) that number to 15 digits. It's the amount of trusted digits for a 64 bits precision (the 16th is not always true). Rest of digits is just garbage.
8赞 Eric Postpischil 10/9/2023
@Ripi2: No, they are not garbage. They are mathematically determined. The IEEE-754 standard precisely defines the value of floating-point numbers, and the double-precision number just below 1 is exactly 0.99999999999999988897769753748434595763683319091796875. Further, rounding that to 15 digits would produce exactly 1, which would conceal the fact this question asks about.
0赞 alias 10/9/2023
Regarding never exceeding 1: How about ? This number is normal and evaluates to in my Python shell.x * (1/x)x = 1.124225499393696e308x * (1/x)1.0000000000000002
2赞 Eric Postpischil 10/9/2023
@alias: As the answer says, “When the reciprocal gets into the subnormal range, results greater than 1 are possible.” The reciprocal of your is about 8.895•10^−309 (in hexadecimal format, exactly 0x1.995b5c7804bbcp-1024), which is in the subnormal range. When the text following that says “With all values involved in the normal range,” the reciprocal is one of the values involved.x
0赞 p d 10/9/2023
@EricPostpischil Thank you! With your comment in mind, 'inv_a' has a mathematical value 1/a + &epsilon where |&epsilon| is less than some negative power of 2 and a*&epsilon may yield a discrepancy between the mathematical and machine values of a * inv_a? Whenever we multiply two floating numbers, a roundup operation is always performed besides 'cout'?