简体   繁体   中英

C++: Strange math results

The following calculation results in "1" for me.

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

Does anybody see why? I thought that unsigned long could handle this.

Thank you very much for the help.

The values on the right of your calculation have type unsigned int or int .

4294966336 + 1920 = 4294968256

assuming sizeof(unsigned int) == 4 , this overflows, leaving you with 960

(960-1)/1920 = 0

(due to rounding in integer arithmetic). This leaves you with

0 + 1 = 1

If you want to perform the calculation using a larger type (and assuming that sizeof(unsigned long) > sizeof(unsigned int) ), you need a cast inside the calculation

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

or, as noted by Slava, set one of the literal values to have unsigned long type using the UL suffix

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

"I thought that unsigned long could handle this." - I'm not sure what you mean by this. There's nothing in the expression that you evaluate that would suggest that it should use unsigned long . The fact that you eventually initialize an unsigned long variable with it has no effect on the process of evaluation. The initializing expression is handled completely independently from that.

Now, in C++ language an unsuffixed decimal integral literal always has signed type. The compiler is not allowed to choose unsigned types for any of the literals used in your expression. The compiler is required to use either int , long int or long long int (the last one - in C++11), depending on the value of the literal. The compiler is allowed to use implementation-dependent extended integral types, but only if they are signed . And if all signed types are too small, the behavior of your program is undefined.

If we assume that we are working with typical real-life platforms with integer types having the "traditional" 16, 32 or 64 bits in width, then there are only two possibilities here

  • If on your platform all signed integer types are too small to represent 4294966336 , then your program has undefined behavior . End of story.

  • If at least one signed integer type is large enough for 4294966336 , there should be no evaluation overflow and your expression should evaluate to 2236963 .

So, the only real language-level explanation for that 1 result is that you are running into undefined behavior, because all signed types are too small to represent the literals you used in your expression.

If on your platform some signed integer type is actually sufficiently large to represent 4294966336 (ie some type has at least 64 bits), then the result can only be explained by the fact the your compiler is broken.

PS Note that the possibility of unsigned int being used for the evaluation only existed in old C language - C89/90. That would produce the third explanation for the result you obtained. But, again, that explanation would only apply to C89/90 compilers, not to C++ compilers or C99 compilers. And your question is tagged [C++].

I meant to leave this as a comment, but do not have enough reputation. I wanted to share this anyway, because I think that there are two components to tmighty's question and one of them might not be answered quite correctly, from what I understand.

  • First, you need to be explicit about the data type you are using, ie use a suffix such as UL or explicit type conversion. Other answers have made this clear enough, I think.
  • Second, you need to then pick a data type that is large enough.

You have already stated "I thought that unsigned long could handle this" and other answers seem to confirm this, but from what I know - and double-checking just now to try to make sure - unsigned long may not be large enough. It is guaranteed to be at least 4294967295 , which would be too small for your use case. Specifically, I have just checked under Windows using VC++ and both compilation as 32 bit and 64 bit define ULONG_MAX to be 4294967295 .

What you may need to do instead is use the "ULL" suffix and use unsigned long long , since its maximum seems to be at least 18446744073709551615 . Even if there are platforms that define unsigned long to be large enough for your purpose, I think you might want to use a data type that is actually guaranteed to be large enough.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM