Is it safe to use -1 to set all bits to true?

39,996

Solution 1

I recommend you to do it exactly as you have shown, since it is the most straight forward one. Initialize to -1 which will work always, independent of the actual sign representation, while ~ will sometimes have surprising behavior because you will have to have the right operand type. Only then you will get the most high value of an unsigned type.

For an example of a possible surprise, consider this one:

unsigned long a = ~0u;

It won't necessarily store a pattern with all bits 1 into a. But it will first create a pattern with all bits 1 in an unsigned int, and then assign it to a. What happens when unsigned long has more bits is that not all of those are 1.

And consider this one, which will fail on a non-two's complement representation:

unsigned int a = ~0; // Should have done ~0u !

The reason for that is that ~0 has to invert all bits. Inverting that will yield -1 on a two's complement machine (which is the value we need!), but will not yield -1 on another representation. On a one's complement machine, it yields zero. Thus, on a one's complement machine, the above will initialize a to zero.

The thing you should understand is that it's all about values - not bits. The variable is initialized with a value. If in the initializer you modify the bits of the variable used for initialization, the value will be generated according to those bits. The value you need, to initialize a to the highest possible value, is -1 or UINT_MAX. The second will depend on the type of a - you will need to use ULONG_MAX for an unsigned long. However, the first will not depend on its type, and it's a nice way of getting the most highest value.

We are not talking about whether -1 has all bits one (it doesn't always have). And we're not talking about whether ~0 has all bits one (it has, of course).

But what we are talking about is what the result of the initialized flags variable is. And for it, only -1 will work with every type and machine.

Solution 2

  • unsigned int flags = -1; is portable.
  • unsigned int flags = ~0; isn't portable because it relies on a two's-complement representation.
  • unsigned int flags = 0xffffffff; isn't portable because it assumes 32-bit ints.

If you want to set all bits in a way guaranteed by the C standard, use the first one.

Solution 3

Frankly I think all fff's is more readable. As to the comment that its an antipattern, if you really care that all the bits are set/cleared, I would argue that you are probably in a situation where you care about the size of the variable anyway, which would call for something like boost::uint16_t, etc.

Solution 4

A way which avoids the problems mentioned is to simply do:

unsigned int flags = 0;
flags = ~flags;

Portable and to the point.

Solution 5

unsigned int flags = -1;  // all bits are true

"Is this a good[,] portable way to accomplish this?"

Portable? Yes.

Good? Debatable, as evidenced by all the confusion shown on this thread. Being clear enough that your fellow programmers can understand the code without confusion should be one of the dimensions we measure for good code.

Also, this method is prone to compiler warnings. To elide the warning without crippling your compiler, you'd need an explicit cast. For example,

unsigned int flags = static_cast<unsigned int>(-1);

The explicit cast requires that you pay attention to the target type. If you're paying attention to the target type, then you'll naturally avoid the pitfalls of the other approaches.

My advice would be to pay attention to the target type and make sure there are no implicit conversions. For example:

unsigned int flags1 = UINT_MAX;
unsigned int flags2 = ~static_cast<unsigned int>(0);
unsigned long flags3 = ULONG_MAX;
unsigned long flags4 = ~static_cast<unsigned long>(0);

All of which are correct and more obvious to your fellow programmers.

And with C++11: We can use auto to make any of these even simpler:

auto flags1 = UINT_MAX;
auto flags2 = ~static_cast<unsigned int>(0);
auto flags3 = ULONG_MAX;
auto flags4 = ~static_cast<unsigned long>(0);

I consider correct and obvious better than simply correct.

Share:
39,996
hyperlogic
Author by

hyperlogic

\(^_^)/

Updated on July 08, 2022

Comments

  • hyperlogic
    hyperlogic almost 2 years

    I've seen this pattern used a lot in C & C++.

    unsigned int flags = -1;  // all bits are true
    

    Is this a good portable way to accomplish this? Or is using 0xffffffff or ~0 better?

  • josesuero
    josesuero about 15 years
    why is -1 guaranteed to be converted to all ones? Is that guaranteed by the standard?
  • Johannes Schaub - litb
    Johannes Schaub - litb about 15 years
    the conversion that happens is that it repeatedly adds one more than ULONG_MAX until it is in range (6.3.1.3 in the C TC2 draft). In C++ it's the same, just using another way formalizing it (modulo 2^n). It all comes down to mathematical relations.
  • Juan Pablo Califano
    Juan Pablo Califano about 15 years
    " 0xFF... is not good because it depends on the width of the type" Which is the only sane way to go, I think. You're supposed to define clearly what each flag/bit means in your program. So, if you define that you're using the lowest 32 bits to store flags, you should restrict yourself to use those 32 bits, whether the actual size of the int is 32 or 64.
  • Johannes Schaub - litb
    Johannes Schaub - litb about 15 years
    there is an ambiguity. -1 is not 0xFFFFFFFF. But -1 is 0xFFFFFFFF if converted to an unsigned int (having 32 bits). That's what is making this discussion so difficult i think. Many people have very different things in mind when they talk about those bit strings.
  • Drew Hall
    Drew Hall about 15 years
    How does ~0 (i.e. the one's complement operator) rely on two's complement representation?
  • mrkurtan
    mrkurtan about 15 years
    You have this backwards. Its setting flags to -1 that relies on a twos complement representation. In a sign+magnitude representation minus one only has two bits set: the sign bit and the least significant bit of the magnitude.
  • Dingo
    Dingo about 15 years
    The C standard requires that int value of zero has its sign bit and all value bits are zero. After the one's complement, all those bits are one. The values of an int with all bits set are: Sign-and-magnitude: INT_MIN One's complement: -0 Two's complement: -1 So the statement "unsigned int flags = ~0;" will assign whichever value above matches the platform's integer representation. But the '-1' of two's complement is the only one that will set all the flags bits to one.
  • Dingo
    Dingo about 15 years
    @Stephen: Agreed on the representation. But when an int value is assigned to an unsigned int, the unsigned doesn't get its value by adopting the int value's internal representation (except in two's-complement systems where that generally works). All values assigned to unsigned int's are modulo (UINT_MAX + 1), so assigning -1 works no matter the internal representation.
  • Ankit Roy
    Ankit Roy about 15 years
    @Stephen: dingoatemydonut's right -- surprisingly enough, C++ guarantees this, it's in section 4.7, paragraph 2 of the ISO standard.
  • Christoph
    Christoph almost 15 years
    the constants you suggested are actually defined in limits.h - stdint.h contains the limits for the additional integers types (fixed-sized integers, intptr_t,...)
  • Christoph
    Christoph almost 15 years
    instead of -1, you could also use UINTMAX_MAX from stdint.h as a type-agnostic initialisation value; but one would assume that the programmer knows the number of significant bits of the flags variable, so there's nothing wrong with 0xff... either
  • Johannes Schaub - litb
    Johannes Schaub - litb almost 15 years
    Sometimes, it's convenient to use the highest value for something special. Like std::string::npos, which is defined as static_cast<size_t>(-1) . The number of bits is not necessarily needed in such cases.
  • Christoph
    Christoph almost 15 years
    @litb: casting -1 is certainly a nice way to get maximal unsigned values, but it's not really descriptive; that's the reason why the _MAX constants exist (SIZE_MAX was added in C99); granted, the C++ version numeric_limits<size_t>::max() is a bit long-winded, but so is the cast...
  • MSalters
    MSalters almost 15 years
    There are a number of cases in which you don't care that much, but they're rare. E.g. algorithms that work on datasets of N bits by breaking it down into chunks of sizeof(unsigned)*CHAR_BIT bits each.
  • James Hopkin
    James Hopkin almost 15 years
    I think this answer's right in practice, but I can't see an absolute guarantee in the Standard that 2^N - 1 is all bits set for an unsigned type. I'm adding an answer to that effect.
  • Johannes Schaub - litb
    Johannes Schaub - litb almost 15 years
    In general, we can't be sure about the value of the padding bits. And if we want, then we could be in danger since we could generate a trap representation for them (and it could raise signals). However, the std requires unsigned char to have no padding bits, and in 4.7/2 in the c++ standard says that converting an integer to an unsigned type, the value of the resulting unsigned variable is the smallest value congruent to the source integer value, (modulo 2^n, n==number-of-bits in the unsigned type). then (-1)==((2^n)-1) (mod 2^n) . 2^n-1 has all bits set in a pure binary numbering system.
  • Johannes Schaub - litb
    Johannes Schaub - litb almost 15 years
    If we really want to have all bits 1 in the object representation of an unsigned type, we would need memset. But we could generate a trap representation thereby :( Anyway, an implementation probably has no reason to throw a bit away of their unsigned integers so it will use it to store its values. But you have got a very good point - there is nothing stopping an interpretation from having a few silly nonsense bits, i think (apart of in char/signed char/unsigned char, which must not have those). +1 of course :)
  • James Hopkin
    James Hopkin almost 15 years
    The Standard seems to pretty clearly have '2’s complement, 1’s complement and signed magnitude' representations in mind, but doesn't want to rule anything out. Interesting point about trapping representations too. As far as I can tell, the bit I quoted is the definition of 'pure binary numeration system' as far as the Standard is concerned - the 'except' bit at the end is really my only doubt over whether casting -1 is guaranteed to work.
  • Saurabh
    Saurabh almost 15 years
    Beware of ones' complement representation (seen in a few obscure DSP chips.)
  • Admin
    Admin over 14 years
    You can mitigate the wordiness with the standard UINT_MAX macro, since you're hardcoding the type unsigned int anyway.
  • mpen
    mpen over 13 years
    "We are not talking about whether -1 has all bits one (it doesn't always have). And we're not talking about whether ~0 has all bits one (it has, of course)." -- whaat??? I thought the whole point was to set all bits to 1. That's how flags work..no?? You look at the bits. Who cares about the value?
  • Donal Fellows
    Donal Fellows over 13 years
    FWIW, if I mean “all 1 bits” I use ~(type)0 (well, fill in the right type of course). Casting zero still results in a zero, so that's clear, and negating all the bits in the target type is pretty clearly defined. It's not that often that I actually want that operation though; YMMV.
  • mpen
    mpen over 13 years
    @Dingo: What's so important about the distinction between "all bits" and "flag bits". If I go flags=~0 and then I want to check if the first bit is set, flags&1, will that not work in all cases?
  • mpen
    mpen over 13 years
    +1. Even if the size of the datatype is bigger than the # of F's (i.e., you didn't quite set all the bits to true), since you're explicitly setting the value, you are at least aware of which bits are "safe to use"..
  • Johannes Schaub - litb
    Johannes Schaub - litb over 13 years
    @Mark the questioner cares. He asks "Is it safe to use -1 to set all bits to true". This does not ask about what bits -1 is represented by, nor does it ask what bits ~0 has. We may not care about values, but the compiler does. We can't ignore the fact that operations work with and by values. The value of ~0 may not be -1, but this is the value you need. See my answer and @Dingo's summary.
  • R.. GitHub STOP HELPING ICE
    R.. GitHub STOP HELPING ICE over 13 years
    @Donal, that surely works, but requires knowing the type and writing it out directly. Perhaps this would be better: var=~(0*var); since it won't break if the type changes. I still prefer -1.
  • Dingo
    Dingo over 13 years
    @Mark There is no intended distinction between "all bits" and "all the flag bits". They are the same. The expression "flags = ~0" will not always work if flags is unsigned. The simplest example is on a one's-complement platform. On such a platform "~x == -x" is true for signed integers. Therefore, "~0 == -0" is true. On that platform, "flags = ~0" is the same as "flags = -0", which in turn is the same as "flags = 0". So on a one's-complement platform "flags = ~0" results in no bits being set.
  • mpen
    mpen over 13 years
    @Dingo: Oh...that clarifies it.. but also makes no sense at all. Not quite sure how "~0 == 0 == no bits set".. that just sounds like an error. The ~ is supposed to invert the bits.. it seems like it's failing to do so??
  • mpen
    mpen over 13 years
    @Johannes: Well.. why is the "highest value" guaranteed to have "all bit set" then?
  • Donal Fellows
    Donal Fellows over 13 years
    -1 has the assumption that you're working with twos-complement arithmetic. Almost everything does nowadays, but not all.
  • R.. GitHub STOP HELPING ICE
    R.. GitHub STOP HELPING ICE over 13 years
    @Donal: you are simply wrong. C specifies that, when converting a value that does not fit into an unsigned type, the values is reduced modulo 2^n where n is the number of bits in destination type. This applies both to signed values and larger unsigned types. It has nothing to do with twos complement.
  • Johannes Schaub - litb
    Johannes Schaub - litb over 13 years
    @Mark because the standard integers are using a pure binary system for counting. If you always count up.. you end at all bits 1 some day :) You can argue an implementation could define the highest unsigned number unequal to 2**n - 1, but then such an impl would have a problem with implementing the conversion of -1 to unsigned, which precisely is required to result in a value of 2**n - 1 with n being the amount of bits in the unsigned type.
  • Donal Fellows
    Donal Fellows over 13 years
    @R..: You've described what happens with twos-complement architectures (the overwhelmingly most common) but there's also ones-complement and sign-value, and to expect that those implement sign/bit conversions the same is just unrealistic, no matter what the standard says. After all, it's “just a standard” and compliance is not usually valued as much as supporting old code.
  • Donal Fellows
    Donal Fellows over 13 years
    And as noted, I use the form from earlier in this exchange. That's not because it is shorter, but because it says “this is working with bits” to me whereas -1 says “this is working with the value”.
  • R.. GitHub STOP HELPING ICE
    R.. GitHub STOP HELPING ICE over 13 years
    They do implement it the same, which is trivial; you just use an unsigned subtraction opcode instead of a signed one or a neg instruction. Machines which have bogus signed arithmetic behavior have separate signed/unsigned arithmetic opcodes. Of course a really good compiler would just always ignore the signed opcodes even for signed values and thereby get twos-complement for free.
  • Steve Jessop
    Steve Jessop over 13 years
    @Mark: you're confusing two operations. ~0 yields an int value with all bits set, of course. But assigning an int to an unsigned int does not necessarily result in the unsigned int having the same bit pattern as the signed bit pattern. Only with a 2's complement representation is this always the case. On a 1s' complement or sign-magnitude representation, assigning a negative int value to an unsigned int results in a different bit pattern. This is because the C++ standard defines signed -> unsigned conversion to be the modulo-equal value, not the value with the same bits.
  • Konrad Rudolph
    Konrad Rudolph over 13 years
    Two comments: first, you are dead wrong about the size of integers on “most” machines. Secondly, ur txt iz very hard 2 read due 2 ur us of sum kind of seckrit language. Plz us plain English.
  • Johannes Schaub - litb
    Johannes Schaub - litb over 13 years
    Someone posted this answer on twitter yesterday. It was binary day yesterday, date 101010 . I guess it will be more fun next year at 111111 HAHAHA.
  • caf
    caf over 13 years
    @R.: The var = ~(0*var) case will fail for var being an unsigned type narrower than int. Perhaps var = ~(0U*var) ? (personally I still prefer -1, though).
  • Donotalo
    Donotalo almost 13 years
    I still have confusion on why -1 is the portable way. consider -1 on an 1's complement system, having 4 bit width of integer. So, 1 = 0001b, -1 = 1110b. unsigned int flags = -1 should assign 1110b to flags, isn't it?
  • supercat
    supercat almost 13 years
    @Donotalo: On a one's-complement system, typecasting a negative signed int to an unsigned int does not simply reinterpret the same bit pattern, but instead generates whatever bit pattern would correspond to (integer value + UNSIGNED_MAX).
  • Donotalo
    Donotalo almost 13 years
    @supercat: so for unsigned int flags = -1 the variable flags will contain 1110b + 1111b = 0001b? what am i missing?
  • supercat
    supercat almost 13 years
    @Donotalo: What you are missing is that the rules for casting a signed type to an unsigned type require that regardless of the bit representation -1 will turn into the value which, when added to +1, will yield zero, i.e. the maximum value for the unsigned type. Note that if has a signed variable holding -1 and one accesses the memory uses a (unsigned char*), there's no guarantee that all the unsigned char's comprising the variable will have all their bits set. The guarantee that (unsigned whatever)(-1) has all the bits set only applies with explicit casts, not with memory overlays.
  • Adrian McCarthy
    Adrian McCarthy almost 13 years
    This answer is much clearer than Johannes Schaub's. However, assigning a negative literal integer to an unsigned type without a cast typically results in a compiler warning. Suppressing the warning requires a cast, which means to have to pay attention to the target type anyway, so you may as well use UINT_MAX or ULONG_MAX and be clear rather than relying on a tiny detail in the standard the clearly confuses many of your fellow programmers.
  • R.. GitHub STOP HELPING ICE
    R.. GitHub STOP HELPING ICE almost 13 years
    Typically? gcc -Wall does not generate a warning for this last I checked; you have to add extra warning options. Surely you can use UINT_MAX etc. if you know the type, but the whole point of the problem at hand is to set all bits to 1 when the maximum value is not already known.
  • Johannes Schaub - litb
    Johannes Schaub - litb almost 13 years
    @Adrian can you provide a quote of me doing so? No, you can always do unsigned int flags = static_cast<unsigned int>(-1); if your compiler is too picky. Nothing needs to be disabled.
  • Adrian McCarthy
    Adrian McCarthy almost 12 years
    @JohannesSchaub-litb: You said to do it exactly as posed in the question, which does not have the cast and thus requires disabling a compiler warning.
  • Johannes Schaub - litb
    Johannes Schaub - litb almost 12 years
    @AdrianMcCarthy casts should be avoided where possible but to disable compiler warnings isn't necessarily a good thing either. The latter depends on how fine you can control the warning of your compiler. The cast introduces said dependency in your code (unsigned long x = (unsigned int)-1; - oops!). So regarding the code itself, omitting the cast definitely is the best you can do. I don't feel experienced enough with the various compilers out there to put out a statement wrt their warnings and flags.
  • David Stone
    David Stone over 11 years
    But then you lose the ability to declare flags as const.
  • tc.
    tc. over 11 years
    Congratulations, you've found a compiler bug!
  • tc.
    tc. over 11 years
    Not on systems with 9-bit chars!
  • Nathan Fellman
    Nathan Fellman over 11 years
    Has this been documented anywhere as a bug?
  • tc.
    tc. over 11 years
    Assuming UINT64_C(0xffffffff) expands to something like 0xffffffffuLL, it is definitely a compiler bug. The C standard largely discusses values, the value represented by 0xffffffff is 4294967295 (not 36893488147419103231), and there aren't any conversions to signed integer types in sight.
  • Adrian McCarthy
    Adrian McCarthy over 11 years
    @JohannesSchaub-litb: With C++11, you can use auto to avoid the potential for incorrect casting by avoiding the duplication of the type (see my answer). The result is correct, portable, warning-free, and obvious to other programmers. The confusion demonstrated throughout this question shows that the -1 trick is not obvious to many programmers and thus a dangerous idiom to use in your code.
  • Admin
    Admin over 11 years
    @DavidStone unsigned int const flags = ~0u;
  • David Stone
    David Stone over 11 years
    @Zoidberg'-- That fails to work on systems other than two's complement. For instance, on a sign-magnitude system, ~0 is an integer that has all bits set to 1, but when you then assign that int to the unsigned variable flags, you perform a value conversion from -2**31 (assuming a 32-bit int) to (-2**31 % 2**32) == 2**31, which is an integer with all bits but the first set to 1.
  • David Stone
    David Stone over 11 years
    @Macke You can avoid stating the type in C++11 with auto. auto const flags = std::numeric_limit<unsigned>::max().
  • David Stone
    David Stone over 11 years
    That is also a reason why this answer is dangerous. It seems as though @Zoidberg'--'s answer would be identical, but it actually isn't. However, as a person reading the code, I would have to think about it to understand why you took two steps to initialize it, and possibly be tempted to change that to a single step.
  • Admin
    Admin over 11 years
    @DavidStone ~0u is an unsigned int, not an int.
  • David Stone
    David Stone over 11 years
    Ah yes, I didn't notice the u suffix in your answer. That would of course work, but still has the problem of specifying the data type you use (unsigned and no larger) twice, which could lead to errors. The error is most likely to show up if the assignment and initial variable declaration are farther apart, though.
  • David G
    David G about 11 years
    I still don't quite get why you are recommending the use of -1 because on one's complement machines -1 is 11111110 (not all 1s). Only in 2's complement is it all 1s. So why are you recommending it?
  • gx_
    gx_ almost 11 years
    @0x499602D2 As litb already explained, signed->unsigned conversion does not involve bit representations, it involves numeric values (with "modulo N" arithmetic). After unsigned int a = -1; the value of a is equal to "-1 modulo UINT_MAX+1" (positive remainder), which is here (UINT_MAX + 1) - 1 i.e. UINT_MAX, whose bit representation is all 1s (even on one's complement machines).
  • David G
    David G almost 11 years
    @gx_ Thank you for that, but do you know why I am getting a runtime error for this program? It only does a runtime error when I do UINT_MAX + 1. Why is that? Why doesn't it just overflow and go back to 0?
  • gx_
    gx_ almost 11 years
    @0x499602D2 You don't have a runtime error, you just don't flush cout and nothing gets printed. Add a << endl (or a << flush) and it works. [Indeed UINT_MAX + 1 overflows and gives 0 because UINT_MAX expands to a literal of type unsigned int (e.g. for 32 bits, 4294967295u). The wrap-around overflow is well-defined though. But my previous comment used "UINT_MAX+1" rather as a "mathematical value" (e.g. for 32 bits, the value 4294967296, even if 4294967296 wouldn't fit in an unsigned int).]
  • David G
    David G almost 11 years
    @gx_ Why would cout need flushing in this context? I've never seen a case like this...
  • gx_
    gx_ almost 11 years
    @0x499602D2 Strange indeed: cout << 0; vs cout << 1;. Ideone seems a little inconsistent here... but we're getting off topic :) The whole point was that converting -1 to an unsigned integral type (this conversion is done implicitly for the above assignment) yields the maximum value of the right type ("UXXX_MAX", or std::numeric_limits< unsigned type >::max()), whose bit-representation happens to be all-ones, which was the desired result.
  • Filipe Gonçalves
    Filipe Gonçalves about 10 years
    @JohannesSchaub-litb C99 in section 6.3.1.3, "signed and unsigned integers" says that if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type. I understand that -1 works if addition is used, but what if if subtracts? I mean, -1 - (UXX_MAX+1) will never be in the range of an unsigned type. Is this an inconsistency in the standard? Do you know if C++ has the same rule?
  • Johannes Schaub - litb
    Johannes Schaub - litb about 10 years
    @FilipeGonçalves well there's the other case where the initial value is too high, then it will subtract. So UXX_MAX+2 will end up as 1.
  • jforberg
    jforberg over 8 years
    As a C programmer, code like this gives me bad dreams at night.
  • phuclv
    phuclv over 5 years
    unsigned int flags = ~0 isn't portable but unsigned int flags = ~0U is portable, and expresses the intent clearer
  • Peter Cordes
    Peter Cordes almost 5 years
    And what happens when you use this in a context like ALL_BITS_TRUE ^ a where a is a signed integer? The type stays signed integer and the bit-pattern (object representation) depends on the target being 2's complement or not.
  • Diamond Python
    Diamond Python almost 5 years
    No, ALL_BITS_TRUE ^ a gives a compile error because ALL_BITS_TRUE is ambiguous. It could be used like uint32_t(ALL_BITS_TRUE) ^ a, however. You can try it yourself on cpp.sh :) Nowadays I'd add a static_assert(std::is_unsigned<UnsignedType>::value, "This is designed only for unsigned types"); in the operator to be sure users don't try to use int(ALL_BITS_TRUE). I'll update the answer.
  • Secundi
    Secundi about 3 years
    +1 for providing the best portable, more explicit and better readable alternative IMO: ~static_cast<T>(0)
  • Secundi
    Secundi about 3 years
    "Leveraging on the fact that assigning all bits to one for an unsigned type is equivalent to taking the maximum possible value for the given type" Maybe I'm a bit pedantic here, but the gain of clearness might be questioned here due to the points Johannes mentioned: value vs bit constellation/representation. Someone not quite familiar with bit representation of unsigned integral values (padding bits...) and the standard might have to think twice for this aspect too, as he would have to do for the -1 approach.
  • Andrew Henle
    Andrew Henle about 3 years
    the best solution at least since C++11 in terms of standard conformity, readability, type safety/explicit clearness and reduction of possible ambiguities Readability?!?! If that's "readable", I'd hate to see unreadable.
  • Secundi
    Secundi about 3 years
    @AndrewHenle, yes, one might question that aspect but I wanted to show the best compromise with respect to all the relevant points. And readability is not just a quantity concern, but also a question about implicity. See for instance, the old C-fashioned "universal" cast style (T)variable is readable at first glance, but readable in terms what's actually going on below the surface? Things are often not that readable as they might occur at first glance in doubt... Maybe I should replace readable with explicitly understandable...
  • tstanisl
    tstanisl almost 3 years
    No. -1 works everywhere, ~0 will fail on one-complement machines