C浮点字面量翻译

6
我们有两个嵌入式项目:一个使用cosmic编译器,另一个使用GCC。两者都遵守ISO/IEC 9899:1990规定。
当我们使用文字“14.8f”初始化一个float变量时,在cosmic编译器下它会被翻译成二进制的0x416CCCCC,而在GCC下会被翻译成0x416CCCCD。
IEC标准第6.3.1.4章节中,浮点类型第2项说明如下:
如果要转换的值在可以表示但不能准确表示的值范围内,则结果可能是以实现定义的方式选择的最近更高或最近更低的值。
由于我们将这些数字用作阈值,因此这显然会产生差异。
cosmic编译器声明使用向下取整的实现方式。由于GCC更为复杂,我想知道它是否有一个编译器标志,可以在编译时选择行为。到目前为止,我只发现您可以选择FE_DOWNWARD,但那与运行时有关,而不是编译时。
有人知道这样一个编译时转换的标志吗?

我怀疑GCC没有这样的标志。但是,您可以使用十六进制浮点语法以一种编译器应该正确转换数字的方式来指定数字。在十六进制浮点中,14.8以下的“float”为“0x1.d99998p+3”。 - Eric Postpischil
这个问题有些相关:https://dev59.com/HlYN5IYBdhLWcg3w2rSU - Jabberwocky
4
@Lundin说:“你必须使用+/- delta与浮点数进行比较。”这是错误的。没有要求使用这种方式来比较浮点数。这种说法不实且会像谣言一样传播,因为它在简单情况下(例如学生项目)起到很差的作用。经常情况下,使用容差来比较并不是一个好的解决方案;在Stack Overflow上经常被推荐使用,却没有考虑它所造成的误报率或容差应该设定多少。经常情况下,试图比较相等性的人会误用浮点数,因此... - Eric Postpischil
1
正确的建议是重新设计他们的解决方案,而不是进行容差比较。但是这个人在进行小于或小于等于的比较,而不是等于。这是一种不同的情况。将值与阈值进行比较表明他们需要将值夹紧到某些设备或应用程序可接受的边界范围内。在这种情况下,进行比较可能是一个合适的解决方案,没有容差。 - Eric Postpischil
1
@Lundin:不,这并不完全取决于您的可移植性要求。使用容差进行比较并不能使程序具有可移植性,反而可能导致程序出错。是否使用容差进行比较取决于您的应用程序。如果您的应用程序不允许出现误报情况,那么使用容差进行比较将无法奏效,无论C实现如何。 - Eric Postpischil
显示剩余4条评论
2个回答

4
仅供参考,GCC手册的相关章节指出:

对于某些浮点常量(C90 6.1.3.1,C99和C11 6.4.4.2),如何选择最近可表示值、更大或更小的相邻可表示值。

遵循C99附录F。

在我的C99标准草案中,附录F说:

F.7.2 翻译

在翻译期间,IEC 60559默认模式有效:

— 舍入方向模式为四舍五入。
— 舍入精度模式(如果支持)被设置为结果不缩短。
— 所有浮点异常都禁用了陷阱或停止(如果支持)。

所以这似乎清楚地说明:

  • GCC使用四舍五入。
  • 您无法更改它。

在此处使用十六进制语法以获得所需的确切float似乎是正确的解决方案,也是该语法存在的原因(我猜测)。


在 C99 中,您可以使用 fesetround 函数来更改它。 - Lundin
感谢澄清,由于我们必须符合C90标准,我想十六进制表示法只能使用C99。 - user1464603
@Lundin 这只影响浮点数计算,而不是编译时字面量的翻译。 - user1464603
@user1464603 哦,是的,我想那就是你想要的。它确实只影响了 round() 的行为。 - Lundin
@user1464603 当然你可以自己编写代码,但是这样做可能不太美观(例如在运行时将0x416CCCCC进行转换)。 - unwind
@Lundin:它不仅影响 round() 的行为,还会在运行时一般影响浮点运算的行为。 - Eric Postpischil

1
如果想以一种方式编写代码,使其在C89编译器上具有匹配行为,可以将浮点常数写成可精确表示的整数除以2的幂,例如(15518925.0f/1048576.0f),这样做可以在所有常见的浮点实现中产生明确的结果,而不受舍入模式的影响。通常没有办法控制编译时常量表达式的舍入方式,但是如果使用上述形式编写这些表达式,就可以使这些问题变得无关紧要。

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