C ++:奇怪的数学结果

时间:2013-10-15 21:06:25

标签: c++ math

以下计算结果为“1”。

unsigned long iEndFrame = ((4294966336 + 1920 - 1) / 1920) + 1;

有人知道为什么吗?我认为unsigned long可以解决这个问题。

非常感谢您的帮助。

3 个答案:

答案 0 :(得分:14)

计算右侧的值包含unsigned intint

4294966336 + 1920 = 4294968256

假设sizeof(unsigned int) == 4,这会溢出,只留下960

(960-1)/1920 = 0

(由于整数运算中的舍入)。这将离开你

0 + 1 = 1

如果您想使用更大的类型执行计算(假设sizeof(unsigned long) > sizeof(unsigned int)),则需要在计算中进行强制转换

unsigned long iEndFrame = (((unsigned long)4294966336 + 1920 - 1) / 1920) + 1;

或者,如Slava所述,使用unsigned long后缀设置其中一个文字值为UL类型

unsigned long iEndFrame = ((4294966336UL + 1920 - 1) / 1920) + 1;

答案 1 :(得分:3)

“我认为unsigned long可以解决这个问题。” - 我不确定你的意思。您评估的表达式中没有任何内容表明它应该使用unsigned long。您最终使用它初始化unsigned long变量的事实对评估过程没有影响。初始化表达式完全独立于此处理。

现在,在C ++语言中,未加十进制的十进制整数文字总是具有 signed 类型。不允许编译器为表达式中使用的任何文字选择无符号类型。编译器需要使用intlong intlong long int(最后一个 - 在C ++ 11中),具体取决于文字的值。允许编译器使用依赖于实现的扩展整数类型,但仅限于签名。如果所有签名类型都太小,程序的行为是不确定的。

如果我们假设我们正在使用典型的现实生活平台,其整数类型的宽度为“传统的”16,32或64位,那么这里只有两种可能性

  • 如果您的平台上所有签名的整数类型都太小而无法代表4294966336,那么您的程序会有未定义的行为。故事结束。

  • 如果至少有一个有符号整数类型足够4294966336,则应该没有评估溢出,并且您的表达式应该评估为2236963

因此,对1结果唯一真正的语言级解释是您遇到了未定义的行为,因为所有签名类型都太小而无法表示您在表达式中使用的文字。

如果在您的平台上,某些有符号整数类型实际上足够大以表示4294966336(即某些类型至少有64位),那么结果只能通过编译器被破坏的事实来解释。 / p>

P.S。请注意,unsigned int用于评估的可能性仅存在于旧C语言 - C89 / 90中。这将为您获得的结果产生第三种解释。但是,同样,这种解释只适用于C89 / 90编译器,而不适用于C ++编译器或C99编译器。你的问题被标记为[C ++]。

答案 2 :(得分:1)

我的意思是将此作为评论,但没有足够的声誉。无论如何,我想分享这个,因为我认为有两个组成部分可以解决问题,其中一个可能没有得到正确解答,我理解的是。

  • 首先,您需要显式关于您正在使用的数据类型,即使用UL等后缀或显式类型转换。我认为其他答案已经足够清楚了。
  • 其次,您需要选择足够大的数据类型。

你已经说过“我认为unsigned long可以解决这个问题”,其他答案似乎也证实了这一点,但据我所知 - 并且现在仔细检查以确保 - unsigned long可能不会足够大。保证至少为4294967295,这对于您的用例来说太小了。具体来说,我刚刚在Windows下使用VC ++进行了检查,并且编译为32位和64位,将ULONG_MAX定义为4294967295

您可能需要做的是使用“ULL”后缀并使用unsigned long long,因为它的最大值似乎至少为18446744073709551615。即使存在将unsigned long定义为足够大的平台,我认为您可能希望使用实际上保证足够大的数据类型。