Casting unsigned int to signed long

Go To Last Post
13 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'd expected that this topic would've been discussed before, but I can't locate any matching posts.

I fear that I made some stupid programming a few years back, where I cast an unsigned int variable to a signed long before doing some calculations.

unsigned int SomeVar1; // 16 bits unsigned variable
unsigned int SomeVar2; // 16 bits unsigned variable
signed long TempVar;   // 32 bits signed variable
...
TempVar = (signed long)SomeVar1 + (signed long)SomeVar2;

This worked when I originally wrote it, and I never thought about the potential hazard.

Now the code has been reused in a new product, and I suspect that it no longer works as I expected. The compiler has changed over the years, and I fear that this is the cause of my problem.

When I cast an unsigned int variable to a signed long, what happens first? Is the value expanded to 32 bits first, and then considered a signed variable, or is it the opposite? Let's say that the original value is 65436. If the value is first expanded to 32 bits, and afterwords considered a signed variable, nothing changes. But if the value is considered signed before it is expanded, the value is changed to -100.

In the ANSI C book by K&R, I found the following text in A6.2: "When any integer is converted to a signed type, the value is unchanged if it can be represented in the new type and is implementation-defined otherwise." Unfortunately, I can't really tell if this answers my question, because A6.2 doesn't specifically mention variable size expansion. At least, I don't think it does. The wording in the section that I haven't quoted reminds me of the nearly unreadable FDA standards...

Can anyone help?

You're absolutely right. This member is stupid. Please help.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Bit15 in your uint16_t variable is sign-extended to the int32_t result.

Bit15 is the sign bit in an int16_t.

SomeVar1 and SomeVar2 are presumably representing signed values. So why not use int16_t in the first place?

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No. SomeVar1 and SomeVar2 definitely represent unsigned values. But in one calculation (a bit more complex than in the example) their values are used, and the result may become large and negative.

Anyway – I located the problem a few minutes ago. And thankfully, it had nothing to do with the typecast.

You're absolutely right. This member is stupid. Please help.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If your variables represent unsigned values, you promote from uint16_t to uint32_t.

As always, choose the appropriate type. Fiddling signed and unsigned is unwise.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You value is unsigned-extended because it's an unsigned type. 65436 will still be 65436 but now as 32-bit signed value.

avrfreaks does not support Opera. Profile inactive.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
As always, choose the appropriate type. Fiddling signed and unsigned is unwise.

Why throw away one bit?
(perhaps you can live with it on a int but not a char.)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Who said that you are throwing anything away?

You still choose the appropriate type. e.g. a character is likely to be unsigned.

The number of fingers on your hand is likely to be unsigned.
The amount of cash in your bank account is likely to be signed.

There are occasions when you want to subtract toes from fingers. In which case you need to do the maths with signed arithmetic.

Of course, both you and I know that you can use unsigned arithmetic providing you know what you are doing. For example, subtracting two Timer values.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok I don't make it clear because still use signed/unsigned char for a byte (just old habit), I mean HW PORT, Timer etc. will normally be unsigned, but that don't prevent me to do some signed calculations based on those values, (else I only have 7 bit to work with)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I quite agree. PORT, SPI data, UART data, TIMERs etc are generally defined as unsigned. It is also the most convenient way to write generic functions.

This normally means casting a uint8_t to an int16_t to evaluate expressions.

It is up to you to handle arithmetic yourself if you want to short circuit C maths. If you know the range of the operands, you can avoid some checks.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
If you know the range of the operands, you can avoid some checks.

I better :)
And many just do as you say cast to int16_t and don't think and perhaps get overflow because it had to be int32_t.
Quote:
It is up to you to handle arithmetic yourself if you want to short circuit C maths.

??? at least C is clear how to cast so I don't see any problems (yes it's not efficient but that is an other story).
And remember C don't do any calculations on 8bit it will be promoted to size of int. (perhaps the optimizer see that it don't make any difference and remove some of the code).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
As always, choose the appropriate type. Fiddling signed and unsigned is unwise.

Believe me. Unsigned int is the appropriate type for these variables. Making these signed wouldn't make sense, and would also force me to use 32-bit variables instead. Which wouldn't be practical for other reasons.

The variables are used several places in the program. They don't just exist for that single calculation where I typecast them to signed long.

Normally, fiddling with signed/unsigned is no big deal at all. At least I've never thought of it as being much of a mystery. I just became unsure when I combined change of signedness with change of size.

Anyway, SprinterSB confirmed what I guessed the ANSI C book was trying to say. Thanks a lot. :)

You're absolutely right. This member is stupid. Please help.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ErikT wrote:
I just became unsure when I combined change of signedness with change of size.
You could always use multiple casts:
(int32_t)(uint32_t)my_u16_var

This makes it clear to the reader what is happening. Further, you could examine the code generated by the compiler for this sequence and for the one without the intermediate cast. I suspect that they would be the same.

Don Kinzer
ZBasic Microcontrollers
http://www.zbasic.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

dkinzer wrote:
ErikT wrote:
I just became unsure when I combined change of signedness with change of size.
You could always use multiple casts:
(int32_t)(uint32_t)my_u16_var

This makes it clear to the reader what is happening. Further, you could examine the code generated by the compiler for this sequence and for the one without the intermediate cast. I suspect that they would be the same.

The time to be concerned is when a cast might not be value-preserving.
uint16_t to int32_t is always value-preserving.
Though not a problem in this case,
uint32_t to int32_t is either value-preserving or undefined, depending on the data.
To me, the intermediate cast has no value at all.
If anything, it requires a bit more effort on the part of the reader to ascertain what it does.

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods