1

Is the maximum variable width that an if statement can evaluate implementation specific. Or can a compiler force a cast on a variable to a another type such as a boolean to be evaluated by an if statement. Consider the following code below, as well as the output on my machine. I ask because I would like to reliably distribute a library using for use on a number of different machines including arm, mips, amd86, x86 etc. Please note that I understand that native integer types do not always reach 64/32/16 bit width.

Furthermore if it is defined, then how exactly is it defined?

Code:

#include <iostream>
#include <cstdint>
#include <bitset>
int main() {

// Initialises variables with a single binary 1 in the left most place.
uint8_t eight_bit = (~0) << (8 - 1);
uint16_t sixteen_bit = (~0) << (16 - 1);
uint32_t thirty_two_bit = (~0) << (32 - 1);
uint64_t sixty_four_bit = (~0) << (64 - 1);


std::cout << "The 8 bit integer is equal to: " 
    << std::bitset<8>(eight_bit) << "\n";
std::cout << "The 16 bit integer is equal to: " 
    << std::bitset<16>(sixteen_bit) << "\n";
std::cout << "The 32 bit integer is equal to: " 
    << std::bitset<32>(thirty_two_bit) << "\n";
std::cout << "The 64 bit integer is equal to: " 
    << std::bitset<64>(sixty_four_bit) << "\n";


if (eight_bit)      
    std::cout << "8 bit variable considered true." << "\n";

if (sixteen_bit)    
    std::cout << "16 bit variable considered true." << "\n";

if (thirty_two_bit) 
    std::cout << "32 bit variable considered true." << "\n";

if (sixty_four_bit) 
    std::cout << "64 bit variable considered true." << "\n";

return 0;
}

Output:

The 8 bit integer is equal to: 10000000
The 16 bit integer is equal to: 1000000000000000
The 32 bit integer is equal to: 10000000000000000000000000000000
The 64 bit integer is equal to: 1000000000000000000000000000000000000000000000000000000000000000
8 bit variable considered true.
16 bit variable considered true.
32 bit variable considered true.
64 bit variable considered true.
silvergasp
  • 1,517
  • 12
  • 23
  • You have a giant pile of undefined behavior there with the shifts. I'm also not sure I understand the question. – T.C. Feb 23 '16 at 03:44
  • An if() statement can evaluate any expression that evaluates to a bool. Any numerical type evaluates to a bool, via a comparison with 0, so the if statement will support any integer type supported on the target implementation. Then we come to pointers, which, in a bool context are also compared to 0. Then we come to an arbitrary class, if it has a suitable bool operator. The if statement is pretty versatile, wouldn't you agree? – Sam Varshavchik Feb 23 '16 at 03:45
  • `~0` is not equal to `1`. Just use `1 << (8-1)`. – R Sahu Feb 23 '16 at 03:52
  • @RSahu It does exactly the same thing as the other bits are simply lost. – silvergasp Feb 23 '16 at 03:53
  • I've had problems with integer promotion on shifting in the past. See http://stackoverflow.com/q/25902721/1553090 – paddy Feb 23 '16 at 03:53
  • @T.C. The idea behind the shift was simply an easy way to get a one in the furthest binary position to the left. This is simply so that I can demonstrate that if a cast to a boolean where to occur then the if statement would evaluate as a false because the cast integer would not have any bits that could be represented in a boolean variable. – silvergasp Feb 23 '16 at 03:57
  • 1
    @bruffalobill if you're interested in writing portable code, learning which shifts are portable is a good start. (These ones are not) – M.M Feb 23 '16 at 05:25
  • `(~0U) << 63` would have been portable, but this isn't. – MSalters Feb 23 '16 at 09:14
  • @MSalters not so; if `unsigned int` is 32-bit then it shifts by more than the width of the type – M.M Feb 23 '16 at 09:41
  • 1
    @M.M : You're indeed right, I managed to miss the other required suffix there. You need `U` as left-shifing a negative value is UB straight away, but you still have to abide by the other rules as well. Adding LL to give `(~0ULL) << 63` makes it safe on that count as well. – MSalters Feb 23 '16 at 09:49

2 Answers2

1

The definition of if(X), is that it behaves as if there were a temporary created by this definition:

bool temp(X);

and then iff temp is true, the test succeeds. If that declaration would be ill-formed then the if-statement is ill-formed.

Conversion from integer types to bool is defined that zero gives false and everything else gives true.

(To be clear, the answer to your question is that any type implictily convertible to bool may be evaluated by an if statement).


NB. (~0) << (8 - 1); etc. in your question causes undefined behaviour. Informally, left-shifting a 1 into the sign bit is undefined (which implies that all left-shifts of negative numbers are undefined).

Also, (~0) << (64 - 1); would cause UB due to shifting by greater than the width of the type, if you have 32-bit ints.

To avoid this, start with 1ULL instead of ~0, or do something like uint64_t x = INT64_MIN;. (With the latter approach you could use a template to find the value via std::numeric_limits).

M.M
  • 138,810
  • 21
  • 208
  • 365
0

The C++ standard (C++11) defines up to 64 bit integral types:

http://en.cppreference.com/w/cpp/types/integer

However, some implementations can go up to 128 bits.

https://msdn.microsoft.com/en-us/library/cc953fe1.aspx

Presumably a particular implementation could define their own extension to go arbitrarily large.

So, if you want to stick with portable C++ standard types, stick to 64-bit and below as defined in the C++ spec. If the native size is smaller, a standard compiler will provide the implementation to support the larger size (possibly at a performance penalty) by combining smaller native words. (e.g. creating a 64-bit integer from 2 32-bit words on a 32-bit platform.)

If you are really trying to ask "how do I find out the largest native size on my platform?" that's a different question.

Brad
  • 3,190
  • 1
  • 22
  • 36