【问题标题】:strange C integer inequality comparison result奇怪的C整数不等式比较结果
【发布时间】:2012-06-20 22:57:02
【问题描述】:
#include <limits.h>
#include <stdio.h>
int main() {
    long ival = 0;
    printf("ival: %li, min: %i, max: %i, too big: %i, too small: %i\n",
           ival, INT_MIN, INT_MAX, ival > INT_MAX, ival < INT_MIN);
}

这给出了输出:

ival: 0, min: -2147483648, max: 2147483647, too big: 0, too small: 1

这怎么可能?

(实际上,我在getargs.c:convertsimple 中遇到了 CPython 2.7.3 中的这个问题/错误。如果您在case 'i' 中查找代码,则检查ival &lt; INT_MIN 始终正确对我来说。另见test case source with further references。)


好吧,我现在测试了几个不同的编译器。为 x86 编译的 GCC/Clang 都返回预期值(太小:0)。为 armv7 编译时,意外输出来自 Xcode 工具链中的 Clang。


如果你想重现:

这是确切的编译命令:/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -arch armv7 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.1.sdk test-int.c

这是 Xcode 4.3.2。

我将生成的 a.out 复制到我的 iPhone 上并执行它。

如果有人对此生成的汇编代码感兴趣:

    .section    __TEXT,__text,regular,pure_instructions
    .section    __TEXT,__textcoal_nt,coalesced,pure_instructions
    .section    __TEXT,__const_coal,coalesced
    .section    __TEXT,__picsymbolstub4,symbol_stubs,none,16
    .section    __TEXT,__StaticInit,regular,pure_instructions
    .syntax unified
    .section    __TEXT,__text,regular,pure_instructions
    .globl  _main
    .align  2
    .code   16
    .thumb_func _main
_main:
    push    {r7, lr}
    mov r7, sp
    sub sp, #20
    movw    r0, #65535
    movt    r0, #32767
    movs    r1, #0
    movt    r1, #0
    str r1, [sp, #16]
    str r1, [sp, #12]
    ldr r1, [sp, #12]
    ldr r2, [sp, #12]
    cmp r2, r0
    movw    r0, #0
    it  gt
    movgt   r0, #1
    and r0, r0, #1
    ldr r2, [sp, #12]
    cmn.w   r2, #-2147483648
    movw    r2, #0
    it  lt
    movlt   r2, #1
    and r2, r2, #1
    mov r3, sp
    str r2, [r3, #4]
    str r0, [r3]
    mov.w   r2, #-2147483648
    mvn r3, #-2147483648
    movw    r0, :lower16:(L_.str-(LPC0_0+4))
    movt    r0, :upper16:(L_.str-(LPC0_0+4))
LPC0_0:
    add r0, pc
    blx _printf
    ldr r1, [sp, #16]
    str r0, [sp, #8]
    mov r0, r1
    add sp, #20
    pop {r7, pc}

    .section    __TEXT,__cstring,cstring_literals
L_.str:
    .asciz   "ival: %li, min: %i, max: %i, too big: %i, too small: %i\n"


.subsections_via_symbols

【问题讨论】:

  • 可能是选角怪癖?有可能将 INT_MIN 转换为 long 并且没有正确处理标志?或相反亦然? O.o
  • @Albert 很有趣,我得到了ival: 0, min: -2147483648, max: 2147483647, too big: 0, too small: 0
  • 我认为 printf 没有 %i 格式。你可能想要 %d。将 printf 等 varargs 函数的参数显式转换为 int 是一个好习惯。 (在这种情况下不需要,因为 (a>b) 的值默认为 int 类型)
  • @wildplasser:是的,printf 有一个%i 格式。与%d相同。
  • 糟糕。我的错。我认为这是以前扩展的遗留物。

标签: c arm


【解决方案1】:

这是一个错误。 C 标准中没有空间让too small 成为 0 以外的任何值。它的工作原理如下:

  1. 由于INT_MINint,它会在“通常的算术转换”期间转换为long。发生这种情况是因为long 的排名高于int(两者都是有符号类型)。不会发生任何提升,因为所有操作数都至少具有 int 等级。不会调用未定义或实现指定的行为。

  2. 在转换过程中,INT_MIN 的值被保留。由于正在从int 转换为long,并且保证long 至少具有int 的范围,所以在转换过程中必须保留INT_MIN 的值。不调用未定义或实现指定的行为。不允许进行模块化转换,仅适用于无符号类型。

  3. 比较的结果应该是0

标志扩展或其他此类事情没有回旋余地。此外,由于对printf 的调用是正确的,因此没有问题。

如果您可以在另一个系统上重现它,或者将它发送给可以重现它的其他人,您应该直接将错误报告给您的工具链供应商。

尝试重现错误:我无法重现以下任何组合的行为,无论是开启还是关闭优化:

  • GCC 4.0,PPC + PPC64
  • GCC 4.2,PPC + PPC64
  • GCC 4.3,x64
  • GCC 4.4,x64
  • Clang 3.0,x64

【讨论】:

  • 我也只能在带有 -O0 的 Arch armv7 的 Xcode (4.3.2) 工具链中使用 Clang 重现它。通过优化,我得到了预期的结果。看起来像一个真正的错误。我举报了。
【解决方案2】:

这会打印什么?

#include <limits.h>

printf("%016ld\n", LONG_MAX);

long l_int_min = (long)INT_MIN;
printf("%016lx\n", l_int_min);

我想知道INT_MIN 是否在没有符号扩展的情况下被强制转换为long。这会使 0 小于结果值。

编辑:好的,第一个printf() 的结果是0000002147483647,这意味着long 在该平台上是32 位的,就像int。因此,将 int 转换为 long 实际上不会改变任何东西。

我尝试保留“这是一个编译器错误”作为最后的手段,但这对我来说似乎是一个编译器错误。

【讨论】:

  • 根据 C 标准,在不保留符号的情况下将 int 转换为 long 是错误的。没有兼容的 C 实现可以做这样的事情。
  • 00000021474836470000000080000000
  • %16lx 格式需要一个无符号长整型参数。 l_int_min 作为有符号长整数传递。它的值是使用 INT_MIN 初始化的,它在使用前进行了符号扩展。
  • 是的,%16lx 格式需要一个无符号长整数。但是因为 printf() 不是真正的类型安全,如果你传递一个有符号的 long 并以这种格式打印它,我希望它会打印一个合理的结果。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-23
相关资源
最近更新 更多