3

I am using MinGW64 (with the -m64 flag) with Code::Blocks and am looking to know how to perform 64 bit calculations without having to cast a really big number to int64_t before multiplying it. For example, this does not result in overflow:

int64_t test = int64_t(2123123123) * 17; //Returns 36093093091

Without the cast, the calculation overflows like such:

int64_t test = 2123123123 * 17; //Returns 1733354723

A VirusTotal scan confirms that my executable is x64.

Additional Information: OS is Windows 7 x64.

FluorescentGreen5
  • 879
  • 11
  • 23
  • If your MinGW install includes the [`file` command](http://man7.org/linux/man-pages/man1/file.1.html), you can just run `file a.exe` to print info about the file. e.g. on my Linux desktop: `an-executable-I-just-compiled: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, ...`. For Windows, you'd see whether it was a 32 or 64-bit PE-format executable without having to upload it somewhere. – Peter Cordes Sep 18 '16 at 23:48

5 Answers5

4

The default int type is still 32 bit even in 64 bit compilations for compatibility resons.

The "shortest" version I guess would be to add the ll suffix to the number

int64_t test = 2123123123ll * 17;

Another way would be to store the numbers in their own variables of type int64_t (or long long) and multiply the varaibles. usually it's rare anyway in a program to have many "magic-numbers" hard-coded into the codebase.

Hayt
  • 5,210
  • 30
  • 37
  • what performance do you mean? It takes more memory (64 instead of 32 bit). But basically you tell the compiler you want this value to be a `long long` during compilation so you wont need to convert during runtime. If you are really concerned about performance though, the best way to check is test different versions and check for yourself. – Hayt Sep 16 '16 at 09:39
4

Some background:

Once upon a time, most computers had 8-bit arithmetic logic units and a 16-bit address bus. We called them 8-bit computers.

One of the first things we learned was that no real-world arithmetic problem can be expressed in 8-bits. It's like trying to reason about space flight with the arithmetic abilities of a chimpanzee. So we learned to write multi-word add, multiply, subtract and divide sequences. Because in most real-world problems, the numerical domain of the problem was bigger than 255.

The we briefly had 16-bit computers (where the same problem applied, 65535 is just not enough to model things) and then quite quickly, 32-bit arithmetic logic built in to chips. Gradually, the address bus caught up (20 bits, 24 bits, 32 bits if designers were feeling extravagant).

Then an interesting thing happened. Most of us didn't need to write multi-word arithmetic sequences any more. It turns out that most(tm) real world integer problems could be expressed in 32 bits (up to 4 billion).

Then we started producing more data at a faster rate than ever before, and we perceived the need to address more memory. The 64-bit computer eventually became the norm.

But still, most real-world integer arithmetic problems could be expressed in 32 bits. 4 billion is a big (enough) number for most things.

So, presumably through statistical analysis, your compiler writers decided that on your platform, the most useful size for an int would be 32 bits. Any smaller would be inefficient for 32-bit arithmetic (which we have needed from day 1) and any larger would waste space/registers/memory/cpu cycles.

Expressing an integer literal in c++ (and c) yields an int - the natural arithmetic size for the environment. In the present day, that is almost always a 32-bit value.

The c++ specification says that multiplying two ints yields an int. If it didn't then multiplying two ints would need to yield a long. But then what would multiplying two longs yield? A long long? Ok, that's possible. Now what if we multiply those? A long long long long?

So that's that.

int64_t x = 1 * 2; will do the following:

  1. take the integer (32 bits) of value 1.
  2. take the integer (32 bits) of value 2.
  3. multiply them together, storing the result in an integer. If the arithmetic overflows, so be it. That's your lookout.
  4. cast the resulting integer (whatever that may now be) to int64 (probably on your system a long int.

So in a nutshell, no. There is no shortcut to spelling out the type of at least one of the operands in the code snippet in the question. You can, of course, specify a literal. But there is no guarantee that the a long long (LL literal suffix) on your system is the same as int64_t. If you want an int64_t, and you want the code to be portable, you must spell it out.

For what it's worth:

In a post-c++11 world all the worrying about extra keystrokes and non-DRYness can disappear:

definitely an int64:

auto test = int64_t(2123123123) * 17;

definitely a long long:

auto test = 2'123'123'123LL * 17;

definitely int64, definitely initialised with a (possibly narrowing, but that's ok) long long:

auto test = int64_t(36'093'093'091LL);
Community
  • 1
  • 1
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
  • @Agnew a literal is indeed the same as spelling out the type of long long, which is not necessarily the same as int64_t. I can remove the ambiguity if you like. – Richard Hodges Sep 16 '16 at 08:32
  • OK, now I consider this hands-down the best answer here, since it mentions the potential distinction between `long long` and `std::int64_t`. If you really feel like it, you could mention it would still be possible to shorten the syntax a bit with user-defined literals; but the answer is indeed even good without that. – Angew is no longer proud of SO Sep 16 '16 at 08:38
  • 1
    Fun fact: in x86-64 machine code, instructions that operate on 64-bit integers are one byte longer than the same instructions on 32-bit integers. (e.g. `add rax, 1234` vs. `add eax, 1234`). The default operand-size is 32-bits, just like for 32-bit x86. So it's not just the ABI designers (compiler guys), but also the *hardware* architects that decided 32-bit was still a good "natural" size. (Highly biased towards having `int` the same size as most other platforms, for source portability.) Anyway, this decision was made by AMD around the year 2000, as they designed AMD64. – Peter Cordes Sep 18 '16 at 23:54
  • Interesting back story, but LL is at least 64 bits since c++11. – FluorescentGreen5 Sep 23 '16 at 10:44
  • 2
    @FluorescentGreen5 agreed but there are still 2 reasons I wouldn't recommend LL in this case. The first is that the question was not tagged c++11. The second is that it is conceivable that in a future architecture, assigning the result of LL to `int64_t` could actually result in a narrowing warning if long long is bigger than 64 bits. Spelling out the type is 5 keystrokes (and in c++11, the form `auto x = uint64_t(2) * 3;` is actually DRY) – Richard Hodges Sep 23 '16 at 11:13
  • @RichardHodges right, adding c++11 tag. but im pretty sure that if they're going to preserve backwards compatibility for `int == int32_t` then they're also going to preserve it for `long long ==` 64 bits. If we were going to work with numbers larger than 64 bits we would use a special library to do that. – FluorescentGreen5 Sep 23 '16 at 11:25
  • 1
    @FluorescentGreen5 you may well be right. But "pretty sure" is not the same as "the standard mandates". If I were code-reading and I saw `long long x = 44444444LL;` It would not cause me concern. If I saw `int64_t x = 4444444LL;` I would think, "does this guy realise that these aren't necessarily the same type? Which did he mean? long long or int64?". I either case, I would recommend specifying the type only once - and using `auto x = 999LL;` if I really wanted a long long and `auto x = std::int64_t(999)` if I really wanted a 64-bit value. It's clearer. – Richard Hodges Sep 23 '16 at 11:34
  • @RichardHodges I see, but can I define any sort of shortcut to `int64_t()`? – FluorescentGreen5 Sep 23 '16 at 11:36
  • 1
    @FluorescentGreen5 how many times are you typing it? If it's less than 20 then creating the alias will take more keystrokes :) I guess we could do `using i64 = int64_t;` but then people reading your code (by far your biggest concern) would need to check that an `i64` really was what they hoped it was. – Richard Hodges Sep 23 '16 at 11:48
  • 1
    If `int64_t` isn't wide enough for a resulting `long long` constant, you'll get a compiler warning (I think). Thanks to good compilers, we no longer have to worry about converting a compile-time-constant to a narrower type, as long as the type is wide enough for that specific value. It's pretty much always safe to just tack on LL to a constant and let the compiler optimize it back down to whatever width it actually needs to be, or that you assign it to. – Peter Cordes Jul 21 '19 at 03:51
  • 1
    `LL` is guaranteed to be at least as wide as `int64_t` because long long must hold at least 64-bit integers, and `int64_t` is fixed-width 2's complement if it exists at all. (Unlike `int_fast64_t` and `least64_t`, the fixed-width-2's-complement types are optional.) Fun fact: if `long long` is 64-bit 1's complement or sign/magnitude, 2's complement `int64_t` can still represent every possible long long (with `-0` = `0`) but not vice versa. Highly improbably in real life of course. – Peter Cordes Jul 21 '19 at 03:52
3

Since you're most likely in an LP64 environment, where int is only 32 bits, you have to be careful about literal constants in expressions. The easiest way to do this is to get into the habit of using the proper suffix on literal constants, so you would write the above as:

int64_t test = 2123123123LL * 17LL;
Paul R
  • 208,748
  • 37
  • 389
  • 560
2

2123123123 is an int (usually 32 bits).

Add an L to make it a long: 2123123123L (usually 32 or 64 bits, even in 64-bit mode).

Add another L to make it a long long: 2123123123LL (64 bits or more starting with C++11).

Note that you only need to add the suffix to constants that exceed the size of an int. Integral conversion will take care of producing the right result*.

(2123123123LL * 17)  // 17 is automatically converted to long long, the result is long long

* But beware: even if individual constants in an expression fit into an int, the whole operation can still overflow like in

(1024 * 1024 * 1024 * 10)

In that case you should make sure the arithmetic is performed at sufficient width (taking operator precedence into account):

(1024LL * 1024 * 1024 * 10)

- will perform all 3 operations in 64 bits, with a 64-bit result.

rustyx
  • 80,671
  • 25
  • 200
  • 267
1

Edit: Literal constants (A.K.A. magic numbers) are frowned upon, so the best way to do it would be to use symbolic constants (const int64_t value = 5). See What is a magic number, and why is it bad? for more info. It's best that you don't read the rest of this answer, unless you really want to use magic numbers for some strange reason.

Also, you can use intptr_t and uintprt_t from #include <cstdint> to let the compiler choose whether to use int or __int64.

For those who stumble upon this question, `LL` at the end of a number can do the trick, but it isn't recommended, as Richard Hodges told me that `long long` may not be always 64 bit, and can increase in size in the future, although it's not likely. See Richard Hodge's answer and the comments on it for more information.

The reliable way would be to put `using QW = int_64t;` at the top and use `QW(5)` instead of `5LL`.

Personally I think there should be an option to define all literals 64 bit without having to add any suffixes or functions to them, and use `int32_t(5)` when necessary, because some programs are unaffected by this change. Example: only use numbers for normal calculations instead of relying on integer overflow to do it's work. The problem is going from 64 bit to 32 bit, rather than going from 32 to 64, as the first 4 bytes are cut off.

Community
  • 1
  • 1
FluorescentGreen5
  • 879
  • 11
  • 23
  • 1
    I think most compilers will warn you when a compile-time constant expression overflows its type. Definitely when a single literal is too big for its type, but maybe also the result of an operator. – Peter Cordes Sep 24 '16 at 03:16
  • @PeterCordes True, but what if overflow shows up in many places? Surely there's got to be some compiler flag or line of code to put at the top to make all literals 64 bit. – FluorescentGreen5 Sep 24 '16 at 04:16
  • 1
    That seems like a good idea, but consider the implications: now you have a piece of code that looks like standard portable C++, but it actually only works correctly when compiled with a special option for a special compiler. On other compilers, or without that option, it will compile and run, but give wrong answers. Silently giving wrong answers is the worst failure mode. I think that's why there aren't many "modify the language this way" compiler options, other than for compatibility with old compilers that pre-date modern standards. – Peter Cordes Sep 24 '16 at 04:29
  • @PeterCordes What about a line of code that goes at the top? – FluorescentGreen5 Sep 24 '16 at 04:34
  • 1
    I totally agree it would be a nice feature, and I don't particularly like C++'s rules for this, or a lot of things (e.g. there's no portable way to get an arithmetic right shift, AFAIK). But we're basically stuck with them at this point, unless we switch to a new language (like Rust) and drop the legacy baggage. Since it's easily possible to deal with the issue without changing the language (using suffixes), I doubt any future extension to C++ will change this. – Peter Cordes Sep 24 '16 at 04:34
  • Yeah, this could be the sort of thing that might be ok for a `#pragma`. But again, this is just one of those things that you have to think about when writing in C. Literal constants are `int`s unless you decorate them with a size suffix. It often matters whether they're signed or unsigned as well, so it's not like you get to avoid thinking about the issue with your proposal. There will be some cases where it would have "worked" if literals were `long long` by default, sure. But it's not a cure-all so people still have to understand the issue. – Peter Cordes Sep 24 '16 at 04:37
  • Also, if `int_var * 1234` has to be evaluated as a `long long`, that could lead to slower code if the compiler doesn't manage to prove that it's ok to actually still do a 32-bit multiply. Making literals have 64-bit type by default could change the results of a program that calls `foo(unsigned long arg)` with code like `foo(int_var * 1234)`. Instead of being truncated to 32-bit, the full 64-bit multiply result is passed to `foo`. (Assuming long is 64-bit). Who knows which behaviour is "correct" for the code in question? – Peter Cordes Sep 24 '16 at 04:44
  • @PeterCordes I think the best thing to do would be just use `const int` or use this: `using int_t = int;` so you can just change it to `int64_t` in that one spot when porting to x64. It's a shame that something that simple things like literal constants can be so problematic :/ – FluorescentGreen5 Sep 24 '16 at 23:07
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/124127/discussion-between-fluorescentgreen5-and-peter-cordes). – FluorescentGreen5 Sep 24 '16 at 23:14