13

How could I extract the absolute value of INT_MIN without overflowing? See this code for the problem:

#include <limits.h>
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    printf("INT_MAX: %d\n", INT_MAX);
    printf("INT_MIN: %d\n", INT_MIN);
    printf("abs(INT_MIN): %d\n", abs(INT_MIN));

    return 0;
}

Spits out the following

INT_MAX: 2147483647
INT_MIN: -2147483648
abs(INT_MIN): -2147483648

I need this for a check if an int value is greater than zero.

As for this question being a duplicate of Why the absolute value of the max negative integer -2147483648 is still -2147483648?, I have to disagree, since this is a HOW, not a WHY question.

Community
  • 1
  • 1
Morgan Wilde
  • 16,795
  • 10
  • 53
  • 99
  • 5
    The absolute value of the smallest number in two-complement just does not exist. Describe what you want to achieve more in detail, i´m sure there is another way. – deviantfan Mar 08 '14 at 11:39
  • where is the function prototype of `abs`? – ajay Mar 08 '14 at 11:41
  • 3
    Have a look at this post : http://stackoverflow.com/questions/11243014/why-the-absolute-value-of-the-max-negative-integer-2147483648-is-still-2147483 – Junior Dussouillez Mar 08 '14 at 11:42
  • Just do not operate at the boundaries - Weird and unusual things happen – Ed Heal Mar 08 '14 at 11:49
  • Describe what you mean by "extract the absolute value"? Do you mean print the absolute value of INT_MIN or do you mean to store the absolute value of INT_MIN in a particular data type – Brandin Mar 08 '14 at 12:03
  • @Brandin I mean **storing** the value somewhere. And the storage type I picked now is an `unsigned int` or any other unsigned version. – Morgan Wilde Mar 08 '14 at 12:06
  • @MorganWilde Read the top comment on this. How could one possibly store the absolute value of INT_MIN in an `unsigned` in 2s complement – Brandin Mar 08 '14 at 12:09
  • @Brandin: Of course it is possible to store the absolute value of `INT_MIN` in an `unsigned int`... – Oliver Charlesworth Mar 08 '14 at 12:40
  • @OliCharlesworth: nope: as per §6.2.6.2¶2, an unsigned type can have the same number of value bits as its corresponding signed type. – ninjalj Mar 08 '14 at 12:43
  • 1
    @ninjalj: True, but how many practical implementations of C can you name where this doesn't hold? – Oliver Charlesworth Mar 08 '14 at 12:55
  • @OliCharlesworth: well, I guess you could always `#error` out if `INT_MAX` equals `UINT_MAX` and still be conforming and not invoke UB. – ninjalj Mar 08 '14 at 13:02
  • "need this for a check if an int value is greater than zero". Rather than find the `absolute_value(int x)`, find the `negative_absolute_value(int x)`. Then one can `int x; if (negative_absolute_value(x) < -limit) Handle_int_magnitude_greater_than_limit(x)`. – chux - Reinstate Monica Mar 08 '14 at 18:43
  • @chux I think you should post this as an answer and elaborate, because it is not clear atm how is this a legitimate solution. – Morgan Wilde Mar 08 '14 at 20:23
  • @Morgan Wilde As the post is marked duplicate, I cannot post an answer. The main idea is to solve the _higher_ level problem and get at the _need_ you have for an absolute_value function. Many such problems can be solved with a negative_absolute_value function with does not have the problem as certainly `|INT_MIN|` >= `INT_MAX`. Example [int_to_IEEEfloat(integer x) See note 2](http://stackoverflow.com/questions/19529356/how-to-convert-an-unsigned-int-to-a-float/19531528#19531528) – chux - Reinstate Monica Mar 08 '14 at 20:35

6 Answers6

5

The %d conversion specifier in the format string of printf converts the corresponding argument to a signed decimal integer, which in this case, overflows for the int type. C standard specifically mentions that signed integer overflow is undefined behaviour. What you should do is to use %u in the format string. Also, you need to include the headers stdio.h and stdlib.h for the prototype of the functions printf and abs respectively.

#include <limits.h>
#include <stdio.h>
#include <stdlib.h>

// This solves the issue of using the standard abs() function
unsigned int absu(int value) {
    return (value < 0) ? -((unsigned int)value) : (unsigned int)value;
}

int main(void) {
    printf("INT_MAX: %d\n", INT_MAX);
    printf("INT_MIN: %d\n", INT_MIN);
    printf("absu(INT_MIN): %u\n", absu(INT_MIN));

    return 0;
}

This gives the output on my 32-bit machine:

INT_MAX: 2147483647
INT_MIN: -2147483648
absu(INT_MIN): 2147483648
samoz
  • 56,849
  • 55
  • 141
  • 195
ajay
  • 9,402
  • 8
  • 44
  • 71
  • Nice, it seems so obvious now. Thanks! – Morgan Wilde Mar 08 '14 at 11:54
  • 4
    Is this actually correct or does it happen to work due to implementation-defined behaviour – Brandin Mar 08 '14 at 11:58
  • 11
    Note this still invokes UB; from the C99 standard for `abs`: "*if the result cannot be represented, the behaviour is undefined.*" – Oliver Charlesworth Mar 08 '14 at 11:58
  • actually: for this you don't need to call abs(INT_MIN) anymore, as just INT_MIN will cause the same result. As found in various locations e.g. like http://www.acm.uiuc.edu/webmonkeys/book/c_guide/2.13.html#abs calling abs on INT_MIN causes undefined behaviour! – xmoex Mar 08 '14 at 12:00
  • @OliCharlesworth the man page of `abs` says - `Returns the absolute value of the integer argument, of the appropriate integer type for the function.` Doesn't it mean that return value of `abs` will always fit in `unsigned int` type. – ajay Mar 08 '14 at 12:03
  • 1
    @ajay: Alas no; the return type of `abs` is `int`. – Oliver Charlesworth Mar 08 '14 at 12:03
  • @OliCharlesworth I wrote my own `absu` that returns `unsigned int` and thus this answer solves my problem. – Morgan Wilde Mar 08 '14 at 12:04
  • @MorganWilde: Ok, but then the definition of that function is the real answer; you should post it! (The code in this answer gives UB, so I have to downvote it...) – Oliver Charlesworth Mar 08 '14 at 12:05
  • @OliCharlesworth I've included my function in this answer, I think it should be sufficient. – Morgan Wilde Mar 08 '14 at 12:10
  • @MorganWilde: Alas, that still exhibits UB. `-value` is still of type `int`, and thus overflows for `INT_MIN`. – Oliver Charlesworth Mar 08 '14 at 12:10
  • 1
    Wouldn't a simpler solution be just to convert the value of INT_MIN into a string representation and then take off the minus sign – Brandin Mar 08 '14 at 12:11
  • @MorganWilde: when using your absu function you can get comparison problems when using it with a signed integer. found: http://stackoverflow.com/a/5416498/944201 – xmoex Mar 08 '14 at 12:14
  • @xmoex would typecasting values before comparing be enough to solve this one? – Morgan Wilde Mar 08 '14 at 12:16
  • @MorganWilde: typecasting before comparing wouldn't be enough. But I suspect typecasting to unsigned the third operand of the conditional expression instead of negating it may be legal. Forget that, I'm not sure unsigned types are required to have more value types than their corresponding signed type. – ninjalj Mar 08 '14 at 12:24
  • if possible (and necessary) i'd cast both values needed for comparison to the next greater signed variant of integer typ like long int (or long long int if int and long is the same size on your machine). this way you occupy more memory but your solution is more generic as you can use one of the provided abs functions for long int or long long int without messing around with lower quality hack-arounds and you'd do a real comparison of two signed values... you could check my answer below if you like. – xmoex Mar 08 '14 at 12:26
  • @ninjalj here we go! This seems like the perfect solution. – Morgan Wilde Mar 08 '14 at 12:30
  • @MorganWilde: Unfortunately, unsigned types may have a padding bit that corresponds to the sign bit of the signed type. So, the value may be not representable as unsigned. – ninjalj Mar 08 '14 at 12:33
  • @MorganWilde I dont see the solution because as it stands you have described three different problems (displaying the absolute value of INT_MIN, storing the absolute value of INT_MIN in an `unsigned` (impossible) and comparing values involving INT_MIN). Choose a problem first and focus on that. – Brandin Mar 08 '14 at 12:34
  • 2
    Everyone commenting here needs to go to the very top and read this comment: `The absolute value of the smallest number in two-complement just does not exist.` from deviantfan. Trying to cast INT_MIN into unsigned just isnt representable. As in that comment you need to describe what youre actually trying to accomplish here – Brandin Mar 08 '14 at 12:36
  • @Brandin OK, great comment, I'll go back to the drawing board and try to distill the issue. Thanks for the effort. – Morgan Wilde Mar 08 '14 at 12:42
  • @Brandin If `|int|` will fit into an `unsigned`, suggest `unsigned absu(int value) { return (value < 0) ? ((unsigned)(-1 - value) + 1u : (unsigned)value; }` to avoid UB. – chux - Reinstate Monica Mar 08 '14 at 18:33
  • @chux Your comment seems clever but I stopped reading right after `If |int| will fit into an unsigned` (as mentioned it wont in 2s complement) – Brandin Mar 09 '14 at 11:25
  • @Brandin: While the Standard would allow an implementation to use two's-complement format for signed types, and have an extra padding bits in unsigned, I really doubt that any do. Otherwise, the extra bit in an unsigned type would allow it to accommodate values up to `2*INT_MAX+1`, which is more than enough range to handle `|-INT_MIN|`. – supercat Oct 17 '18 at 16:09
  • 1
    @chux: As a simplification, `(value < 0) ? -(unsigned)value : (unsigned)value`. Casting to the unsigned type *before* the negation would result in fully-defined behavior with the only possible anomaly occurring on a machine where e.g. `int` is 17 bits two's-complement with asymmetric range and `unsigned` is 16 bits + 1 padding. On such machines, `INT_MIN` would be -65536, but `UINT_MAX` would only be 65535. The behavior of converting `INT_MIN` to `unsigned` would be *defined* as yielding 0u, and -0u is of course 0u. – supercat Oct 17 '18 at 16:13
  • @supercat Yes with `|int|` in range of `unsigned`, [your](https://stackoverflow.com/questions/22268815/absolute-value-of-int-min/22268963?noredirect=1#comment92633323_22268963) `(value < 0) ? -(unsigned)value : (unsigned)value` is a nice well defined alternative to [this](https://stackoverflow.com/questions/22268815/absolute-value-of-int-min/22268963?noredirect=1#comment33833999_22268963). Note that given C' specs, a `int negative_abs(int)` is a full `int` range alternative to folding via `abs()`. – chux - Reinstate Monica Oct 17 '18 at 16:31
  • @supercat The case where `signed_type_MAX == unsigned_type_MAX` and not `signed_type_MAX == unsigned_type_MAX/2` I have encounter once, pre-C99 on a platform with a 64-bit signed *,/,+,-, opcodes but not unsigned counterparts. There existed a wide integer pair types as 64-bit 2's complement and (63-bit unsigned + 1 pad). AFAIK, it was C compliant - at the time. I could imagine a like-wise case today with n==128. – chux - Reinstate Monica Oct 17 '18 at 16:47
  • @chux: I would not find it surprising for sign-magnitude or ones'-complement implementations to treat unsigned types as signed with the sign bit forced to zero, but it would seem weird for a two's-complement platform to do so. Was the system you were talking about two's-complement? – supercat Oct 18 '18 at 18:40
  • Yes. ......... iirc – chux - Reinstate Monica Oct 19 '18 at 18:22
3

How about

printf ("abs(INT_MIN) = %ld", -((long int) INT_MIN));

Or if your long is not longer than an int:

printf ("abs(INT_MIN) = %lld", -((long long int) INT_MIN));

Or if you are prepared to accept that abs(INT_MIN) is always INT_MAX + 1:

printf ("abs(INT_MIN) = %u", ((unsigned int) INT_MAX ) + 1 );
abligh
  • 24,573
  • 4
  • 47
  • 84
  • 6
    That assumes that sizeof(long) > sizeof(int), which isn't always the case... – Oliver Charlesworth Mar 08 '14 at 11:47
  • I had a similar idea, but what happens when I need the `abs()` of `LONG LONG`? – Morgan Wilde Mar 08 '14 at 11:47
  • @OliCharlesworth: In which case use `long long int` (edited to fix this). – abligh Mar 08 '14 at 11:48
  • @abligh ok, this works on some level, so I'll upvote, but this isn't a full-proof solution. – Morgan Wilde Mar 08 '14 at 11:49
  • @MorganWilde no 2's complement signed integer type is going to be able to represent the the absolute value of the largest negative type. – abligh Mar 08 '14 at 11:50
  • That's exactly what I meant when I said it isn't a full solution. – Morgan Wilde Mar 08 '14 at 11:53
  • @MorganWilde: What I'm saying is that provably you cannot have a full solution. Let's say the platform has no integer type longer than 32 bits, and you are using the longest type (and trying to find the absolute value of the smallest integer). Then `abs(INT_MIN)` will be `2147483648`, but that will be one larger than the `INT_MAX` which is by definition the largest integer that the largest 2's complement type (by assumption) can help. – abligh Mar 08 '14 at 11:58
2

There is no portable way to extract the absolute value of the most negative number as an integer. The ISO C standard says (§6.2.6.2¶2):

Each bit that is a value bit shall have the same value as the same bit in the object representation of the corresponding unsigned type (if there are M value bits in the signed type and N in the unsigned type, then M ≤ N ).

Notice it uses ≤, not <.

Since the sign bit in 2's complement has the value −(2M), and each value bit has a value that is a power of two between 1 and 2M-1, there is no way an unsigned integer on implementations where M=N can represent 2N, it can only represent up to 2N-1 = 1+2+...+2N-1.

ninjalj
  • 42,493
  • 9
  • 106
  • 148
  • So using `unsigned int` only lets me represent one more value `2^(N-1)`? This is confusing. Isn't the range of `unsigned int` `0` to `2^(N) - 1`? The standard also says(6.2.5:9) - `The range of nonnegative values of a signed integer type is a subrange of the corresponding unsigned integer type`. So the difference in the the two ranges is only one value `2^(N-1)`? – ajay Mar 08 '14 at 13:19
  • Using unsigned doesn't guarantee you any more values. But, you can check the macros in `limits.h` `INT_MAX`, `UINT_MAX`, ... to see if your particular platform gives you an strictly larger value range of non-negative integers for unsigned integers than it does for signed integers. – ninjalj Mar 08 '14 at 13:24
  • `UINT_MAX` is `2^32 - 1` and `INT_MAX` is `2^31 - 1` on my machine. I wonder why is it not guaranteed and then what is the use of using unsigned types anyway? Also supposing that `signed` and `unsigned` have the same range (except `2^(N-1)`), what value would the signed type with sign bit `1` represent in the unsigned type? – ajay Mar 08 '14 at 13:40
  • Integers can have padding bits. Some integer values may be trap representations (similar to NaNs in floating point numbers). Older machines from the 60s and 70s, and DSPs do some weird things. The C standard is written so as to support every quirky machine ever known to have run C. – ninjalj Mar 08 '14 at 13:46
  • The standard says nothing about how many padding bits are there. It also says there need not be any padding bits. Is there any way I can find out if there's one on my machine? This is really quirky and I never knew this. Trying to write `C` that runs on all machines that ever used `C` is really tough then! – ajay Mar 08 '14 at 13:53
  • You could probably tell if a platform has padding bits with `sizeof` and `CHAR_BIT`. I don't know of any modern platform that has, but probably some DSPs do. – ninjalj Mar 08 '14 at 14:18
0

In C, only the int version exists for the function int abs(int j). You can use another function labs under the header stdlib.h. Its prototype: long int labs(long int j);

#include <limits.h> 
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    printf("INT_MAX: %d\n", INT_MAX);
    printf("INT_MIN: %d\n", INT_MIN);
    printf("abs(INT_MIN): %ld\n", labs((long)INT_MIN));

    return 0;
}
xuefu
  • 61
  • 7
  • http://www.cplusplus.com/reference/climits/ says that the actual values of those limits are implementation-dependent. so this solution works only if INT_MIN is not equal to LONG_MIN – xmoex Mar 08 '14 at 12:06
0

Casting into the next available greater integer type should do it. but you have to use the correspondant abs-variant (in this case llabs(...))

printf("llabs(INT_MIN): %lld\n", llabs((long long int)INT_MIN));

edit:

you can check what's the next greater type by comparing INT_MIN to LONG_MIN and LLONG_MIN. Maybe in your case a cast to long will already do it.

printf("labs(INT_MIN): %ld\n", labs((long int)INT_MIN));

Note that the explicit casts are in fact unnecessary as the function itself would cast the argument implicitly

xmoex
  • 2,602
  • 22
  • 36
-2

First things first, you have to #include <math.h> to properly use the abs function.

Second thing is, if the only thing you want to achieve is to print the absolute value of the INT_MIN defined in limits.h, you can just print it out as an unsigned integer or as a long long integer, like this:

printf( "abs(INT_MIN): %u\n", abs( INT_MIN ) );     // %u for unsigned int
printf( "abs(INT_MIN): %lld\n", abs( INT_MIN ) );   // %lld for long long int

Since you want to have the absolute value, which most definitely will be unsigned, this should be alright.

If you don't want to include math.h, you can make it on your own like this:

// ternary implementation of the function abs
printf( "abs(INT_MIN): %u\n", ( INT_MIN > 0 ) ? INT_MIN : -INT_MIN );

If you want to use it for other purposes, then you can store the abs( INT_MIN ) value in unsigned int or long long int variables.

Utkan Gezer
  • 3,009
  • 2
  • 16
  • 29