Variable Overflow

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

I understand in theory why this is this way, but I think im probably missing a easier solution.

WinAVR:

This Doesn't Work:

unsigned int  num1 = 65535,
						num2 = 100;
			
		unsigned long num3 = num1+num2;
			
		if(num3>65535){
			//GOOD
		}

This Does:

unsigned int 	num1 = 65535,
						num2 = 100;
			
		unsigned long num3 = ((unsigned long)num1+(unsigned long)num2);
			
		if(num3>65535){
			//GOOD
		}

Thanks,
Andy

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

The only easier solution you are missing is that one of the two typecasts
would suffice. It will then cause the other half of the expression to be
promoted to unsigned long before adding both.

Arguably though, your version looks more `symmetric', and is thus better
readable.

You could omit the outer pair of parenthesis.

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

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

The easier solution is a little bit tricky, and needs some assumptions. Obviously if you add two 16-bit variables you can get more than 16 valid bits – to correctly represent the result you need 17 bits. So after your addition processor generates condition known as overflow and to signalize such a condition processor uses Carry flag (C). In C you cannot directly read this flag, but you can do that in assembler. So the simple solution is to put assembler opcode which will detect overflow, for example ‘brcc’ instruction.

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

TFrancuz wrote:
In C you cannot directly read this flag, but you can do that in assembler. So the simple solution is to put assembler opcode which will detect overflow, for example ‘brcc’ instruction.

In practice overflow detection is often useless[1]. The reason it is useless is simple. You usually have no means to do something about it once you have detected it, except of giving up. You can not (well, not with reasonable effort in a microcontroller) dynamically resize your variable storage and base all further calculations on a larger data type. In an embedded application you can also often not cry for help from the user.

The most sensible thing you can do it to make sure right from the beginning that overflow can't happen. And that is in fact what the OP tried by placing the result from two integer additions in a long - only that a C idiosyncrasy got in his way.

---
[1] There are, of course exceptions. E.g. in safety critical applications. But even in those applications you have a very limited set of responses. E.g. driving all periphery in a safe position and then quitting to work, or rebooting. Or switching to an emergency procedure that guarantees a minim service level, but not an optimal or extended service level.

Another exception is when this is part of your algorithm. You scale down the values when they no longer fit. Since this reduces your resolution, it is kind of providing a reduced service level.

Stealing Proteus doesn't make you an engineer.

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

Useless or not it depends on application. For example I wrote an application which logs messages in FLASH memory (64k). After filling up the whole memory the log should wrap around and start again from address 0, erasing the oldest messages. Ok, but how to detect overflow over 64k? I could use 32-bit long, but it's waste of time. The simplest solution was to use carry flag after addition.
Of course it's useless if you just discover that you need more bits to store result. But it is still very useful to distinguish between different situations. One can say that the purposes of use carry flag are exactly the same as those in assembler. Nobody is telling you that C flag in microcontroller is useless.

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

Quote:
So the simple solution is to put assembler opcode which will detect overflow, for example ‘brcc’ instruction.

Except that this "simple" solution might not work. There is no guarantee that the compiler will put this assembler opcode immediately after the add. The compiler is free to reorder the code in any way that it wants as long as the functionality does not change. The problem is that the compiler has no idea what the functionality of that inlined assembly is (or more specifically, that the assembly is directly related to the add). If the compiler happens to put in even a single opcode before the inlined assembly that affects flags, then you will be making decisions on bogus data.

Regards,
Steve A.

The Board helps those that help themselves.

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

A way to detect the overflow without a long (and without carry):

unsigned int num3 = num1 + num2;

if ((num3 < num1) || (num3 < num2)) {
    // overflow

But I have problems to see the actual intention. Your first version also uses a long and ">65535" is marked as "GOOD". So if you don't want to detect the overflow but need the correct result, ... (see Jörgs post).

Stefan Ernst

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

Koshchi wrote:
Quote:
So the simple solution is to put assembler opcode which will detect overflow, for example ‘brcc’ instruction.

Except that this "simple" solution might not work. There is no guarantee that the compiler will put this assembler opcode immediately after the add. The compiler is free to reorder the code in any way that it wants as long as the functionality does not change. The problem is that the compiler has no idea what the functionality of that inlined assembly is (or more specifically, that the assembly is directly related to the add). If the compiler happens to put in even a single opcode before the inlined assembly that affects flags, then you will be making decisions on bogus data.

True, that’s why it’s tricky. You can use approach similar to that used in ATOMIC_BLOCK, but without disabling interrupts or combinations of volatile keyword.

Last Edited: Tue. Jul 7, 2009 - 02:49 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

TFrancuz wrote:
Useless or not it depends on application. For example I wrote an application which logs messages in FLASH memory (64k). After filling up the whole memory the log should wrap around and start again from address 0, erasing the oldest messages. Ok, but how to detect overflow over 64k?

If the next number is suddenly smaller than the previous one, although you added a positive value to your uint16_t.

Stealing Proteus doesn't make you an engineer.

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

ArnoldB wrote:
TFrancuz wrote:
Useless or not it depends on application. For example I wrote an application which logs messages in FLASH memory (64k). After filling up the whole memory the log should wrap around and start again from address 0, erasing the oldest messages. Ok, but how to detect overflow over 64k?

If the next number is suddenly smaller than the previous one, although you added a positive value to your uint16_t.

Yes, but it wasn’t so simple in my case.