using a for loop to determine maximum value of an int variable

P

Peter Duniho

[...]
I cant understand what is going on in the second loop (or more
preciscesly why, what is going on in the second loop, is happeneing) -
why divide y by 2 for instance?

Can someone please go through the second loop and comment it for me so
I understand what is happening.

It's a way of subdividing the "narrow it down" portion of the algorithm, to
maximize speed while still allowing for accuracy.

In particular, note that after the first loop, you now have an upper bound
on the maximum possible value for the variable. That is, y + y. You know
that y <= max < y + y.

So, the second loop tries to traverse the distance between y and y + y as
quickly as possible. It does this by first trying to go half the distance.
If that doesn't work, it cuts the distance in half and tries again. If it
*does* work, then it also cuts the distance in half and tries again.

The reason for cutting the distance in half in the failure case should be
obvious: you need to try a smaller distance, because the first try overshot.
The reason for cutting the distance in half in the success case might be
obvious to you, or it might not: it's because delta is half of the known gap
between a value known to be below or equal to the maximum, and the value
known to be above the maximum. First time through the loop, that's the y <=
max < y + y thing again.

Since the max value is less than y + y, you know for a fact that trying to
add delta one more time is going to fail (in the simplest case that is, you
simply have y + delta + delta = y + y, but even after you've been through
the loop once, adding whatever the current value of delta is twice will
always result in the net sum being y + y, so you never want to add delta
twice...halving it each time through is a way of ensuring that you don't).

Note that in some sense, the code you're asking about is simply finding the
individual bits that make up the maximum binary number allowed for the type.
The first loop finds the highest bit stored in the number, scanning through
the powers of two until it hits one that turns the number negative. The
second loop works its way backwards through the bits, adding each one that
will fit back in until you have the full maximum value allowed.

Note also that the code you're asking about (as well as many other samples
in this thread) is mostly theoretical. As has been mentioned, it makes a
lot more sense to simply note the built-in max value constants and use that
for a given type. Even if you didn't have those constants, it would be far
simpler to just check the size of the type and use predetermined knowledge
about the number representation for that type to come up with the
appropriate maximum value. Algorithmically determining the maximum value
for some type is essentially pointless for any built-in type (I suppose if
you're dealing with some abstracted numeric type that has nondeterministic
range restrictions, such an algorithm might be useful).

That said, I have some comments regarding the code you're asking about:

* Because we're dealing with binary numbers, the check for "y + delta >
0" before adding delta to y isn't needed. Perhaps this ties in with the
"abstracted numeric type" I posited above, but it doesn't apply to an "int",
for example.

* Also because the storage is a simple binary number -- that is,
assuming the maximum value is (2^N-1) for some N -- then the second loop is
a waste of time. Given that the code assumes that the type allows overflow
and it assumes that the storage is a straight binary representation, once
you've gotten to x being negative, you can find the maximum positive value
simply by subtracting 1 from x.

* I don't really like the condition for the while() in the second loop.
I believe that the loop is guaranteed to terminate, because of the way y is
calculated in the first loop. But it still strikes me as wrong to have a
loop in which the term that allows the loop to make forward progress can
eventually reach 0, but in which the loop condition does not test for that
case. That code in a slightly different context could easily wind up
looping infinitely. It's bad form, IMHO.

Pete
 
T

Tom Porterfield

for (double j = 3.14, int i = 0; i * j < 300; i++, j *= 1.1)
{
Console.WriteLine("i: {0}, j: {1}", i, j);
}

compiler output:
Error 1
Identifier expected, 'int' is a keyword C:\...\Temporary
Projects\ConsoleApplication1\Program.cs 17 29 ConsoleApplication1

You can only have one declare one type in a for loop. Just as you can't do
the following:

int i = 0, double j = 3.14;

but this is correct:

int i = 0, j = 3;

That doesn't mean you can't do what you have above, just needs a slight
modification:

int i = 0;
for (double j = 3.14; i * j < 300; i++, j *= 1.1)
{
Console.WriteLine("i: {0}, j: {1}", i, j);
}
 
P

per9000

Tom said:
You can only have one declare one type in a for loop. Just as you can't do
the following:

int i = 0, double j = 3.14;

Makes sense when you put it that way. Thanks.

/Per
 
G

garyusenet

Wow thanks to all the replied. And thankyou Pete for your very detailed
explanation. I'm going to re read over the next day or so and hopefully
it will start to sink in. This is all very new to me and so I find it
hard. But hopefully given time it will become more intuitive.

Thanks again!

Gary-
 
G

garyusenet

Can someone please tell me why an integer variable becomes negative
when it exceeds its maximum value, what causes this?

Thanks,

Gary-
 
T

Tom Porterfield

Can someone please tell me why an integer variable becomes negative
when it exceeds its maximum value, what causes this?

I can't find much documentation on why this happens, only that it does
happen as a result of how it truncates values outside of its range. As I
mentioned in an earlier post in this thread, it loops back to the min value
and then adds one. Take the following code example:

int i = int.MaxValue;
Console.WriteLine(int.MinValue);
for (int j = 0; j < 10; j++)
{
Console.WriteLine(++i);
}

The first time through this loop, the output will be the same as
int.MinValue. The output will then increase by one each time through the
loop.

-2147483648
-2147483648
-2147483647
-2147483646
-2147483645
-2147483644
-2147483643
-2147483642
-2147483641
-2147483640
-2147483639

All integral types behave this way (short, int, long). The behavior is
different for floating point types (float, double) and decimal.
 
B

Ben Newsam

Can someone please tell me why an integer variable becomes negative
when it exceeds its maximum value, what causes this?

The answer is "Two's complement arithmetic".

When integer values are stored (in binary, of course), the method
chosen to denote that a number is negative is to set the "top" bit to
a 1. It is always the "top" bit, no matter how many bits are used to
represent the number. So, if we were using just 4 bits, which can
store 16 different values, "unsigned" would be from 0 (0000 in binary)
to 15 (1111 in binary). To allow negative numbers, the maximum
positive number would be 7 (0111 in binary). Adding 1 to that would
not give 8, but -8, negative because the top bit is set. This method
of representing numbers is very useful because it makes arithemetic
simple. Starting from the maximum positive integer, you can keep
subtracting 1 to get the next smaller number. Zero really is zero (no
bits set), and you can continue subtracting to get negative numbers:

0111 7
0110 6
0101 5
0100 4
0011 3
0010 2
0001 1
0000 0
1111 -1
1110 -2
1101 -3
1100 -4
1011 -5
1010 -6
1001 -7
1000 -8

The reason it is called "two's complement" is because to change a
number from its positive representation to its negative (*and* vice
versa, which is really handy!) you "complement" the number by swapping
all the 1s for 0s and all the 0s for 1s, and then add 1. So, to change
3 to -3, you take 0011, complement it to 1100, and add 1, leaving
1101. To change it back to its positive, you take -3 (1101)),
complement it (0010), add one (0011) and you're back at 3 again. This
is useful when doing stuff at a very low level.

Now, to get back to the original example: clearly (in this case,
anyway) integers are stored in 32 bits of memory. In 32 bit "signed"
integers, the maximum possible integer that can be stored is 0111 1111
1111 1111 1111 1111 1111 1111, which in decimal is 2147483647. If you
add one to that, you will get 1000 0000 0000 0000 0000 0000, which is,
lo and behold, -2147483648
 
B

Bill Butler

Peter Duniho said:
That said, I have some comments regarding the code you're asking
about:

* Because we're dealing with binary numbers, the check for "y +
delta > 0" before adding delta to y isn't needed. Perhaps this ties
in with the "abstracted numeric type" I posited above, but it doesn't
apply to an "int", for example.

* Also because the storage is a simple binary number -- that is,
assuming the maximum value is (2^N-1) for some N -- then the second
loop is a waste of time. Given that the code assumes that the type
allows overflow and it assumes that the storage is a straight binary
representation, once you've gotten to x being negative, you can find
the maximum positive value simply by subtracting 1 from x.

* I don't really like the condition for the while() in the second
loop. I believe that the loop is guaranteed to terminate, because of
the way y is calculated in the first loop. But it still strikes me as
wrong to have a loop in which the term that allows the loop to make
forward progress can eventually reach 0, but in which the loop
condition does not test for that case. That code in a slightly
different context could easily wind up looping infinitely. It's bad
form, IMHO.

All good points
Perhaps I should have spent more than 5 minutes on it :^)
I was simply attempting to point out how to improve on O(N) performance
Bill
 
P

Peter Duniho

Bill Butler said:
[...]
All good points
Perhaps I should have spent more than 5 minutes on it :^)
I was simply attempting to point out how to improve on O(N) performance

Well, I hope you're not taking it too hard. After all, applying the
unbounded binary search technique the problem seems reasonable, if you want
to completely abstract it. :) Just getting the basic algorithm out there
is the key thing, even if it could use a little massaging after the fact.

And I think you're being way too conservative on your algorithm order
analysis. IMHO, since the element of the data that alters the length of the
algorithm is the number of bits in the variable (rather than the maximum
value per se), I think that the order is actually exponential -- O(2^N) --
rather than linear. So your binary search is clearly even *more* of an
improvement than you seem to think.

Still, hopefully we can all agree that using a built-in constant is really
the fastest, most appropriate solution. :)

Pete
 
B

Ben Newsam

Now, to get back to the original example: clearly (in this case,
anyway) integers are stored in 32 bits of memory. In 32 bit "signed"
integers, the maximum possible integer that can be stored is 0111 1111
1111 1111 1111 1111 1111 1111, which in decimal is 2147483647. If you
add one to that, you will get 1000 0000 0000 0000 0000 0000, which is,
lo and behold, -2147483648

I missed some 0s off the lowest negative number. To fix it, keep
adding 0s until there are 31 of them! <g> Thus:

10000000000000000000000000000000
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads

macro substitution in C# 2
determine if int is power of 2 3
why do I get compile error 2
locking an int 31
Variable vs. child scopes 3
access a variable using a value from db column for its name. 12
for loop 15
b-tree 11

Top