C++ unsigned long 在 4294967295 之后不会回绕

Posted

技术标签:

【中文标题】C++ unsigned long 在 4294967295 之后不会回绕【英文标题】:C++ unsigned long doesn't wrap around after 4294967295 【发布时间】:2015-06-07 17:43:25 【问题描述】:

我在玩一些 c++ 代码,发现如果我用 4294967295(它的最大允许值)开始一个 unsigned long 并添加假设 6,我必须得到 5,它确实如此!但是下面的加法运算,mod 255并没有给出正确答案。为什么会发生这种情况,如何避免?

经过测试的平台: Windows 7,Visual Studio c++:工作正常; CentOS 6.6 版(最终版),g++:给出错误的答案; 请参阅代码下方的注释:

#include <stdio.h>
int main(int argc, char *argv[])  
   unsigned long s1=4294967295;
   unsigned long vp=245;
   unsigned long a;
   unsigned long b;
   unsigned long result;

   /* s1 is 4294967295 + 6 wich is 5  */
   s1+=6;
   a=s1;
   b=255*vp*s1;
   result = (a + b) % 255;
   printf("s1=%u , a=%u , b=%u , (a+b)=%u , (a+b)%255= %u\n", s1, a, b, a + b, result);

   /* s1 is a static value 5 */
   s1=5;

   a=s1;
   b=255*vp*s1;
   result = (a + b) % 255;
   printf("s1=%u , a=%u , b=%u , (a+b)=%u , (a+b)%255= %u\n", s1, a, b, a + b, result);
   return 0;

注意: 如果我用 Visual Studio 编译代码,我会得到正确的答案。

s1=5 , a=5 , b=312375 , (a+b)=312380 , (a+b)%255= 5
s1=5 , a=5 , b=312375 , (a+b)=312380 , (a+b)%255= 5

但是,如果我使用 gcc 版本 4.4.7 20120313 (Red Hat 4.4.7-11) (GCC) 编译,我会得到:

s1=5 , a=5 , b=312375 , (a+b)=312380 , (a+b)%255= 6
s1=5 , a=5 , b=312375 , (a+b)=312380 , (a+b)%255= 5

在g++中如何避免?

【问题讨论】:

“如果我用 4294967295(他的最大允许值)开始一个无符号长整数”你从哪里读到的? 【参考方案1】:

如果我用 4294967295(他的最大允许值)开始一个无符号长整数

如果unsigned long 是32 位类型,4294967295 只是unsigned long 的最大值。在 Windows 上就是这种情况,包括 32 位和 64 位。在 Linux 上,unsigned long 在 32 位平台上是 32 位类型,但在 64 位平台上是 64 位类型。 C++ 标准没有规定类型的特定大小,它只是说unsigned long 必须至少为 32 位,因此 Windows 和 Linux 都是正确的。

如果您想要有保证的 32 位类型,请使用来自 &lt;cstdint&gt;uint32_t(如果有)。它是该语言的最新添加,我不知道您的 Visual Studio 版本是否有它;如果没有,请参阅'uint32_t' identifier not found error。

另外,请注意您用于打印值的代码中存在错误。它碰巧不会在您尝试的两个平台上崩溃,但会截断 GCC 中的结果。要打印出unsigned long,您需要使用%lu 说明符,而不是%u。要打印出百分比字符,您需要将% 加倍。

printf("s1=%lu , a=%lu , b=%lu , (a+b)=%lu , (a+b)%%255= %lu\n", s1, a, b, a + b, result);

或者,由于您使用的是 C++,因此请使用其自己的打印机制。

#include <iostream>
…
    std::cout << "s1=" << s1
              << ", a=" << a
              << ", b=" << b
              << ", (a+b)=" << (a+b)
              << ", (a+b)%255=" << result;

unsigned long是64位类型的平台上,s1的第一次循环值为4294967301。b的值为268328082129975。如果你使用正确的你会看到这些值打印说明符。

【讨论】:

uint32 可以找到 #include 有 uint32_t ,这不是新增内容。所以优于 @user4983898 自 C99 以来一直使用 C,但 Windows 在实现 C99 方面已经远远落后。它最近才在 C++ 中正式发布。【参考方案2】:

您使用的是 64 位系统吗?因为那时long 在 GCC 中通常是 64 位,而在 Visual Studio 编译器中仍然是 32 位,这将解释为什么它不适用于 GCC 但适用于 VS。

要获取某个类型的最大值,请使用`std::numeric_limits 获取,例如

unsigned long s1 = std::numeric_limits<unsigned long>::max();

【讨论】:

它打印 5 和 6 而不是一个大数字的事实可能是因为他们在printf() 中使用%u 而不是%lu 操作!。我知道了。我将类型更改为无符号整数,即 32 位。现在工作正常。一些编译器指令,谢谢。

以上是关于C++ unsigned long 在 4294967295 之后不会回绕的主要内容,如果未能解决你的问题,请参考以下文章

带有 unsigned long long 和 sprintf 的 Visual C++ 6.0

JAVA 与 C++ 数据类型转换 byte unsigned char

C++ 指针(不论什么一个指针本身的类型都是unsigned long int型)

将 unsigned long long 转换为 wchar_t * 并连接

无法将 'vector<unsigned long>' 转换为 Python 对象

C++数据类型范围