Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inconsistent results converting unsigned [char, short, int, long] to double

Tags:

c++

c

#include <stdio.h>

int main(int argc, char* argv[]) {
  unsigned char c = 10;
  unsigned short d = 10;
  unsigned int e = 10;
  unsigned long f = 10;
  double g = -c;
  double h = -d;
  double i = -e;
  double j = -f;
  printf("%d %lf\n", c, g);
  printf("%u %lf\n", d, h);
  printf("%u %lf\n", e, i);
  printf("%lu %lf\n", f, j);
}

gives as output

10 -10.000000
10 -10.000000
10 4294967286.000000
10 18446744073709551616.000000

Why are the results inconsistent, yielding -10 for some types and huge values for others?

like image 871
bruno nery Avatar asked Nov 19 '13 19:11

bruno nery


1 Answers

The operand of a unary - operator is promoted; types narrower than int are promoted to int or to unsigned int.

Since (signed) int can hold all the values that can be represented by an unsigned char, the value (unsigned char)10 is promoted to the signed int value 10. Negating that gives you the (signed) int value -10, which is then converted to double.

An unsigned int is not "promoted" to int, because int can't hold all the values. So the expression -e applies the unsigned int negation operator, which obviously cannot yield a negative value. The result is UINT_MAX + 1 - 10, which on your system is 4294967286. Converting to double yields the value you see.

Likewise, unsigned long is not promoted, so -f yields ULONG_MAX + 1 - 10, which when converted to double yields 18446744073709551606 (264-10) (apparently your system has 64-bit long and unsigned long). Converting that value to double loses some precision, yielding the value you see.

Aside from the promotion rules, it's important to remember that the type of a C expression is (almost always) not affected by the context in which it appears. -f yields a result of the same value and type regardless of what it's assigned to.

like image 138
Keith Thompson Avatar answered Oct 03 '22 03:10

Keith Thompson