什么使这些数学上等效的浮点方程提供了不同的答案?

发布于 2025-02-06 05:23:24 字数 1257 浏览 1 评论 0原文

因此,我知道计算机在处理多种语言时并不是特别“好”。我已经看过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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

旧故 2025-02-13 05:23:24

在您的“ A”,“ B”和“ C”案例中指定为printf的文字(常数)值是类型double,而不是float < /代码>。添加f后缀,使其使其float,然后在用clang-cl编译后运行它,给出了所有情况相同的答案:

#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.3f);
    printf("b: %.50f\n", 0.1f + 0.2f);
    printf("c: %.50f\n", (0.1f * 10 + 0.2f * 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;
}

output :(

a: 0.30000001192092895507812500000000000000000000000000
b: 0.30000001192092895507812500000000000000000000000000
c: 0.30000001192092895507812500000000000000000000000000
d: 0.30000001192092895507812500000000000000000000000000
e: 0.30000001192092895507812500000000000000000000000000
f: 0.30000001192092895507812500000000000000000000000000
g: 0.30000001192092895507812500000000000000000000000000

使用您的使用原始代码,我得到的输出是您所引用的

。代码> double ,编译器如何评估这些表达式可能是特定于实现的。添加显式f后缀将(应该?)删除任何歧义。

The literal (constant) values specified as arguments to printf in your "a", "b" and "c" cases are of type double, not float. Adding the f suffix, to make them float, and then running it after compiling with clang-cl gives the same answer for all cases:

#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.3f);
    printf("b: %.50f\n", 0.1f + 0.2f);
    printf("c: %.50f\n", (0.1f * 10 + 0.2f * 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;
}

Output:

a: 0.30000001192092895507812500000000000000000000000000
b: 0.30000001192092895507812500000000000000000000000000
c: 0.30000001192092895507812500000000000000000000000000
d: 0.30000001192092895507812500000000000000000000000000
e: 0.30000001192092895507812500000000000000000000000000
f: 0.30000001192092895507812500000000000000000000000000
g: 0.30000001192092895507812500000000000000000000000000

(With your original code, the output I get is as you have quoted.)

Now, although float arguments to functions with variadic arguments (like printf) are promoted to double, how the compiler evaluates those expressions will likely be implementation-specific. Adding the explicit f suffix will (should?) remove any ambiguity.

一萌ing 2025-02-13 05:23:24

大多数精确的小数分数不能完全表示为二进制浮点数,精度有限。

对于IEEE 754双精度格式(C double),最接近0.1和0.2的表示都略高于确切的小数值,因此它们的总和也略高于确切的小数总和。但是,最接近的0.3表示值略低于确切的小数值。

如果C编译器折叠常数表达式,则应考虑舍入规则。在转换为二进制表示之前,它不应用十进制常数 0.3 代替小数表达式0.1 + 0.2。它应该将0.10.2转换为其二进制表示形式,然后将它们总结到单个二进制表示。

下面的示例说明,无论值来自常数还是来自具有相同常数分配的变量(与常数相同的类型,double在这种情况下):

#include <stdio.h>

int main(void) {
    double d1 = 0.1;
    double d2 = 0.2;
    double d3 = 0.3;

    printf("0.1: %.50f\n", 0.1);
    printf("0.2: %.50f\n", 0.2);
    printf("0.1+0.2: %.50f\n", 0.1 + 0.2);
    printf("0.3: %.50f\n", 0.3);
    printf("\n");
    printf("d1: %.50f\n", d1);
    printf("d2: %.50f\n", d2);
    printf("d1+d2: %.50f\n", d1 + d2);
    printf("d3: %.50f\n", d3);
    return 0;
}

输出:输出:

0.1: 0.10000000000000000555111512312578270211815834045410
0.2: 0.20000000000000001110223024625156540423631668090820
0.1+0.2: 0.30000000000000004440892098500626161694526672363281
0.3: 0.29999999999999998889776975374843459576368331909180

d1: 0.10000000000000000555111512312578270211815834045410
d2: 0.20000000000000001110223024625156540423631668090820
d1+d2: 0.30000000000000004440892098500626161694526672363281
d3: 0.29999999999999998889776975374843459576368331909180

一致性 :是一件好事!

对于IEEE 754单精度格式(C float),最接近0.1、0.2和0.3的表示均略高于确切的小数值。只要我们谨慎使用常数上的f后缀使它们键入float而不是double,我们仍然会获得一致的结果使用常数和表达式使用这些常数分配的变量的表达式,如下所示:

#include <stdio.h>

int main(void) {
    float f1 = 0.1f;
    float f2 = 0.2f;
    float f3 = 0.3f;

    printf("0.1f: %.50f\n", 0.1f);
    printf("0.2f: %.50f\n", 0.2f);
    printf("0.1f+0.2f: %.50f\n", 0.1f + 0.2f);
    printf("0.3f: %.50f\n", 0.3f);
    printf("\n");
    printf("f1: %.50f\n", f1);
    printf("f2: %.50f\n", f2);
    printf("f1+f2: %.50f\n", f1 + f2);
    printf("f3: %.50f\n", f3);
    return 0;
}

输出:

0.1f: 0.10000000149011611938476562500000000000000000000000
0.2f: 0.20000000298023223876953125000000000000000000000000
0.1f+0.2f: 0.30000001192092895507812500000000000000000000000000
0.3f: 0.30000001192092895507812500000000000000000000000000

f1: 0.10000000149011611938476562500000000000000000000000
f2: 0.20000000298023223876953125000000000000000000000000
f1+f2: 0.30000001192092895507812500000000000000000000000000
f3: 0.30000001192092895507812500000000000000000000000000

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 constant 0.3 before converting to the binary representation. It should convert 0.1 and 0.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):

#include <stdio.h>

int main(void) {
    double d1 = 0.1;
    double d2 = 0.2;
    double d3 = 0.3;

    printf("0.1: %.50f\n", 0.1);
    printf("0.2: %.50f\n", 0.2);
    printf("0.1+0.2: %.50f\n", 0.1 + 0.2);
    printf("0.3: %.50f\n", 0.3);
    printf("\n");
    printf("d1: %.50f\n", d1);
    printf("d2: %.50f\n", d2);
    printf("d1+d2: %.50f\n", d1 + d2);
    printf("d3: %.50f\n", d3);
    return 0;
}

Output:

0.1: 0.10000000000000000555111512312578270211815834045410
0.2: 0.20000000000000001110223024625156540423631668090820
0.1+0.2: 0.30000000000000004440892098500626161694526672363281
0.3: 0.29999999999999998889776975374843459576368331909180

d1: 0.10000000000000000555111512312578270211815834045410
d2: 0.20000000000000001110223024625156540423631668090820
d1+d2: 0.30000000000000004440892098500626161694526672363281
d3: 0.29999999999999998889776975374843459576368331909180

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 the f suffix on the constants to make them type float instead of double, we still get consistent results for expressions using constants and expressions using variables assigned with those constants, as shown below:

#include <stdio.h>

int main(void) {
    float f1 = 0.1f;
    float f2 = 0.2f;
    float f3 = 0.3f;

    printf("0.1f: %.50f\n", 0.1f);
    printf("0.2f: %.50f\n", 0.2f);
    printf("0.1f+0.2f: %.50f\n", 0.1f + 0.2f);
    printf("0.3f: %.50f\n", 0.3f);
    printf("\n");
    printf("f1: %.50f\n", f1);
    printf("f2: %.50f\n", f2);
    printf("f1+f2: %.50f\n", f1 + f2);
    printf("f3: %.50f\n", f3);
    return 0;
}

Output:

0.1f: 0.10000000149011611938476562500000000000000000000000
0.2f: 0.20000000298023223876953125000000000000000000000000
0.1f+0.2f: 0.30000001192092895507812500000000000000000000000000
0.3f: 0.30000001192092895507812500000000000000000000000000

f1: 0.10000000149011611938476562500000000000000000000000
f2: 0.20000000298023223876953125000000000000000000000000
f1+f2: 0.30000001192092895507812500000000000000000000000000
f3: 0.30000001192092895507812500000000000000000000000000
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文