C 64-bit Constant

0 views
Skip to first unread message

Reda

unread,
Aug 5, 2024, 1:33:39 AM8/5/24
to repadicon
Funnyenough, but you don't actually need to add any suffix to your hex constant in order for it to be treated correctly. Section 6.4.4.1 of the C standard and section 2.14.3 of the C++ standard contain the following table:

This means that the compiler will iterate through the following types for the hexadecimal constant 0x800000000000000 (it has no suffix, so it uses the "none" row, and it's a hex constant, so it uses the "Hexadecimal Constant" column), and it will use the first type which can store that value*:


So, to answer your question of "How can I write and use the value 0x800000000000000 and make sure the compiler won't treat the high bit as a sign bit?": Simply just write unsigned long long value = 0x800000000000000.


If you want to do some bitwise arithmetic with the value, you can just go ahead and do that (i.e. just write 0x800000000000000 >> myval). You're guaranteed that it will not be treated as an overflowed signed integer, and your right shift won't do any sign extension because it's a positive value.


*I'm assuming that int is 32-bits, long is 32-bits, and long long is 64-bits. Note that your compiler might use different bit sizes for these types, which may change the end result (though the process will still be the same).


Generally, putting the constant in memory and loading it is a good approach, and likely to be faster than trying to generate with lui/addi/slli/etc unless the constant has a long string of 0s or 1s that make it easy to generate.


The ISA does provide encodings for 48-bit, 64-bit, 96-bit, etc instructions. If you want a better way to load a 64-bit constant, you can always add a wider instruction with more immediate bits. You could add a 64-bit instruction with 52 bits of immediate for instance which then lets you load a 64-bit constant with that instruction plus addi.


Fine. We have declared a 64-bit integer constant, and in this case we have stored a 40-bit integer at that address. I would have assumed that the F0F0F0F0E1 would simply be stored with preceding zeros, I think it should look like 000000F0F0F0F0E1.


Actually, constants are always interpreted as "big enough" to hold the specified value, so 0x1234000099 will be be a 64bit constant with or without the LL suffix. Where it gets complicated is when you do math. "3000 * 4000" will be different than "3000L * 4000" (and the first one will probably be WRONG, since 120000000 doesn't fit in an "int.")


Specifically, it is interpreted as an int. It just so happens that AVR-GCC makes int 16 bits wide for the 8-bit architectures, but other processors (such as the 32-bit ARM used by the Due) can use a different sized int type, such as 32 bits.


- Launching the game in 32 bit: can join a (multiplayer) game, but get constant crashes to desktop with no error message and frequent steam disconnections. I also get a strange visual bug in game where the images are all scrambled (the artwork is missing, and it looks like one of those colourblind tests full of random pixels) - I will get a screenshot and edit this post to include it. In summary, I can usually play a turn or two before everything goes sideways and the errors happen no matter what multiplayer game I join.


Thanks for the suggestions, Groo. So far those launch suggestions have not helped get past the splashscreen in 64bit mode - I tried playing through the single player tutorial in 32bit with it and it seemed ok (no crashes to desktop). Will test multiplayer and let you know how it goes.


Update: tried both those launch commands, and continue crashing to desktop with no error message in multiplayer mode (32 bit), I occasionally also freeze and am forced to restart the computer (when I do so, it tells me that the NET Broadcast Window is preventing the restart). Neither option helps getting past the splashscreen on 64bit mode. The crashes happen every turn, occasionally I get two turns if I am lucky.


No idea what else the above has done to my computer but happy I got this far! Will test multiplayer and see if the crashes keep happening.



Next update: it works beautifully! I can play 64bit Endless Legend crash free in multiplayer with the above fix. I'm soooo happy :D


I followed these instructions after hours of trying to troubleshoot the problem and it worked! Never tampered with those particular Windows menus before for a video game but I'm glad that I had some faith in them. I hope future people will find this post helpful like I did. Please note my system specs are Windows 10 x64, and I am using the Steam version of the Definitive Edition. I note that a few posts above me it did not work for them, potentially due to anti-virus flagging? (my AVS did not flag Endless Legend at any point however)


Now the cause of the problem was clear. Without the literal suffix LL the compiler was correctly zero extending the 32-bit integer literal value -2147483648 to 64-bits which actually has a value of 2147483648. I had to explicitly tell the compiler that I want to treat the literal as a 64-bit integer using the LL suffix.


I could see in the C++ debugger that the value in the L_20_System_Int64 variable was not correct. Indeed, the lack of a literal suffix was the cause. After a simple change to the il2cpp code generation method for ldc.i8, the correct C++ code was generated, and the conv.ovf.i4 test worked as expected.


I was surprised to see the effort in C++11 to get user-defined literal suffixes into the standard. After this experience though, I have a new found respect for these seemingly innocuous characters, and the importance they can play in the correct execution of a program.


Correct. A = 1, A = 1.0, and A = 1.00000000 should all produce the same internal floating point bits since 1.0 has an exact representation in the IEEE binary floating point representations. Besides, trailing zeros do not affect the value of a floating point constant. The value 0.1 does not have an exact representation, so is quite different from 1.0.


@JohnCampbell provided one explanation: that the evolution was via 32-bit systems first, and then changing it back to 64-bit would break codes, so they just stuck with 32-bit default real. That makes perfect sense.


Doing that would run into problems, given that the Fortran standard has a requirement that (i) at least two real types should be provided, and (ii) double precision should have higher precision than single precision. In the absence of hardware 128-bit (now quadruple, proposed double) precision, double (new) precision calculations (which would have higher precision than 64-bit IEEE) would be quite slow.


There are many simulations that run fine at present with single-precision reals (32 bits, of which 23+1 are mantissa). These codes would need to be recompiled with a compiler flag that selects 32-bit reals for REAL if the default has become 64-bit. Or, if 64-bit default reals are to be used in the future, all constants in the programs will need to be modified to be 64-bit real constants.


Doing that would run into problems, given that the Fortran standard has a requirement that (i) at least two real types should be provided, and (ii) double precision should have higher precision than single precision.


How about integer off-the-range constants? It seems that at least some compilers treat them somewhat inconsistently. The standard says that a literal integer constant without a kind parameter should be treated as default integer type. Consider following code:


That was obvious even without this warning. I am just curious why reals are treated somewhat differently (not saying it is necessarily not standard-conforming). The same invocation of ifort -stand:f18 with the real assignment


There is all this discussion about what is error prone coding, much of which is basically style based bias. (COMMON is now bad but thousands of lines of code for OOP definitions is not error prone?)

F90 introduced generic intrinsics, but now there is all this class coding for supporting different variable kind/type. Many lines of code that must correctly interact. Is this progress ?


As explained by @wclodius , the vector Cray machines were quite atypical ones. And having a 64 bits default REAL was actually sometimes a source of frustration because it meant twice the memory occupation in the cases where 32 bits REALs were enough. For memory critical applications it was frustrating.


Regarding Cray in particular, I used several of the 64-bit real machines in the 1980s, and then I switched over to using mostly RISC machines in the 1990s. Then Cray was bought and sold several times. I think SGI owned cray for a while, and they built and sold RISC-based machines at that time. I think that might have been when the Cray compilers switched to default 32-bit real and integer data sizes. Then later, maybe in the early 2000s, Cray ended up with AMD chips, which were also 32-bit real and integer defaults, and that was the first time I ran into problems matching fortran code with library routines because there were so many possibilities. I remember thinking at that time that things would probably have been simpler to just stick with default 64-bit real and integers throughout that time.


On a separate issue, Cray characters (in their cft77 compiler) were 8-bits, not 64-bits. There were 8 characters per 64-bit word. I think this made character processing slow, but memory was so limited at that time, that was the compromise that was chosen. People used Crays for their floating point performance, not for their character processing abilities.

3a8082e126
Reply all
Reply to author
Forward
0 new messages