当前位置: 首页 > 知识库问答 >
问题:

是否允许此浮点优化?

南门飞
2023-03-14

我试图检查浮动在哪里失去了准确表示大整数的能力。所以我写了这个小片段:

int main() {
    for (int i=0; ; i++) {
        if ((float)i!=i) {
            return i;
        }
    }
}

这段代码似乎适用于所有编译器,除了clang。Clang生成一个简单的无限循环。戈德博尔特。

这是允许的吗?如果是,这是QoI问题吗?

共有2个答案

王经赋
2023-03-14

正如@Angew所指出的,代码= 运算符两侧需要相同的类型<代码>(浮点)i!=i也将RHS提升为浮动,因此我们有了(浮动)i!=(浮动)i。

g也会生成一个无限循环,但它不会优化内部的工作。您可以看到它转换为-

<代码>x!=x只有在“无序”时才为真,因为x是NaN。(无穷大在IEEE数学中与自身相等,但NaN不相等。NaN==NaN为假,NaN!=NaN为真)。

gcc7。4和更早版本正确地将代码优化为jnp作为循环分支(https://godbolt.org/z/fyOhW1):只要操作数到<代码>x!=x不是NaN。(gcc8和更高版本也会检查je是否中断循环,未能根据任何非NaN输入始终为真的事实进行优化)。x86 FP比较无序上的集合PF。

顺便说一句,这意味着clang的优化也是安全的:它只需要CSE(float)i!=(隐式转换为浮点)i相同,并证明-

(虽然假定此循环将命中有符号溢出UB,但它可以发出它想要的任何asm,包括非法指令,或空无限循环,而不管循环体实际是什么。)但是忽略签名溢出UB,这种优化仍然是100%合法的。

即使使用-fwrapv使有符号整数溢出定义明确(作为2的补码环绕),GCC也无法优化掉循环体。https://godbolt.org/z/t9A8t_

即使启用“无陷阱数学”也无济于事。(不幸的是,GCC的默认设置是启用数学训练,尽管GCC的实现已中断/有缺陷。)内景-

但是,使用O3-fwrapv-fno陷阱数学,如果不将其编译为空的无限循环,就完全错过了优化。如果没有上的pragma STDC FENV\u访问,记录屏蔽FP异常的粘性标志的状态不是代码的明显副作用。否int-

这些编译器都在为使用IEEE 754单精度(binary32)浮点和32位int的C实现进行优化。

bug修复的(int)(flow)i!=i循环将在具有窄16位int和/或更宽的C实现上具有UB浮点,因为您会在到达第一个不能完全表示为浮点的整数之前触发有符号整数溢出UB。

但是,在使用x86-64 System V ABI为gcc或clang等实现编译时,使用一组不同的实现定义的选择下的UB不会产生任何负面后果。

顺便说一句,您可以从FLT_RADIXFLT_MANT_DIG静态计算此循环的结果,在中定义

我不确定ISO C标准在多大程度上明确了浮动行为,也不确定不基于固定宽度指数和有效位字段的格式是否符合标准。

在评论中:

@geza我很想听听结果!

@nada:是16777216

您是否声称获得了打印/返回16777216的循环?

更新:由于该评论已被删除,我认为不会。OP可能只是引用了第一个整数之前的浮点值,而这个整数不能精确地表示为32位浮点值。https://en.wikipedia.org/wiki/Single-precision_floating-point_format#Precision_limits_on_integer_values也就是说,他们希望用这个错误代码验证什么。

修复错误的版本当然会打印出第一个不可精确表示的整数,而不是之前的值。

(所有较高的浮点值都是精确整数,但它们是2的倍数,然后是4,然后是8,等等。对于高于显式宽度的指数值。可以表示许多较高的整数值,但(显式)最后一位的1个单位大于1,因此它们不是连续整数。最大的有限浮点略低于2^128,这对于int64_t来说太大了。)

如果任何编译器确实退出了原始循环并打印了它,那将是一个编译器错误。

邵伟
2023-03-14

请注意,内置运算符= 要求其操作数的类型相同,并在必要时使用提升和转换来实现。换句话说,您的条件相当于:

(float)i != (float)i

这永远不会失败,因此代码最终会溢出i,使您的程序具有未定义的行为。因此,任何行为都是可能的。

要正确检查要检查的内容,应将结果转换回int:

if ((int)(float)i != i)
 类似资料:
  • 有问题的代码如下: 编译器会优化它吗?根据C-Standard(如果我理解正确的话),第二个操作数必须提升为;因此乘法必须使用FPU(或fp仿真)完成。 从理论上讲,该操作可以在正常的硬件寄存器中完成,只需添加一个即时的(并且可能是溢出检查)。是否允许编译器执行此优化?是否有已知的编译器这样做?如果是这样,他们是否也会识别该表达式 这是避免有关隐式转换的静态代码检查器警告所必需的? 补充一下:我知

  • 问题内容: 我可以定义setter方法以返回此方法而不是void吗? 喜欢: 然后我可以使用新的ClassA()。setItem1()。setItem2() 问题答案: 关于JavaBeans规范有很多误解。 它存在的主要原因是统一的Java“组件”模型。这是一种使用反射与Java对象进行编程交互的方式。该API本身名为JavaBeans Introspection 。请看一下示例用法,您将比普通

  • 问题内容: 希望能帮助您理解“ Java并发实践”中的以下内容: 从构造函数中调用可重写的实例方法(既不是私有方法也不是final方法)也可以使this引用转义。 这里的“转义”是否仅表示在实例完全构建之前,我们可能正在调用实例方法? 我看不到“ this”以任何其他方式逃避了实例的范围。 ‘最终’如何防止这种情况的发生?我缺少实例创建中的’最终’某些方面吗? 问题答案: 这意味着在类之外调用代码

  • 问题内容: 我拼凑了一个图片网站。基本模式是非常简单的MySQL,但是在尝试表示与图像相关联的可能的管理标志(“不合适”,“受版权保护”等)时遇到了一些麻烦。我目前的概念如下: (为了方便阅读而被截断;我发誓要搭配各种外键和索引) 在标志类型的查找表上是外键,并且您可以想象 应该 在上外键。现在的问题是,当第一次发出标志时,没有逻辑解析类型(我将其声明为的一种很好的用法)。但是,如果设置了值,则应

  • 问题内容: URI(特别是HTTP URL)是否允许包含一个或多个空格字符?如果 必须 对URL 进行编码,这是通常遵循的约定还是合法的选择? 特别是,有人可以指向RFC指出 必须 对带有空格的URL 进行编码吗? 提出问题的动机: 在对网站进行Beta测试时,我注意到某些URL的构造带有空格。Firefox似乎做对了,这让我感到惊讶!但是我希望能够将开发人员指向RFC,以便他们觉得有必要修复这些

  • 问题内容: MySQL是否允许使用嵌套事务? 问题答案: 支持。 您可以执行以下操作: