乘以1.0的精度和整型转浮点型的转换

46

可以假设条件 (int)(i * 1.0f) == i 对于任何整数 i 都是成立的吗?


我认为是这样,因为任何尾数位都将丢失。但我不知道int是否会被提升为float - Tony The Lion
9
由于某种原因,我想说“不”,但我实在没有理由...... :-] - user131441
@John:我有同样的感觉。我也在考虑大整数,以及乘以1.0是否有任何保证... 可能在IEEE754中答案是“是”。 - Kerrek SB
@John:那也是我的最初感觉,现在我只是不知道了 :) - Violet Giraffe
4个回答

73

不。

如果i足够大,以至于int(float(i)) != i(假设float是IEEE-754单精度浮点数,i = 0x1000001就足以展示这一点),那么这是错误的,因为乘以1.0f会强制转换成float,虽然随后的乘法并未改变值,但结果发生了变化。

然而,如果i是32位整数且double是IEEE-754双精度浮点数,则int(i*1.0) == i是正确的。


仅仅为了更加清楚,乘以1.0f是精确的。可能不准确的是从intfloat的转换。


6
补充一下这个回答:简单来说,精度的损失是由于float类型只有24位可以用来存储int类型,而int类型有32位,因此在转换时最低有效位将被四舍五入。 - Ifthikhan
同意,但实际上这不是丢弃吗? :) - Ifthikhan
2
@anatolyg:不是这样的。在C++中,这由第5节的第10段(通常的算术转换)控制:“如果任一操作数为float,则另一个操作数应转换为float。”因此,在乘法之前,i被转换为float,这会导致四舍五入。 - Stephen Canon
2
@MichaelShopsin:除非ab是NaN,否则a <= ba > b中只有一个为真。 - Eric Postpischil
1
@MichaelShopsin:你们两个都是对的。Eric的陈述绝对适用于标准定义的浮点数。然而,已经有充分记录的编译器错误导致了Michael所描述的行为。但需要明确的是,这不是浮点比较的问题,而是编译器错误的问题。 - Stephen Canon
显示剩余5条评论

15
不,IEEE-754浮点数在相同的位宽下牺牲整数精度以换取更大的动态范围。例如,可以看到以下代码片段的输出结果:
int main() {
        int x = 43046721;

        float y = x;

        printf("%d\n", x);
        printf("%f\n", y);
}

43046721 在32位 float 数字中只有24位精度,因此无法正确表示,输出结果大致如下:

43046721
43046720.000000

事实上,当转换为32位浮点数时,我预计任何大于16,777,216的奇数都会出现相同的问题。
一些值得注意的点:
- 这与隐式的int-to-float转换有关,而不是乘法本身。 - 这并不是C语言所特有的 - 例如Java也会遇到完全相同的问题。 - 大多数编译器都有优化选项,可以影响这种转换的处理方式,通过忽略标准的某些限制。在这种情况下,如果编译器优化掉了对float和back的转换,则(int)((float)x * 1.0f) == x 可能总是true。

1
不,这种行为是实现定义的,因为C和C ++不要求IEEE-754,尽管这是迄今为止最常见的表示法。
要确保使用IEEE-754:
在C中,使用#ifdef __STDC_IEC_559__
在C++中,使用std::numeric_limits::is_iec559常量

好的,谢谢!您知道有哪些现代(或目前正在使用)的实现不符合IEEE-754标准吗? - Violet Giraffe

-5

不,这对于所有整数来说都是绝对错误的,因为进行了类型转换。请检查代码。

#include <stdio.h>

int main()
{
    int i = 0;
    for (; i < 2147483647; ++i) {
        if ((int)(i * 1.0f) != i) {
            printf("not equal\n");
            break;
        }
    }
    printf("out of the loop\n");
    getchar();
    return 0;
}

这段代码假设您使用32位整数


1
32位整数的INT_MAX不等于32767。 - Stephen Canon
6
你运行了这个程序,对吧?因为在我的系统里它实际上会打印出“not equal”,这正是它应该做的... - thkala
2
@thkala 允许浮点运算的精度和范围超出类型指定的限制。因此,编译器可以使用double甚至是long double或扩展的80位x87浮点数,这种情况下,“不相等”不被打印是合法的。另一个(不太可能的)可能性是float已经具有至少31位的精度。但是,对于标准IEEE754 32位float,如果将测试更改为if ((int)((float)i * 1.0f) != i),则必须打印“不相等”。 - Daniel Fischer
但是根据我的编译器,它是正确的。请帮忙 :-( - Mohit Sehgal
@MohitSehgal,你的编辑仍然是错误的。比较不是“*因为类型转换而对所有整数都是绝对[false]*”。比较只对需要比浮点数更高精度的整数是错误的。32位IEEE-754浮点数具有24位尾数。在0x01000000和0xFFFFFFFF之间的所有无符号32位整数将使比较为假。其他所有整数将使其为真。 - kdbanman
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接