Strange behaviour of 64 bit integer in C++ - c++

I'm quite new to programming, I have recently learnt a little C++ and I am using Visual Studio 2017 Community version.
I need to use a 64 bit integer to store a value and carry out some arithmetic operations, however my compiler only allows me to use 32 bits of the "int64" variable I have created.
Here is an example of some code and the behaviour
unsigned __int64 testInt = 0x0123456789ABCDEF;
printf("int value = %016X\n", testInt); // only 32 bits are being stored here? (4 bytes)
printf("size of integer in bytes = %i\n\n", sizeof(testInt)); // size of int is correct (8 bytes)
The value stored in the variable seems to be 0x0000000089ABCDEF.
Why can I not use all 64 bits of this integer, it seems to act as a 32 bit int?
Probably I'm missing something basic, but I can't find anything relating to this from searching :(

It would be nice if it were just something basic, but it turns out that 64 bit ints are not dealt with consistently on all platforms so we have to lean on macros.
This answer describes the use of PRIu64, PRIx64, and related macros included in <inttypes.h>. It looks funny like this, but I think the portable solution would look like:
#include <inttypes.h>
unsigned __int64 testInt = 0x0123456789ABCDEF;
printf("int value = %016" PRIX64 "\n", testInt);
The PRIX64 expands to the appropriate format specifier depending on your platform (probably llX for Visual Studio).

Format specifier %X takes an unsigned int (probably 32 bit on your system), whereas __int64 corresponds to long long.
Use printf("int value = %016llX\n", testInt) instead. Documentation can be found, for example, at cppreference.com.

Related

Using uint64_t in 32 bit application

Due to some libraries, I have to compile my application in 32 bit, but I need to use integer variables that exceed the max number of 32 bit types. So for example if I try to use uint64_t I get an overflow at 2147483647.
I thought it is possible to use 64 bit integer variables in 32 bit application, so what did I miss here? Do I have to include some specific header oder do I have to set some option therefore? Using VS17.
EDIT:
I did some testing, and in this example program, I can reproduce my overflow problem.
#include <iostream>
int main()
{
uint64_t i = 0;
while (true)
{
std::printf("%d\n",i);
i += (uint64_t)10000;
}
return 0;
}
The bug is here:
std::printf("%d\n",i);
^^
You've used the wrong format specifier, and therefore the behaviour of the program is undefined. %d is for signed int. You need to use
std::printf("%" PRIu64 "\n",i);
PRIu64 is declared in <cinttypes>.
P.S. You also haven't included the header which declares std::printf.

c++ portable conversion of long to double

I need to accurately convert a long representing bits to a double and my soluton shall be portable to different architectures (being able to be standard across compilers as g++ and clang++ woulf be great too).
I'm writing a fast approximation for computing the exp function as suggested in this question answers.
double fast_exp(double val)
{
double result = 0;
unsigned long temp = (unsigned long)(1512775 * val + 1072632447);
/* to convert from long bits to double,
but must check if they have the same size... */
temp = temp << 32;
memcpy(&result, &temp, sizeof(temp));
return result;
}
and I'm using the suggestion found here to convert the long into a double. The issue I'm facing is that whereas I got the following results for int values in [-5, 5] under OS X with clang++ and libc++:
0.00675211846828461
0.0183005779981613
0.0504353642463684
0.132078289985657
0.37483024597168
0.971007823944092
2.7694206237793
7.30961990356445
20.3215942382812
54.8094177246094
147.902587890625
I always get 0 under Ubuntu with clang++ (3.4, same version) and libstd++. The compiler there even tells me (through a warning) that the shifting operation can be problematic since the long has size equal or less that the shifting parameter (indicating that longs and doubles have not the same size probably)
Am I doing something wrong and/or is there a better way to solve the problem being as more compatible as possible?
First off, using "long" isn't portable. Use the fixed length integer types found in stdint.h. This will alleviate the need to check for the same size, since you'll know what size the integer will be.
The reason you are getting a warning is that left shifting 32 bits on the 32 bit intger is undefined behavior. What's bad about shifting a 32-bit variable 32 bits?
Also see this answer: Is it safe to assume sizeof(double) >= sizeof(void*)? It should be safe to assume that a double is 64bits, and then you can use a uint64_t to store the raw hex. No need to check for sizes, and everything is portable.

What is the point of the ULARGE_INTEGER union?

http://msdn.microsoft.com/en-us/library/windows/desktop/aa383742%28v=vs.85%29.aspx
They are supposed to be used like this, set two 32 bit values on LowPart and HighPart and then perform arithmetic on the QuadPart.
int a,b,c;
ULARGE_INTEGER u;
...
u.LowPart = a;
u.HighPart = b;
u.QuadPart += c;
So if you are going to perform arithmetic on the QuadPart(64 bits) anyways you need a 64 bit processor, right? So what is the whole point? Why not assign the value directly to the QuadPart?
You don't need a 64 bit processor to perform arithmetic on a 64 bit data type. All 32 bit compilers that I know of support arithmetic on 64 bit integers. If the hardware doesn't allow native arithmetic, then the compiler has to generate code to do the arithmetic. Typically this will make use of support functions in the compiler's RTL.
The struct is intended for use by compilers that don't provide native support for 64 bit data types. The very documentation to which you linked makes that clear:
Note Your C compiler may support 64-bit integers natively. For
example, Microsoft Visual C++ supports the __int64 sized integer type.
For more information, see the documentation included with your C
compiler.
Compilers that don't support native 64 bit integers will not be able to treat the QUADPART union member as an integer.
typedef union _ULARGE_INTEGER {
struct {
DWORD LowPart;
DWORD HighPart;
};
struct {
DWORD LowPart;
DWORD HighPart;
} u;
ULONGLONG QuadPart;
} ULARGE_INTEGER, *PULARGE_INTEGER;
And the definition of ULONGLONG:
#if !defined(_M_IX86)
typedef unsigned __int64 ULONGLONG;
#else
typedef double ULONGLONG;
#endif
Of course, all compilers written in the past 10 years (or more) will have native support for 64 bit integers. But this union was originally introduced a very long time ago and the compiler landscape would have been different then. When looking at Windows header files, always bear in mind the history and legacy.
Typically ULARGE_INTEGER is used when you need to convert a pair of 32-bit integers into a 64-bit integer or vice-versa.
For example, consider manipulating a FILETIME structure:
void add_millisecond(FILETIME * ft)
{
ULARGE_INTEGER uli;
uli.LowPart = ft->dwLowDateTime;
uli.HighPart = ft->dwHighDateTime;
uli.QuadPart += 10000;
ft->dwLowDateTime = uli.LowPart;
ft->dwHighDateTime = uli.HighPart;
}
You can't assign the QuadPart value directly because you don't have it; all you have is the high and low parts.
So if you are going to perform arithmetic on the QuadPart(64 bits)
anyways you need a 64 bit processor, right?
No. But the real question should be, do you need a compiler that supports a 64 bit integer type? The answer to that too, in this case, is no. That's what these functions are for:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa383711%28v=vs.85%29.aspx

C++ long overflowing prematurely

I'm having a bizarre problem with C++ where the long data type is overflowing long before it should. What I'm doing (with success so far) is to have integers behave like floats, so that the range [-32767,32767] is mapped to [-1.0,1.0]. Where it stumbles is with larger arguments representing floats greater than 1.0:
inline long times(long a, long b) {
printf("a=%ld b=%ld ",a,b);
a *= b;
printf("a*b=%ld ",a);
a /= 32767l;
printf("a*b/32767=%ld\n",a);
return a;
}
int main(void) {
printf("%ld\n",times(98301l,32767l));
}
What I get as output is:
a=98301 b=32767 a*b=-1073938429 a*b/32767=-32775
-32775
So times(98301,32767) is analogous to 3.0*1.0. This code works perfectly when the arguments to times are less than 32767 (1.0), but none of the intermediate steps with the arguments above should overflow the 64 bits of long.
Any ideas?
long is not necessarily 64 bits. try 'long long' instead.
The type long is not necessarily 64 bits. If you are on the 32 bit architecture (at least on MS Visual c++), the long type is 32 bits. Check it out with sizeof (long). There is also the long long data type that may help.
You probably have 32-bit longs. Try using long long instead.
98301 * 32767 = 3221028867, while a 32-bit long overflows at 2147483648
The C standard only guarantees that long will have at least 32 bit (which is actually the case on most 32bit platforms).
If you need 64 bit, use long long. It's guaranteed to hold at least 64 bit.

Why do I get a "constant too large" error?

I'm new to Windows development and I'm pretty confused.
When I compile this code with Visual C++ 2010, I get an error "constant too large." Why do I get this error, and how do I fix it?
Thanks!
int _tmain(int argc, _TCHAR* argv[])
{
unsigned long long foo = 142385141589604466688ULL;
return 0;
}
The digit sequence you're expressing would take about 67 bits -- maybe your "unsigned long long" type takes only (!) 64 bits, your digit sequence won't fit in its, etc, etc.
If you regularly need to deal with integers that won't fit in 64 bits you might want to look at languages that smoothly support them, such as Python (maybe with gmpy;-). Or, give up on language support and go for suitable libraries, such as GMP and MPIR!-)
A long long is 64 bits and thus holds a maximum value of 2^64, which is 9223372036854775807 as a signed value and 18446744073709551615 as an unsigned value. Your value is bigger, hence it's a constant value that's too large.
Pick a different data type to hold your value.
You get the error because your constant is too large.
From Wikipedia:
An unsigned long long's max value is at least 18,446,744,073,709,551,615
Here is the max value and your value:
18,446,744,073,709,551,615 // Max value
142,385,141,589,604,466,688 // Your value
See why your value is too long?
According to http://msdn.microsoft.com/en-us/library/s3f49ktz%28VS.100%29.aspx, the range of unsigned long long is 0 to 18,446,744,073,709,551,615.
142385141589604466688 > 18446744073709551615
You have reached the limit of your hardware to represent integers directly.
It seems that beyond 64 bits (on your hardware) requires the integer to be simulated by software constructs. There are several projects out there that help.
See BigInt
http://sourceforge.net/projects/cpp-bigint/
Note: Others have misconstrued that long long has a limit of 64 bits.
This is not accurate. The only limitation placed by the language are:
(Also Note: Currently C++ does not support long long (But C does) It is an extension by your compiler (coming in the next version of the standard))
sizeof(long) <= sizeof(long long)
sizeof(long long) * CHAR_BITS >= 64 // Not defined explicitly but deducible from
// The values defined in limits.h
For more details See:
What is the difference between an int and a long in C++?