Jump to content

Pop quiz: integer number representation in C

MG2R

I'm learning for an exam at the moment. During class, the teacher interluded with the following quiz. Don't worry, I know the answers already (see spoilers) and don't need your help solving them. See for yourself if you can answer them all correctly :)

Assume machine with 32 bit word size, two's complement integers.Assume execution of the tests with C.For each of the following expressions, either:  * Argue that it's true for all argument values, or  * Give example where not trueInitialization:  int x = foo();  int y = bar();  unsigned ux = x;  unsigned uy = y;Expressions:  1) if (x < 0) then ((x * 2) < 0)  2) ux >= 0  3) if (x & 7 == 7) then ((x << 30) < 0)  4) ux > -1  5) if (x > y) then (-x < -y)  6) x * x >= 0  7) if (x > 0 && y > 0) then (x + y > 0)  8) if (x >= 0) then (-x <= 0)  9) if (x <= 0) then (-x >= 0)

There are a few that render weird answers at first glance, see if you can spot them.

 

  1. False, beware of overflows
  2. True, unsigned integers are always 0 or more
  3. True, the sign bit will always be 1
  4. False! Since ux is unsigned, C will interpret -1 as unsigned as well. Since two's complement of -1 is every bit turned to 1, this expression actually evaluates if all possible values of ux are bigger than the biggest integer representable by ux.
  5. False: x = -1, y = Tmin (smallest negative number representable)
  6. False: x = 50.000 (again, that overflow)
  7. False: beware of overflows
  8. True
  9. False, -Tmin isn't representable in two's complement numbers

Link to comment
Share on other sites

Link to post
Share on other sites

Damn overflows!

-.-

i5 4670k @ 4.2GHz (Coolermaster Hyper 212 Evo); ASrock Z87 EXTREME4; 8GB Kingston HyperX Beast DDR3 RAM @ 2133MHz; Asus DirectCU GTX 560; Super Flower Golden King 550 Platinum PSU;1TB Seagate Barracuda;Corsair 200r case. 

Link to comment
Share on other sites

Link to post
Share on other sites

Me.. Woosh!!                             The questions..

CPU: i7 4770k | GPU: Sapphire 290 Tri-X OC | RAM: Corsair Vengeance LP 2x8GB | MTB: GA-Z87X-UD5HCOOLER: Noctua NH-D14 | PSU: Corsair 760i | CASE: Corsair 550D | DISPLAY:  BenQ XL2420TE


Firestrike scores - Graphics: 10781 Physics: 9448 Combined: 4289


"Nvidia, Fuck you" - Linus Torvald

Link to comment
Share on other sites

Link to post
Share on other sites

yay I got it right....I was a bit scared given the amount of false's I had...good quiz though....I almost got hung up on 5 though :P  I always forget whether the positive or negative side of two's complement has the extra number (I finally figured it must be the negative)

0b10111010 10101101 11110000 00001101

Link to comment
Share on other sites

Link to post
Share on other sites

yay I got it right....I was a bit scared given the amount of false's I had...good quiz though....I almost got hung up on 5 though :P  I always forget whether the positive or negative side of two's complement has the extra number (I finally figured it must be the negative)

My only real problem was number four, that took me quite a while to full think through.

Link to comment
Share on other sites

Link to post
Share on other sites

I like this, will show my friends.

Comb it with a brick

Link to comment
Share on other sites

Link to post
Share on other sites

My only real problem was number four, that took me quite a while to full think through.

Yea I can understand that....some languages don't properly convert making it always true :P

0b10111010 10101101 11110000 00001101

Link to comment
Share on other sites

Link to post
Share on other sites

i didn't believe your answer for the number 4, so i called BS and ran at my compiler to prove you wrong

i was wrong

 

then i did some tests to prove that it's stupid

but it's not

 

it took some time to get it, but in the end, i got it

*slow clap*

 

edit:

why does

	float a = -1.0f;	unsigned k = a;	if(k == -1)		puts("yup");	else		puts("nope");

print "yup"?

 

more edit, more questions:

why does

	unsigned k = -1.0f;

give a warning for overflow? it's not a matter of type size, because

	unsigned k = 1.0f;

compiles fine, and i checked anyway, they're all 4 bytes

Link to comment
Share on other sites

Link to post
Share on other sites

i didn't believe your answer for the number 4, so i called BS and ran at my compiler to prove you wrong

i was wrong

 

then i did some tests to prove that it's stupid

but it's not

 

it took some time to get it, but in the end, i got it

*slow clap*

 

edit:

why does

	float a = -1.0f;	unsigned k = a;	if(k == -1)		puts("yup");	else		puts("nope");

print "yup"?

 

more edit, more questions:

why does

	unsigned k = -1.0f;

give a warning for overflow? it's not a matter of type size, because

	unsigned k = 1.0f;

compiles fine, and i checked anyway, they're all 4 bytes

 

1) Because the conversion from float to int simply truncates the fractional part. If a = -1.5f the result is still the same. The conversion from signed to unsigned simply zero extends or truncates the signed value to fit the unsigned destination. 

 

2) GCC doesn't warn me even with -Wall -Wextra Actually it does and I wasn't paying attention

 

jfOAxPY.png

main(i){for(;i<101;i++)printf("Fizz\n\0Fizzz\bBuzz\n\0%d\n"+(!(i%5)^!!(i%3)*3)*6,i);}

Link to comment
Share on other sites

Link to post
Share on other sites

-snip-

 

So I know that Avratz answered the first part, but I will still reanswer with a different approach.

So -1f is converted to integer, so -1 and then -1 effectively it is like going 0 + (-1), which rolls over the 00000... to 11111.... which then is equal to a freaky big number.

 

The reason for the warning is because it is possible to overflow.  Integer types and float types, even though being the same byte size store information very differently.  After all it is possible that this statement is true for floating points (a + 1 == a).  This is because floating point's largest number you can store is roughly 3.4 * 10 ^ 38....which would overflow the integer by a vast margin

0b10111010 10101101 11110000 00001101

Link to comment
Share on other sites

Link to post
Share on other sites

-snip-

-snap-

pardon me, i didn't really explain what bothered me in the first question:

for what i see and understand, the 'unsigned = signed' assignment does a bit-to-bit copy, so the signed representation of -1 is equal to the unsigned representation of the biggest unsigned, and that's why the 4th question is false

but an 'int = float' assignment automatically makes a conversion, and not a mere memory copy. why is that? when are values converted implicitely? is it because 'unsigned = signed' is an assignment between 'int's, so the compiler feels like there's no need for a reinterpretation of the bits?

 

and @WanderingFool, i see your point but that doesn't explain why the positive assignment doesn't warn any overflow

Link to comment
Share on other sites

Link to post
Share on other sites

but an 'int = float' assignment automatically makes a conversion, and not a mere memory copy. why is that? when are values converted implicitely? is it because 'unsigned = signed' is an assignment between 'int's, so the compiler feels like there's no need for a reinterpretation of the bits?

This happens because 'int = float' would have no logical meaning when doing a bit-by-bit copy. When you do 'unsigned = signed', both of the datatypes interpret the bits in the same way, only to differ in a sign bit and some overflow jazz, which still maps numbers quite nicely (i. e. you get a nice mapping between the signed values and the unsigned values). When you try to map floats to ints, this nice mapping breaks, because floating point numbers are constructed using a complex algorithm which requires three different parts to be stored within the word containing the number. There's no logical mapping whatsoever when doing a bit-by-bit copy between a float and an int.

Link to comment
Share on other sites

Link to post
Share on other sites

 @WanderingFool, i see your point but that doesn't explain why the positive assignment doesn't warn any overflow

hmm, my bad, I totally missed that positive type when reading your first post.   Well my guess is that it is likely reading the value, and knows that it can safely convert 1.0f to 1.  Since it can do the conversion at compile time.  With -1.0f, it is more likely that it is detecting you will get an unexpected concequence by using a negative number.  The reason why -1; would likely not trigger an warning but -1.0f would though would be that -1; more likely implies the user knows they are getting the largest value....I hope this makes sense, my mind is a bit scrambled this morning.

 

MG2R also did a nice job of why memory isn't just copied and a conversion is done.

0b10111010 10101101 11110000 00001101

Link to comment
Share on other sites

Link to post
Share on other sites

...

 

Floats follow the IEEE 754 standard, so a bit for bit copy would be meaningless. http://en.cppreference.com/w/cpp/language/implicit_cast

 

If you did want access to the raw bits of a float you could do

union FloatInt{    float        f;    unsigned int i;};

What's odd is that in GCC, unsigned x = -1.0f; throws the overflow warning and sets x to 0, but float f = -1.0f; unsigned x = f; works.

 

For example

int main(){    float f = -1.5f;    int b = f;    unsigned a = b;    unsigned c = f;    unsigned x = -1.0f;    unsigned mark = 0xDEADC0DE;    printf("%d %d %d %d\n", b, a, c, x);    return 0;}

Output is -1 -1 -1 0

 

If we look at the disassembly in PE Explorer we get this

 

       mov	eax,[L00403034]                         ;float f = -1.5f;  		mov	[esp+4Ch],eax  		fld	dword ptr [esp+4Ch]                                                            ;int b = f;  		fnstcw	word ptr [esp+2Eh]                  ;store current control flags  		movzx	eax,[esp+2Eh]                       ;load flags  		mov	ah,0Ch                                  ;modify flags  		mov	[esp+2Ch],ax                            ;put new control flags onto stack  		fldcw	word ptr [esp+2Ch]                  ;set control flags  		fistp	dword ptr [esp+48h]                 ;set b to -1  		fldcw	word ptr [esp+2Eh]                  ;load previous flags                                                            ;unsigned a = b;  		mov	eax,[esp+48h]         		mov	[esp+44h],eax                           ;store b in a                  		fld	dword ptr [esp+4Ch]                     ;load -1.5f  		fldcw	word ptr [esp+2Ch]                  ;load modified flags  		fistp	qword ptr [esp+20h]                 ;store -1  		fldcw	word ptr [esp+2Eh]                  ;restore flags          		mov	eax,[esp+20h]                           ;load -1  		mov	edx,[esp+24h]                           ;???  		mov	[esp+40h],eax                           ;unsigned c = f;                  		mov	dword ptr [esp+3Ch],00000000h           ;unsigned x = -1.0f                  		mov	dword ptr [esp+38h],DEADC0DEh           ;marker so I can find the assembly                  		mov	eax,[esp+3Ch]  		mov	[esp+10h],eax  		mov	eax,[esp+40h]  		mov	[esp+0Ch],eax  		mov	eax,[esp+44h]  		mov	[esp+08h],eax  		mov	eax,[esp+48h]  		mov	[esp+04h],eax  		mov	dword ptr [esp],SSZ00403024__d__d__d__d_  		call	jmp_msvcrt.dll!printf  		mov	eax,00000000h  		leave  		retn

 

Weird.

main(i){for(;i<101;i++)printf("Fizz\n\0Fizzz\bBuzz\n\0%d\n"+(!(i%5)^!!(i%3)*3)*6,i);}

Link to comment
Share on other sites

Link to post
Share on other sites

  • 3 weeks later...

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×