Hi, I started learning about Binary since I was curious how programming language turns into 1s and 0s. I learned that 2^64 is the number limit. I made a loop that would count up to that number :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
#include "stdafx.h"
#include <iostream>
#include <cmath>
usingnamespace std;
int main()
{
for (unsignedlonglong x = 0; x <= 18400000000000000000; x+=100000000000000000)
{
cout << x << endl;
}
cin.get();
}
This program works fine. However, adding another 0 to x+=10000000000000000 to make it reach 18 quadrillion faster ends up giving this result :
^ This goes to 18 quadrillion and then goes to another number and starts adding again - and it does this twice, both times going as close to 18 quadrillion as it can. Why does adding another 0 do this?
(Note: For the sake of brevity, from this point forward k = 10^17)
When x == 184*k the loop will continue running because the condition is x <= 184*k. (2^64 - 1) - 184*k is less than k, so 184*k + k will loop around to become 185*k % 2^64 = (184 + 1) * k - 2^64 = 53255926290448384, and then it will run one more time until x > 184*k.
Should clarify some things. x+=10000000000000000 (10^16) - Accidentally typed an extra zero into the copied and pasted code. Also, I realize that x<= 184*k means that as long as x is less than or equal to 184*k that it should continue running. However, when x+=100000000000000000 (10^16) - It simply stops exactly at 184*k . However, if I add an extra 0, it will do as you said and loop over until x will become larger than 184*k - Even though adding another 0 still makes 184*k divisible by the number that it'll add. In fact, making x+= anything less than 10^17 will make it stop at exactly 184*k . It doesn't really make sense, but that's how it's working. Why?
By the way, I really appreciate you helping me Helios, it's very kind of you to help !
Not gonna lie, I'm just starting out and that code looks slightly complicated for me to understand - But that could just be because I haven't slept and it's hours into the AM. Will look over again after some rest, but it would be appreciated it you could break down the code and tell me what each part does. You don't have to though - Thanks for the help JLBorges !
However, when x+=100000000000000000 (10^16) - It simply stops exactly at 184*k . However, if I add an extra 0, it will do as you said and loop over until x will become larger than 184*k - Even though adding another 0 still makes 184*k divisible by the number that it'll add. In fact, making x+= anything less than 10^17 will make it stop at exactly 184*k . It doesn't really make sense, but that's how it's working. Why?
2^64 - 184*k is greater than k/10, so 184*k + k/10 fits in a 64-bit integer, so the value doesn't loop around.
What determines whether the loop monotonically increases x is not the divisibility of the limit by the increment, but whether there exists an integer m such that limit < limit*m < 2^64.
#include <iostream>
#include <limits>
int main()
{
// max is the largest value that unsigned long long can hold
constauto max = std::numeric_limits<unsignedlonglong>::max() ;
for( int i = 1 ; i < 7 ; ++i )
{
// the increment incr is 1000000000000000000 * i
// ie. through the loop, incr becomes 1000000000000000000, 2000000000000000000
// 3000000000000000000 ... up to 6000000000000000000
unsignedlonglong incr = 1000000000000000000 * i ;
std::cout << "increment: " << incr << "\n\n" ;
unsignedlonglong x = 0;
// ubound is the largest value of x to which we can add incr and
// still not exceed the largest value that unsigned long long can hold
unsignedlonglong ubound = max - incr ;
// print values of x, in steps of incr, up to ubound
for( ; x <= ubound ; x += incr ) std::cout << x << '\n' ;
// at this point, x is still less than or equal to max, but we can't add
// incr to it without breaching the largest value that unsigned long long can hold
// print out this largest possible value that x reached
// this is the last number printed out for this particular value of incr
std::cout << x << '\n' ; // x can't be incremented any more
std::cout << "\n---------------------------\n\n" ;
}
}
Thanks Helios, I understand now. Having x<= k makes the program unable to compute the last addition needed to complete the task since it would be greater than 2^64 - forcing it to jump to another number and begin adding again until it will be able to add k to achieve both x being greater than 184*k but less than 2^64. However, taking out a 0 (or multiple ones) allows it to compute since the 2^64 limit wont be breached when it tried to add one more time to make x greater than 184*k. -- My only question is that when it loops, why does it choose to subtract instead of doing something like crash or overload? Moreover, how does it know what to subtract? - since for the first subtraction, it will always take away roughly 183.47*k and then begin adding again.
Thanks JLBorges, looking at the code now I understand it better. However, I still haven't learned some of the functions that have been used and the way they've been used, so I don't think I'd be able to write a set of code like this, yet.
Thanks to both of you, it has been very informative. I'm going through a very good tutorial of C++, but I like to explore every nook and cranny when I get the chance to, it makes one learn better. So, you guys helping me has been a great help, thanks!
My only question is that when it loops, why does it choose to subtract instead of doing something like crash or overload? Moreover, how does it know what to subtract? - since for the first subtraction, it will always take away roughly 183.47*k and then begin adding again.
I answered this in my first reply. Addition with n-bit unsigned operands is performed in modulo 2^n arithmetic:
result = (addend1 + addend2) % 2^n
result = (minuend - subtrahend + 2^n) % 2^n