3

So this seems easy but either I'm missing something or... there is no way to do it?..

First try:

unsigned abs(int value) { return value < 0 ? -value : value; }

Nope, "-value" is an UB. This is detected by clang -fsanitize and is generally unsafe in the face of aggressive optimizations (although I really hope no sane compiler abuses this).

Ok. Let's cast to unsigned!

unsigned abs(int value) { return value < 0 ? -unsigned(value) : unsigned(value); }

Nope, this results in C4146 warning in MSVC. Additionally due to definition of unary minus on unsigned values I think this assumes a two-complement integer format for signed integers.

Ok...

unsigned abs(int value) { return value < 0 ? ~unsigned(value) + 1 : unsigned(value); }

This does not seem to generate any warnings or undefined behaviors, but of course only works for two-complement integers. Also, it's kinda obscure - needs a comment that explains why this is not using something straightforward...

Is it actually possible to implement an aforementioned function without triggering UBs or relying on the integer representation? Please tell me the answer is "yes" before I lose all remaining hope in C.

zeuxcg
  • 9,216
  • 1
  • 26
  • 33
  • Are you converting signed integer to unsigned integer or do you need an `abs` function taking `unsigned int` as parameter? – Thomas Matthews Sep 20 '15 at 06:29
  • How about 0-val instead of -val? `unsigned abs(int value) { return value < 0 ? 0-value : value; }` – Alex Lop. Sep 20 '15 at 06:34
  • 3
    Since `INT_MIN` is an edge case, you could just handle it explicitly. That makes it clear you handle the edge case. – chris Sep 20 '15 at 06:34
  • Wait, so you aren't happy with `-value` because -INT_MIN is (in unlimited precision math) larger than INT_MAX (and so gives UB). But in that case, what DO you want to be the result of abs(INT_MIN) ? IMO you should just take the first option and accept that `abs(INT_MIN)` is UB, or you should throw an exception of some kind. – Chris Beck Sep 20 '15 at 06:35
  • @ChrisBeck, Since `unsigned` can hold it, `-INT_MIN` (mathematically speaking) should presumably be the result. – chris Sep 20 '15 at 06:36
  • @AlexLop. I think this solves my problem! Well, not *exactly* that, but value < 0 ? 0u - unsigned(value) : unsigned(value); seems to work fine - it avoids MSVC warning and is correct AFAICS. – zeuxcg Sep 20 '15 at 06:37
  • ah I see, you are casting to unsigned. (missed that before). Yeah why don't you just explicitly handle INT_MIN then, like @chris suggested. – Chris Beck Sep 20 '15 at 06:37
  • Explicitly handling INT_MIN does not seem better than ~unsigned(value) + 1 - the latter is faster and both imply 2-complement I think. – zeuxcg Sep 20 '15 at 06:38
  • @zeuxcg I think that even without the casting it would do the job. The conversion from signed to unsigned is done automatically by the compiler (if I remember correctly) – Alex Lop. Sep 20 '15 at 06:39
  • @zeuxcg: is there anything in your question not solved by the solution proposed in the possible duplicate link? – Chris Beck Sep 20 '15 at 06:40
  • @zeuxcg, In that case, how about `static_cast(-static_cast(value))`? You could still argue that `sizeof(int) == sizeof(long long)` could be true I guess. – chris Sep 20 '15 at 06:41
  • Yes, solution from the duplicate link is exactly the same as my solution #2 - it produces a warning on MSVC, as mentioned in the question. – zeuxcg Sep 20 '15 at 06:41
  • @zeuxcg: What is the exact warning produced? – Chris Beck Sep 20 '15 at 06:42
  • @chris Yeah, that works, but my *original* problem *also* involves unsigned long long abs(long long) value :) – zeuxcg Sep 20 '15 at 06:42
  • @ChrisBeck C4146, as mentioned in the question. – zeuxcg Sep 20 '15 at 06:42
  • 1
    @n.m., NO! Please read the question and the documentation for std::abs more carefully. – zeuxcg Sep 20 '15 at 06:43
  • @zeuxcg: why don't you just do `0-unsigned(-value))` then. – Chris Beck Sep 20 '15 at 06:43
  • @ChrisBeck yeah, that works as mentioned somewhere above in the comment stream; I did not think of this solution. (presumably you mean unsigned(value)) – zeuxcg Sep 20 '15 at 06:44
  • i think it's very slightly different from what appeared earlier. the standard completely defines unsigned arithmetic, and defines the conversion of negative signed to unsigned the way you want. explicitly ocnverting to unsigned before `0-` is important, and avoiding `~` is also a good thing. the "real answer" is `-unsigned(value)` like in the original thing that this is a duplicate of. Adding `0` before minus to silence msvc warning is the only thing that's new here. – Chris Beck Sep 20 '15 at 06:45
  • @ChrisBeck Yeah, I'm referring to http://stackoverflow.com/questions/32676417/how-to-implement-unsigned-absint?noredirect=1#comment53198175_32676417 – zeuxcg Sep 20 '15 at 06:46
  • @zeuxcg oops... sorry, you are right. – n. m. could be an AI Sep 20 '15 at 06:56
  • @zeuxcg - have a look into it. sure it will help [Bit Twiddling Hacks](http://graphics.stanford.edu/~seander/bithacks.html#IntegerAbs) look for 'Compute the integer absolute value (abs) without branching': – Nitin Tripathi Sep 20 '15 at 07:01
  • unsigned myabs(int v) { return (v >= 0) ? (unsigned)v : (unsigned)-(v+1)+1; } – Alexey Frunze Sep 20 '15 at 09:57

2 Answers2

2

The correct approach is #2, but to silence MSVC warning a workaround needs to be applied:

unsigned abs(int value) { return value < 0 ? 0 - unsigned(value) : unsigned(value); }

The reason this is correct is that signed-to-unsigned conversion is defined to return 2^N+v for negative v; unary minus for unsigned integers is defined as 2^N-v; thus regardless of signed integer representation this code returns the absolute value properly.

zeuxcg
  • 9,216
  • 1
  • 26
  • 33
1

If -value may result in undefined behavior then this should be well defined:

unsigned abs(int value) { return value < 0 ? 0-value : value; }
Alex Lop.
  • 6,810
  • 1
  • 26
  • 45