Big Numbers

[ LiB ]

Big Numbers

I'm going to come right out and say this: 32-bit integers are getting too small. Being able to store from -2 billion to +2 billion numbers, 32-bit numbers seemed big a long time ago, but that's just not enough storage space for numbers anymore.

Consider this: The standard C++ second-timer uses a signed 32-bit integer to store the number of seconds that has passed since 1970. What happens when you get past 2 billion seconds? Simple: Integers wrap around and become -2 billion instead. Obviously, this is a problem, but how long is 2 billion seconds anyway? It's 35 million minutes, or 596 thousand hours, which is 24 thousand days. That's 68 years . In 2038, all of our C++ second-timers will wrap around. That's a big problem.

As game programmers, we may not have to be concerned with wrapping second-timers; instead, we'll probably be using wrapping millisecond-timers. So how many milliseconds can 32 bits represent? About 24 days. Since this book is about programming persistent worlds with huge uptimes , 24 days just won't cut it for us.

So, why don't we use 64-bit numbers instead? A signed 64-bit number can hold from -9 quintillion to +9 quintillion. That's a huge number; in fact it's so large that you probably can't conceive how big it actually is. To put things is perspective, when used to store milliseconds, 64-bit integers can represent 292 million years. I think 64 bits ought to be enough for a timer.

So, the first thing you need to do is create a uniform 64-bit integer format. I'm going to do this within the BasicLib library package, which you can find on the CD in the directory /Libraries/BasicLib/. The file that will hold the 64-bit datatype is called BasicLibTypes.h. Everything within this file is also within the BasicLib namespace.

In the C99 standard, C is supposed to have a long long datatype (two longs), which is 64 bits. For example:

 long long foo;             // signed 64 bits unsigned long long bar;    // unsigned 64 bits 

Unfortunately, not all compilers are up-to-date. The Linux compiler, GCC, has no problems with 64-bit integers, but Microsoft Visual C++.NET and prior versions don't support it (VS.NET 2003 should support it, though); therefore, you need to use the Microsoft-specific 64-bit integer format:

 __int64 foo;               // signed 64 bits unsigned __int64 bar;      // unsigned 64 bits 


Personally, I never use bitshifts any more anyway. I put my trust into the compiler to make the best decision about optimizing my code, since it can do a much better job than I can.

In front of the int64 are two underscores, not just one. As far as I can tell, __int64 acts just like a long long , with just one exception: it doesn't support bitshifting. I'm not sure why this is, but it's one limitation you must keep in mind when using __int64 .

So, finally, here's the code that seamlessly uses __int64 's or long long s based on the system you are using:

 #ifdef __GNUC__   // Linux     typedef long long int sint64;     typedef unsigned long long int uint64; #endif #ifdef WIN32    // Windows     typedef __int64 sint64;     typedef unsigned __int64 uint64; #endif 

I've typedefed the 64-bit integers into new types: sint64 and uint64 . Now, whenever you need to use a 64-bit number, you can just include the BasicLib.h file, and use it:

 #include "BasicLib/BasicLib.h" sint64 foo; uint64 bar; 

Voil ! Platform-independent 64-bit integers!


64-Bit Integers and Streams

Visual C++ 6 has a major problem streaming 64-bit integers to and from streams. I think it's actually a bug somewhere in the template code, but regardless, you just can't do it. Since Microsoft has already created a newer and better compiler, I can't imagine it cares much about fixing the old version. You're going to have to live with this limitation.

I worked around the limitation and created a hack . Yes, hacks are an ugly thing, but sometimes they are just plain neccessary.

To cut a long story short, I had to create some way of determining if you're using VC6, and I did so using macros:

 #ifdef WIN32     #if _MSC_VER >= 1300         #define GOODCOMPILER     #else         #define CRAPPYCOMPILER     #endif #endif #ifdef __GNUC__     #define GOODCOMPILER #endif 

If you have VC7 or above, or GCC, you have a good compiler. If you have VC6 or below, you have a crappy compiler. To work around the streaming problem, I created a few stream helper functions:

 template< typename type > inline void insert( std::ostream& s, const type& t ); template< typename type > inline type& extract( std::istream& s, type& t ); 

These functions essentially perform the same task as the common operator<< and operator>> (see Appendix C, "C++ Primer," on the CD if you are unfamiliar with them), but since those operators are broken in VC6, you need to use these instead whenever you stream a sint64 or uint64 . For example:

 sint64 bigint = 12345677889467365; // cout << bigint << endl;    <-- WILL NOT COMPILE ON VC6 BasicLib::insert( cout, bigint ); BasicLib::extract( cin, bigint ); 

You can find these functions in the BasicLibString.h file on the CD; I had to create a template specialization for the 64-bit integer types, which wraps around the VC6 _i64toa , _ui64toa , and _atoi64 functions. A template specialization is a function that works on a specific kind of datatype. It's a rather large and complex topic, so I won't be covering it here. All you really need to know is that you need to use insert and extract when streaming 64-bit integers.

[ LiB ]

MUD Game Programming
MUD Game Programming (Premier Press Game Development)
ISBN: 1592000908
EAN: 2147483647
Year: 2003
Pages: 147
Authors: Ron Penton © 2008-2017.
If you may any questions please contact us: