Absolute value of INT_MIN

32,239

Solution 1

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

Solution 2

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 );

Solution 3

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.

Share:
32,239
Morgan Wilde
Author by

Morgan Wilde

JavaScript. iOS and the Web.

Updated on July 18, 2022

Comments

  • Morgan Wilde
    Morgan Wilde almost 2 years

    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.

  • Oliver Charlesworth
    Oliver Charlesworth over 10 years
    That assumes that sizeof(long) > sizeof(int), which isn't always the case...
  • Morgan Wilde
    Morgan Wilde over 10 years
    I had a similar idea, but what happens when I need the abs() of LONG LONG?
  • abligh
    abligh over 10 years
    @OliCharlesworth: In which case use long long int (edited to fix this).
  • Morgan Wilde
    Morgan Wilde over 10 years
    @abligh ok, this works on some level, so I'll upvote, but this isn't a full-proof solution.
  • abligh
    abligh over 10 years
    @MorganWilde no 2's complement signed integer type is going to be able to represent the the absolute value of the largest negative type.
  • Oliver Charlesworth
    Oliver Charlesworth over 10 years
    ( INT_MIN > 0 ) ? INT_MIN : -INT_MIN - huh?
  • Junior Dussouillez
    Junior Dussouillez over 10 years
    "you have to include math.h to properly use abs". abs is in stdlib.h...
  • Morgan Wilde
    Morgan Wilde over 10 years
    That's exactly what I meant when I said it isn't a full solution.
  • Utkan Gezer
    Utkan Gezer over 10 years
    @OliCharlesworth Whatever man, whatever... This is the exact translation; but of course, it will always be just -INT_MIN
  • Utkan Gezer
    Utkan Gezer over 10 years
    @JuniorDussouillez I didn't say that it isn't in stdlib.h, did I? It is just in both.
  • Morgan Wilde
    Morgan Wilde over 10 years
    Nice, it seems so obvious now. Thanks!
  • Oliver Charlesworth
    Oliver Charlesworth over 10 years
    @ThoAppelsin: Indeed, but -INT_MIN == INT_MIN in practice (although technically undefined behaviour).
  • Brandin
    Brandin over 10 years
    Is this actually correct or does it happen to work due to implementation-defined behaviour
  • Oliver Charlesworth
    Oliver Charlesworth over 10 years
    Note this still invokes UB; from the C99 standard for abs: "if the result cannot be represented, the behaviour is undefined."
  • abligh
    abligh over 10 years
    @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.
  • xmoex
    xmoex over 10 years
    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 acm.uiuc.edu/webmonkeys/book/c_guide/2.13.html#abs calling abs on INT_MIN causes undefined behaviour!
  • ajay
    ajay over 10 years
    @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.
  • Oliver Charlesworth
    Oliver Charlesworth over 10 years
    @ajay: Alas no; the return type of abs is int.
  • Morgan Wilde
    Morgan Wilde over 10 years
    @OliCharlesworth I wrote my own absu that returns unsigned int and thus this answer solves my problem.
  • Oliver Charlesworth
    Oliver Charlesworth over 10 years
    @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...)
  • xmoex
    xmoex over 10 years
    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
  • Morgan Wilde
    Morgan Wilde over 10 years
    @OliCharlesworth I've included my function in this answer, I think it should be sufficient.
  • Oliver Charlesworth
    Oliver Charlesworth over 10 years
    @MorganWilde: Alas, that still exhibits UB. -value is still of type int, and thus overflows for INT_MIN.
  • Brandin
    Brandin over 10 years
    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
  • xmoex
    xmoex over 10 years
    @MorganWilde: when using your absu function you can get comparison problems when using it with a signed integer. found: stackoverflow.com/a/5416498/944201
  • ninjalj
    ninjalj over 10 years
    @OliCharlesworth: that would be only for 2's complement. The standard allows signed magnitude and 1's complement, which I suspect are still used on some DSPs.
  • Morgan Wilde
    Morgan Wilde over 10 years
    @xmoex would typecasting values before comparing be enough to solve this one?
  • xuefu
    xuefu over 10 years
    yeah, thanks for your remind.
  • ninjalj
    ninjalj over 10 years
    @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.
  • xmoex
    xmoex over 10 years
    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.
  • Morgan Wilde
    Morgan Wilde over 10 years
    @ninjalj here we go! This seems like the perfect solution.
  • ninjalj
    ninjalj over 10 years
    @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.
  • Brandin
    Brandin over 10 years
    @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
    Brandin over 10 years
    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
  • Morgan Wilde
    Morgan Wilde over 10 years
    @Brandin OK, great comment, I'll go back to the drawing board and try to distill the issue. Thanks for the effort.
  • ajay
    ajay over 10 years
    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)?
  • ninjalj
    ninjalj over 10 years
    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.
  • ajay
    ajay over 10 years
    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?
  • ninjalj
    ninjalj over 10 years
    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.
  • ajay
    ajay over 10 years
    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!
  • ninjalj
    ninjalj over 10 years
    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.
  • chux - Reinstate Monica
    chux - Reinstate Monica over 10 years
    @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.
  • Brandin
    Brandin over 10 years
    @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)
  • supercat
    supercat over 5 years
    @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
    supercat over 5 years
    @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.
  • chux - Reinstate Monica
    chux - Reinstate Monica over 5 years
    @supercat Yes with |int| in range of unsigned, your (value < 0) ? -(unsigned)value : (unsigned)value is a nice well defined alternative to this. Note that given C' specs, a int negative_abs(int) is a full int range alternative to folding via abs().
  • chux - Reinstate Monica
    chux - Reinstate Monica over 5 years
    @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.
  • supercat
    supercat over 5 years
    @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?
  • chux - Reinstate Monica
    chux - Reinstate Monica over 5 years
    Yes. ......... iirc