2

I saw in some code we have a signed value being passed to function (which takes unsigned value). The returned value is again converted back to signed (with the intention of getting the original signed value).. Even though it works currently.. My doubt, will original signed value is retained in this case, in all possible scenarios?

I was told casting is bad, and should be avoided as much as possible.. Can some computer science experts let me know, when this code can be broken?

ST3
  • 8,826
  • 3
  • 68
  • 92
pdk
  • 535
  • 3
  • 5
  • 16
  • 1
    Can you please post this code? Its kinda hard to tell without seeing it. However this does seem to be a bit silly on its face. – David says Reinstate Monica Sep 12 '13 at 19:19
  • thanks..The code cannot be shown because of copy right..It is huge legacy code..On top of this code, a new feature is added. This feature calls the legacy interface function which takes unsigned value.. But the set and get functions in the new feature is using signed values.. – pdk Sep 12 '13 at 19:23
  • Perhaps a stripped down version then? – David says Reinstate Monica Sep 12 '13 at 19:24
  • AppInfo->m_exam.SetMark (mConfiginfo.Get("student", studentID, "Marks", AppInfo->m_exam.GetMark())); Here the SetMark and GetMark are taking signed values, whereas the Get() function is taking unsigned.. – pdk Sep 12 '13 at 19:27
  • I found here http://stackoverflow.com/questions/5040920/converting-from-signed-char-to-unsigned-char-and-back-again?rq=1 looks like it may not be reliable always ? Any inuts – pdk Sep 12 '13 at 19:50

3 Answers3

4

All implementations that I know (after working with C++ for 20+ years on different platforms), convert signed <-> unsigned integer by doing nothing, i.e. the binary value is passed unchanged.

The roots of such behavior is in handling (i.e. not handling) the integer overflows in C. Having such legacy this signed/unsigned conversion is a reasonable thing to do. I do not think this will be changed ever.

To summarize, conversions signed -> unsigned -> signed and unsigned -> signed -> unsigned are 100 % safe. When you have even number of conversions, you either need to take care that the value of the number is a non negative integer that is not using the highest bit, in this case it is not important if the type is signed or unsigned. Otherwise you should explicitly take care of the out of range values with your own code.

Kirill Kobelev
  • 10,252
  • 6
  • 30
  • 51
3

The integer promotions sections of the standards covers conversion of signed and unsigned very well. Your question is tagged C and C++, and though I could dust off both standards, I will show you the relevant portions of the C99 standard here.

Regarding promoting a like-sized signed integer to an unsigned integer where the signed integer is NOT in range of the unsigned integer (i.e. it is less than zero(0)):

C99 6.3.1.3-p2

Otherwise, 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.

which essentially means "add (UINT_MAX+1)".

For example, on a typical 32 bit system, UINT_MAX is 0xFFFFFFFF. Now suppose you want to convert the following:

int ival = -1;
unsigned int uval = (unsigned int)ival;

According to the standard, ival will be promoted to unsigned int by doing the following:

uval = (UINT_MAX+1) + (-1);

Which of course, results in uval = UINT_MAX;. This is defined by the standard.

Converting to signed from unsigned is a different story. Immediately following the previous section of the standard cited above is:

C99 6.3.1.3-p3

Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised.

This essentially means you cannot rely on an unsigned conversion to like-size signed result to have implementation-indepentant behavior if the value in the unsigned is not within the allowable range of the signed int. In other words this:

unsigned int uval = INT_MAX;
int val = (int)uval; 

has defined behavior, since INT_MAX is a value within the signed integer range, whereas this:

unsigned int uval = INT_MAX + n;
int val = (int)uval; 

for some arbitrary n such that (INT_MAX+n <= UINT_MAX) is implementation defined. Therefore, don't rely on it. The particular portion of the standard that should scare you into avoiding doing this is the potential for: "an implementation-defined signal is raised". Yikes. Just don't do it.


Examples from the text above

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

int main(int argc, char *argv[])
{
    int ival = -1;
    unsigned int uval = (unsigned int)ival;
    printf("%d : %u\n", ival, uval);

    uval = INT_MAX;
    ival = (int)uval;
    printf("%d : %u\n", ival, uval);

    uval += 1;
    ival = (int)uval;
    printf("%d : %u\n", ival, uval);

    return 0;
}

Output Platform: Apple LLVM version 4.2 (clang-425.0.28)

-1 : 4294967295
2147483647 : 2147483647
-2147483648 : 2147483648
WhozCraig
  • 65,258
  • 11
  • 75
  • 141
1

Conversions from signed to unsigned types are well defined; the result is the original value modulo 2^n, where n is the number of bits in the value representation of the unsigned type. That is, negative values are wrapped to large positive values. However, when you convert from an unsigned to a signed type, if the value being converted can be represented in the target type the value is unchanged; if it can't be represented, the result is implementation defined. So converting a large unsigned value into a signed value that isn't large enough to hold it is not required to produce a negative value.

Pete Becker
  • 74,985
  • 8
  • 76
  • 165