5

For a clever and complicated reason that I don't really want to explain (because it involves making a timer in an extremely ugly and hacky way), I wrote some C# code sort of like this:

int i = 0;
while (i >= 0) i++; //Should increment forever
Console.Write(i);

I expected the program to hang forever or crash or something, but, to my surprise, after waiting for about 20 seconds or so, I get this ouput:

-2147483648

Well, programming has taught me many things, but I still cannot grasp why continually incrementing a number causes it to eventually be negative...what's going on here?

Solomon Ucko
  • 5,724
  • 3
  • 24
  • 45
Peter Olson
  • 139,199
  • 49
  • 202
  • 242
  • 6
    you aren't implementing a timer, you're implementing a busy loop which is rather naff – David Heffernan May 31 '11 at 22:10
  • 4
    I'll have to vehemently doubt your _clever and complicated_ reasons. It seems that there is nothing clever about incrementing an integer indefinitely and not even considering that your computer may have limitations; not even considering how a computer actually works before you program it to do a simple task like... counting to infinity (meh, just only infinity, nothing out of the ordinary) – sehe May 31 '11 at 22:19
  • 5
    +1 just to counter the stupid downvote. – Tim Jarvis May 31 '11 at 22:30
  • possible duplicate of [Incrementing an integer value beyond its integer limit - C#](http://stackoverflow.com/questions/3108022/incrementing-an-integer-value-beyond-its-integer-limit-c) – user7116 May 31 '11 at 22:32
  • Instead, `while (true) i++;` will increment forever, overflowing every time you "exceed" `2147483647`. You can also try `while (i != -1)`. – Jeppe Stig Nielsen Jun 08 '12 at 05:49

9 Answers9

12

In C#, the built-in integers are represented by a sequence of bit values of a predefined length. For the basic int datatype that length is 32 bits. Since 32 bits can only represent 4,294,967,296 different possible values (since that is 2^32), clearly your code will not loop forever with continually increasing values.

Since int can hold both positive and negative numbers, the sign of the number must be encoded somehow. This is done with first bit. If the first bit is 1, then the number is negative.

Here are the int values laid out on a number-line in hexadecimal and decimal:

 Hexadecimal        Decimal
 -----------    -----------
 0x80000000     -2147483648
 0x80000001     -2147483647
 0x80000002     -2147483646
    ...              ...
 0xFFFFFFFE              -2
 0xFFFFFFFF              -1
 0x00000000               0
 0x00000001               1
 0x00000002               2
     ...             ...
 0x7FFFFFFE      2147483646
 0x7FFFFFFF      2147483647

As you can see from this chart, the bits that represent the smallest possible value are what you would get by adding one to the largest possible value, while ignoring the interpretation of the sign bit. When a signed number is added in this way, it is called "integer overflow". Whether or not an integer overflow is allowed or treated as an error is configurable with the checked and unchecked statements in C#. The default is unchecked, which is why no error occured, but you got that crazy small number in your program.

This representation is called 2's Complement.

Jeffrey L Whitledge
  • 58,241
  • 9
  • 71
  • 99
  • "Since int can hold both positive and negative numbers, the sign of the number must be encoded somehow. This is done with first bit. If the first bit is 1, then the number is negative." Is this true? I believe dotnet uses twos complement to represent signed integers. But I do believe that your reasoning is the right path as to why he gets the small number, is because the twos complement of the most negative number equals the most negative number. – Ernesto May 31 '11 at 22:32
  • @Ernesto - What I said is true, but incomplete. The first bit does represent the sign. I started to describe the rest of the format, but it was getting to be too much, so I just drew a diagram. I put in a link to the full description just as you were commenting. – Jeffrey L Whitledge May 31 '11 at 22:34
  • @Jeffrey -- being nit-picky, it is misleading to say that in 2's Complement the most significant bit is not strictly speaking a **sign bit**: instead it is a **binary digit** like the other bits, but it is taken to have a negative value where the others are positive. So in a 16-bit integer the LSB (bit 0) has a value of 0 or +1, bit 1 is 0 or +2, ... bit 14 has a value of 0 or +16384 and the MSB (bit 15) has a value of 0 or -32768. (BTW the MSB isn't always the "first bit" -- for example in a little-endian or bit-stream, it doesn't come first. But I think we all knew what you meant!) – AAT May 31 '11 at 23:32
  • @Ernesto -- the OP doesn't get a small number, he gets a very large **negative** number. – AAT May 31 '11 at 23:35
  • @Jeffrey, I agree. @AAT, It's sort of a bad habit of talking about negative numbers as if they were smaller than the positive ones. You're absolutely right. – Ernesto Jun 01 '11 at 04:25
  • @AAT @Ernesto - I started the "small number" terminology in my answer, and it made sense to me at the time, probably because I had just created a number-line (which isn't there anymore), and things to the left are smaller than things to the right. But I can also see your perspective that the absolute value determines smaller verses larger, and that generalizes to the magnitude of complex numbers also. So your point is well-taken. – Jeffrey L Whitledge Jun 01 '11 at 13:58
8

The value is overflowing the positive range of 32 bit integer storage going to 0xFFFFFFFF which is -2147483648 in decimal. This means you overflow at 31 bit integers.

It's been pointed out else where that if you use an unsigned int you'll get different behaviour as the 32nd bit isn't being used to store the sign of of the number.

ChrisF
  • 134,786
  • 31
  • 255
  • 325
  • 6
    To be precise, it didn't overflow a 32-bit value, it overflowed the positive range so it effectively overflowed 31 bits. -2147483648 is `0x80000000`; `0xFFFFFFFF` is -1. – David Yaw May 31 '11 at 22:14
  • 2
    @David it **did** overflow the positive maximum value for a signed 32-bit integer. An overflow is logically defined by the constraints of the data type, not by having reached the maximally-filled bit pattern of the underlying storage. Otherwise, reaching `0xFFFFFFFF` and adding 1 would be the true overflow. – ErikE May 31 '11 at 22:27
2

What you are experiencing is Integer Overflow.

In computer programming, an integer overflow occurs when an arithmetic operation attempts to create a numeric value that is larger than can be represented within the available storage space. For instance, adding 1 to the largest value that can be represented constitutes an integer overflow. The most common result in these cases is for the least significant representable bits of the result to be stored (the result is said to wrap).

Karmic Coder
  • 17,569
  • 6
  • 32
  • 42
2

int is a signed integer. Once past the max value, it starts from the min value (large negative) and marches towards 0.

Try again with uint and see what is different.

John Alexiou
  • 28,472
  • 11
  • 77
  • 133
2

Try it like this:

int i = 0;
while (i >= 0) 
   checked{ i++; } //Should increment forever
Console.Write(i);

And explain the results

H H
  • 263,252
  • 30
  • 330
  • 514
2

What the others have been saying. If you want something that can go on forever (and I wont remark on why you would need something of this sort), use the BigInteger class in the System.Numerics namespace (.NET 4+). You can do the comparison to an arbitrarily large number.

Tejs
  • 40,736
  • 10
  • 68
  • 86
0

This happens because when the variable "i" reaches the maximum int limit, the next value will be a negative one.

TarkaDaal
  • 18,798
  • 7
  • 34
  • 51
Alina Danila
  • 1,683
  • 1
  • 24
  • 60
0

It has a lot to do with how positive numbers and negative numbers are really stored in memory (at bit level).

If you're interested, check this video: Programming Paradigms at 12:25 and onwards. Pretty interesting and you will understand why your code behaves the way it does.

ErikE
  • 48,881
  • 23
  • 151
  • 196
InBetween
  • 32,319
  • 3
  • 50
  • 90
-1

I hope this does not sound like smart-ass advice, because its well meant, and not meant to be snarky.

What you are asking is for us to describe that which is pretty fundamental behaviour for integer datatypes.

There is a reason why datatypes are covered in the 1st year of any computer science course, its really very fundamental to understanding how and where things can go wrong (you can probably already see how the behaviour above if unexpected causes unexpected behaviour i.e. a bug in your application).

My advice is get hold of the reading material for 1st year computer science + Knuth's seminal work "The art of computer pragramming" and for ~ $500 you will have everything you need to become a great programmer, much cheaper than a whole Uni course ;-)

Tim Jarvis
  • 18,465
  • 9
  • 55
  • 92