如果浮点数范围更大,那么通过浮点数进行往返转换是否总是有定义行为?

8
假设我有两个算术类型,一个整数类型 I 和一个浮点类型 F。我还假设 std::numeric_limits::max() 小于 std::numeric_limits::max()。
现在,假设我有一个正整数值 i。由于 F 的可表示范围大于 I,F(i) 应该总是有定义的行为。
然而,如果我有一个浮点值 f,使得 f == F(i),那么 I(f) 是否被良好定义?换句话说,I(F(i)) 是否总是有定义的行为?
相关部分摘自 C++14 标准:
4.9 浮点-整数转换 [conv.fpint] 1. 浮点类型的 prvalue 可以转换为整数类型的 prvalue。转换将截断;也就是说,小数部分被丢弃。如果截断后的值无法在目标类型中表示,则行为未定义。[注:如果目标类型是 bool,请参见 4.12。--end note] 2. 整数类型或未作用域枚举类型的 prvalue 可以转换为浮点类型的 prvalue。如果可能,结果是精确的。如果要转换的值在可以表示的值范围内,但不能准确地表示该值,则实现可以选择下一个更低或更高的可表示值。[注:如果整数值不能准确地表示为浮点类型的值,则会发生精度损失。--end note]如果要转换的值超出了可以表示的值范围,则行为未定义。如果源类型是 bool,则将值 false 转换为零,将值 true 转换为一。

2
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - T.C.
2
假设 IF 具有相同的大小,比如32位。然后取最大的整数。它必然会转换成类型为 F 的值,但会有损失。如果所选的可表示值大于下一个整数,则转换回来会导致未定义行为。 - Kerrek SB
@orlp - Steve的资源几乎不相关。主流计算机使用32位和64位二进制补码算术和32位和64位IEEE浮点数。假设C++标准委员会写了一个在这些机器上无法实现的要求。那些委员会成员将被迅速替换为更明智的代表(并且标准将被重新编写)。 - David Hammen
可能在答案中没有提到,所以我要补充一下:它不取决于浮点类型的范围,而是精度。IEEE float 的范围与整数的范围相比巨大,但它只有23位的尾数,这意味着在指数高于23时,它将无法表示所有整数(甚至近似)。第一个这样的值将是 2^24 + 1,它位于 (1 + 0/2^23) * 2^24(1 + 1/2^23) * 2^24 之间。double 可以正确舍入到任何32位整数(但不能舍入到任何64位整数)。 - Wug
@orlp 这是与您问题相关的重要信息。我以“评论”的形式发布它是有原因的。我相信在这种情况下,有人可能会发现它很有用。 - Steve
显示剩余5条评论
3个回答

4
然而,如果我有一个浮点值f使得f == F(i),那么I(f)是否定义良好?换句话说,I(F(i))的行为是否总是定义良好的呢?不是。假设I是带符号的二进制补码32位整数类型,F是32位单精度浮点类型,并且i是最大正整数。这在浮点类型范围内,但它不能被表示为浮点数。其中一些32位用于指数。相反,从整数到浮点数的转换取决于实现,但通常是通过四舍五入到最接近的可表示值来完成的。该四舍五入后的值超出了整数类型的范围。将其转换回整数会失败(更好地说,这是未定义的行为)。

小小的挑剔。 “相反,从整数到浮点数的转换四舍五入到最接近的可表示值。”这并不保证。它将是相邻的表示,而不是最接近的一个。 - orlp
从技术上讲,这不是未定义行为,而是实现定义行为(因为它取决于使用的浮点类型的语义,这些语义是实现定义的)。 - Wug
@Wug 这取决于实现,它可能是未定义的,也可能不是 :) - orlp
1
@orlp - 编辑。从整数到浮点数的转换是实现相关的(也与应用程序相关;应用程序可以使用IEEE浮点数控制舍入)。如果该实现相关的转换使得浮点值超出了整数类型的范围,则将其转换回整数的行为是未定义的。 - David Hammen
@DavidHammen 我非常清楚,看看我的答案。我只是吹毛求疵 :) - orlp
显示剩余2条评论

-1

不管标准如何,你不能指望这种转换通常会返回原始整数。从数学上讲,这是没有意义的。但是如果你仔细阅读你引用的内容,标准清楚地指出了从int到float转换可能会丢失精度。

假设你的类型I和F使用相同数量的位。 I的所有位(除了可能存储符号的一位)都用于指定数字的绝对值。另一方面,在F中,一些位用于指定指数,一些位用于指定有效数字。由于可能的指数,范围将更大。但是有效数字的精度将更低,因为有较少的位用于其规格说明。

只是作为一个测试,我打印了

std::numeric_limits<int>::max();
std::numeric_limits<float>::max();

我将第一个数字转换为浮点数,然后再转回去。最大的浮点数指数为38,而最大的整数有10位数字,因此浮点数的范围更大。但是,当我将最大整数转换为浮点数并再次转换时,我从2147473647变成了-2147473648。因此,这个数字增加了一个单位并绕到了负面。

我没有检查我的系统实际上使用了多少位浮点数,但它至少证明了精度的损失,并显示了gcc的“四舍五入”。


我从未要求原始整数值。我只是问转换是否定义良好。 - orlp

-1

不。

可能会出现i == std::numeric_limits<I>::max()的情况,但是iF中不能完全表示。

如果要转换的值在可表示的值范围内,但无法准确表示该值,则可以选择下一个较低或较高的可表示值,这是实现定义的选择。

由于可能选择下一个较高的可表示值,所以结果F(i)可能不再适合I,因此进行反向转换将导致未定义行为。


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