什么使这些数学上等效的浮点方程提供了不同的答案?
因此,我知道计算机在处理多种语言时并不是特别“好”。我已经看过0.1 + 0.2
在不同语言中失败了几次。我还了解到,编译器可以通过评估编译过程中的某些表达方式来优化一些。知道了这一切,我进行了一些实验。
这是我写的一些C代码:
#include <stdio.h>
float g1 = 0.1;
float g2 = 0.2;
int main() {
float l1 = 0.1;
float l2 = 0.2;
printf("a: %.50f\n", 0.3);
printf("b: %.50f\n", 0.1 + 0.2);
printf("c: %.50f\n", (0.1 * 10 + 0.2 * 10) / 10);
printf("d: %.50f\n", l1 + l2);
printf("e: %.50f\n", (l1 * 10 + l2 * 10) / 10);
printf("f: %.50f\n", g1 + g2);
printf("g: %.50f\n", (g1 * 10 + g2 * 10) / 10);
return 0;
}
这是它的输出:
a: 0.29999999999999998889776975374843459576368331909180
b: 0.30000000000000004440892098500626161694526672363281
c: 0.29999999999999998889776975374843459576368331909180
d: 0.30000001192092895507812500000000000000000000000000
e: 0.30000001192092895507812500000000000000000000000000
f: 0.30000001192092895507812500000000000000000000000000
g: 0.30000001192092895507812500000000000000000000000000
完全有意义的是“ D”,“ E”,“ F”和“ G”具有相同的结果。我认为“ A”,“ B”和“ C”与“ D”,“ E”,“ F”和“ G”的不同是由于编译时间和运行时间评估之间的差异。但是,我发现“ A”和“ C”是相同的,但“ B”是不同的。
我目前的理解是正确的吗?为什么“ a”和“ c”是相同的,而“ b”是不同的?
So I know computers aren't especially 'good' at handling floats in many languages. I've seen 0.1 + 0.2
fail quite a few times in different languages. I've also learned that compilers can optimize a bit by evaluating some expressions during compilation. Knowing all that, I ran a little experiment.
Here's some C code I wrote:
#include <stdio.h>
float g1 = 0.1;
float g2 = 0.2;
int main() {
float l1 = 0.1;
float l2 = 0.2;
printf("a: %.50f\n", 0.3);
printf("b: %.50f\n", 0.1 + 0.2);
printf("c: %.50f\n", (0.1 * 10 + 0.2 * 10) / 10);
printf("d: %.50f\n", l1 + l2);
printf("e: %.50f\n", (l1 * 10 + l2 * 10) / 10);
printf("f: %.50f\n", g1 + g2);
printf("g: %.50f\n", (g1 * 10 + g2 * 10) / 10);
return 0;
}
Here's its output:
a: 0.29999999999999998889776975374843459576368331909180
b: 0.30000000000000004440892098500626161694526672363281
c: 0.29999999999999998889776975374843459576368331909180
d: 0.30000001192092895507812500000000000000000000000000
e: 0.30000001192092895507812500000000000000000000000000
f: 0.30000001192092895507812500000000000000000000000000
g: 0.30000001192092895507812500000000000000000000000000
It makes complete sense to be that "d," "e," "f" and "g" have the same result. I think "a," "b" and "c" being different from "d," "e," "f" and "g" is because of the difference between compile-time and run-time evaluation. However, I find it strange that "a" and "c" are the same but "b" is different.
Are my current understandings correct? Why are "a" and "c" the same while "b" is different?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
在您的“ A”,“ B”和“ C”案例中指定为
printf
的文字(常数)值是类型double
,而不是float < /代码>。添加
f
后缀,使其使其float
,然后在用clang-cl编译后运行它,给出了所有情况相同的答案:output :(
使用您的使用原始代码,我得到的输出是您所引用的
。代码> double ,编译器如何评估这些表达式可能是特定于实现的。添加显式
f
后缀将(应该?)删除任何歧义。The literal (constant) values specified as arguments to
printf
in your "a", "b" and "c" cases are of typedouble
, notfloat
. Adding thef
suffix, to make themfloat
, and then running it after compiling with clang-cl gives the same answer for all cases:Output:
(With your original code, the output I get is as you have quoted.)
Now, although
float
arguments to functions with variadic arguments (likeprintf
) are promoted todouble
, how the compiler evaluates those expressions will likely be implementation-specific. Adding the explicitf
suffix will (should?) remove any ambiguity.大多数精确的小数分数不能完全表示为二进制浮点数,精度有限。
对于IEEE 754双精度格式(C
double
),最接近0.1和0.2的表示都略高于确切的小数值,因此它们的总和也略高于确切的小数总和。但是,最接近的0.3表示值略低于确切的小数值。如果C编译器折叠常数表达式,则应考虑舍入规则。在转换为二进制表示之前,它不应用十进制常数 0.3 代替小数表达式
0.1 + 0.2
。它应该将0.1
和0.2
转换为其二进制表示形式,然后将它们总结到单个二进制表示。下面的示例说明,无论值来自常数还是来自具有相同常数分配的变量(与常数相同的类型,
double
在这种情况下):输出:输出:
一致性 :是一件好事!
对于IEEE 754单精度格式(C
float
),最接近0.1、0.2和0.3的表示均略高于确切的小数值。只要我们谨慎使用常数上的f
后缀使它们键入float
而不是double
,我们仍然会获得一致的结果使用常数和表达式使用这些常数分配的变量的表达式,如下所示:输出:
Most exact decimal fractions cannot be represented exactly as a binary floating point number with limited precision.
For IEEE 754 double precision format (C
double
), the closest representations of 0.1 and 0.2 are both slightly above the exact decimal value, so their sum is also slightly above the exact decimal sum. However, the closest representable value of 0.3 is slightly below the exact decimal value.If the C compiler folds constant expressions, it should take the rounding rules into account. It should not replace the decimal expression
0.1 + 0.2
with the decimal constant0.3
before converting to the binary representation. It should convert0.1
and0.2
to their binary representations and sum them to a single binary representation.The example below illustrates that the output is the same whether the values come from constants or from variables assigned with the same constants (and of the same type as the constants,
double
in this case):Output:
Consistency is a good thing!
For the IEEE 754 single precision format (C
float
), the closest representations of 0.1, 0.2, and 0.3 are all slightly above the exact decimal values. As long as we are careful to use thef
suffix on the constants to make them typefloat
instead ofdouble
, we still get consistent results for expressions using constants and expressions using variables assigned with those constants, as shown below:Output: