gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

Beginners > why use unsigned short

#17914 - xadeu - Wed Mar 17, 2004 5:54 am

As a GBA Developer why does one go through the trouble of declaring vars to be
Code:
unsigned short x;

and so on. why not
Code:
int x;

Is it because the memory is in high
demand and one should not
waste it by wastefully declaring
a var as an int?

#17915 - poslundc - Wed Mar 17, 2004 6:35 am

There are a number of reasons to declare a variable using a specific data type.

1. To conserve memory. Although this doesn't apply to local/global variables unless they are in an array or part of a struct. (The compiler will always pad your variable length to 4 bytes.)

2. To expose relevant characteristics of the data. I generally use the nomenclature that sajimori suggests in this thread where I will use ints for anything general-purpose (counters, most calculations, etc). but will use typedefs that reveal information about the kind of data stored (eg. u16, s32) whenever it matters to the program or the algorithm.

3. To conform to data. Within structs it is important to specify the length of each field so that it matches the data format being used.

4. Mathematical reasons. It can make a big difference whether a variable is signed or not. Aside from the difference in range it provides, it also determines whether a right bitshift is logical or arithmetic. 8-bit and 16-bit variables will overflow at different values than their 32-bit counterparts.

There may be others, too.

Dan.

#17942 - johnny_north - Wed Mar 17, 2004 9:41 pm

This falls under Conserving Memory:

You might choose a 16 bit variable to use as a bit field, say where each bit of the variable is essentially 1(true) or 0(false) and you need between 8 and 17 boolean values (not inclusive). I think a bool type uses 32 bits. Although it physically doesn't matter if you declare this a s16 or a u16, declaring it as a signed value doesn't seem to make much sense.

#17963 - Miked0801 - Wed Mar 17, 2004 11:10 pm

Personally, I only use signed values if it needs to be signed. It makes much easier on my soft optimizer if I don't have to worry about sign bits when doing bit-wise arithmetic on stuff. The compiler seems to handle it better as well

#17967 - sajiimori - Wed Mar 17, 2004 11:14 pm

Sheesh -- what ever happened to the 80/20 rule?? =D

#17971 - poslundc - Thu Mar 18, 2004 12:34 am

Miked0801 wrote:
Personally, I only use signed values if it needs to be signed. It makes much easier on my soft optimizer if I don't have to worry about sign bits when doing bit-wise arithmetic on stuff. The compiler seems to handle it better as well


What does it affect, other than right-shifts, the mode of the multiplication instruction and the mode of the load instruction?

The only advantage I can see is that the u8 data type can use a different addressing mode than the s8 data type.

Dan.

#17976 - Miked0801 - Thu Mar 18, 2004 12:53 am

Just when looking at asm output when compiling, it seems that signed stuff tends to make (very slightly) larger code. Again, I could be wrong. Still, I prefer unsigned just for myself - and whenever I use a countdown to 0 for loop, I invariably get burned :)

#17977 - ampz - Thu Mar 18, 2004 12:53 am

Smaller variables improve performance if the variable is located in a memory with a 16 or 8 bit bus.
There are alot of good reasons to use variables of proper size, especially on systems with limited memory and computing resources.

#17981 - poslundc - Thu Mar 18, 2004 1:55 am

ampz wrote:
Smaller variables improve performance if the variable is located in a memory with a 16 or 8 bit bus.


Good one; I missed that on my list. The corollary, however, is that it works both ways. On the GBA, with the exception of the savegame areas, it's always more efficient (though not always more practical) to load 16 bits at a time instead of 8 bits. In IWRAM and the I/O registers you get more efficiency loading 32 bits at a time than 16 or 8.

Quote:
There are alot of good reasons to use variables of proper size, especially on systems with limited memory and computing resources.


This is true, but it's also important to keep in mind that very often the compiler will pad your variable length anyway. As far as I'm aware, structs and arrays are the only places where specifying the size of your primitive data type will affect how it is stored.

Dan.

#17982 - poslundc - Thu Mar 18, 2004 2:09 am

Miked0801 wrote:
Still, I prefer unsigned just for myself - and whenever I use a countdown to 0 for loop, I invariably get burned :)


That's unusual... counting down to zero with signed variables should work exactly the same way as with unsigned variables.

In fact, the added advantage to using signed variables is that if you aren't using a monotonically decreasing function for loop termination you can express your termination condition as an inequality (eg. "while (x-- > 0)" instead of just "while (x--)").

In general, though, I don't worry about whether a variable is signed or unsigned unless it actually matters to the algorithm. In C, this usually means that variables are signed by default (except I think chars are by default unsigned). I find this makes things less confusing for other programmers reading my code: I don't use either an explicitly signed or an unsigned type unless there's a reason within the program.

Dan.

#17984 - Miked0801 - Thu Mar 18, 2004 2:13 am

lol. Then you'll get burned too:

(edit - made my example a bit better)

A fast clear of 4? words of memory :)

Code:

u32 i;
u32 foo[4];

for(i=3; i>=0; i--)
{
    foo[i] = 0;
}

#17985 - tepples - Thu Mar 18, 2004 2:21 am

poslundc wrote:
Miked0801 wrote:
Still, I prefer unsigned just for myself - and whenever I use a countdown to 0 for loop, I invariably get burned :)

That's unusual... counting down to zero with signed variables should work exactly the same way as with unsigned variables.

Unless you anticipate often having 32768 to 65535 of something.

Miked0801: For one thing, your code will probably trigger a GCC warning that the comparison is always true. For another, explicitly unrolling the loop will probably make the code more predictably faster:
Code:
foo[0] = 0;
foo[1] = 0;
foo[2] = 0;
foo[3] = 0;

_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#17986 - Miked0801 - Thu Mar 18, 2004 2:25 am

I wish my version of GCC did, it does not though (3.2.2 nor 2.9.5) :(

And yes, of course I know there are faster ways to clear words. This was just a quick, contrived example to show a difference between signed and unsigned for loops that has burned me a few times in the past.

#17987 - poslundc - Thu Mar 18, 2004 2:27 am

tepples wrote:
poslundc wrote:
That's unusual... counting down to zero with signed variables should work exactly the same way as with unsigned variables.

Unless you anticipate often having 32768 to 65535 of something.


That's why I use plain and simple ints for general counters. If I need to worry about values between 2-4 billion, then presumably I will have anticipated the need for making the variable unsigned somewhere along the way. :)

Mike: I thought you were making the case against using signed variables as decrementing counters? If you're getting burned by situations like that, take my suggestion and stop declaring your variables as unsigned when you don't have to. ;)

Dan.

#17988 - Miked0801 - Thu Mar 18, 2004 2:30 am

Yep. Signed are better unless you have reason to not use them. I just like unsigned because I do :P

I may have to re-evaluate though.

#17991 - ampz - Thu Mar 18, 2004 3:42 am

poslundc wrote:
ampz wrote:
Smaller variables improve performance if the variable is located in a memory with a 16 or 8 bit bus.

Good one; I missed that on my list. The corollary, however, is that it works both ways. On the GBA, with the exception of the savegame areas, it's always more efficient (though not always more practical) to load 16 bits at a time instead of 8 bits. In IWRAM and the I/O registers you get more efficiency loading 32 bits at a time than 16 or 8.

Using a 32bit counter is not more efficient than using a 16bit counter.
Only time it is more efficient to use a 32bit variable instead of a 16bit variable is when you actually want the extra 16bits.
Quote:
Quote:
There are alot of good reasons to use variables of proper size, especially on systems with limited memory and computing resources.

This is true, but it's also important to keep in mind that very often the compiler will pad your variable length anyway. As far as I'm aware, structs and arrays are the only places where specifying the size of your primitive data type will affect how it is stored.

That is certainly compiler specific. I have not investigated the matter in detail, but if there is no way of making GCC store variables efficiently, then it is not much of a optimizing compiler.
Why write memory consuming code just because your compiler sucks?

#17997 - poslundc - Thu Mar 18, 2004 5:07 am

ampz wrote:
That is certainly compiler specific. I have not investigated the matter in detail, but if there is no way of making GCC store variables efficiently, then it is not much of a optimizing compiler.
Why write memory consuming code just because your compiler sucks?


Well, the compiler needs to keep everything word-aligned to be compatible with the hardware. Theoretically it could see a combination of 8-bit or 16-bit variables and pack them together to maintain 32-bit alignment, but this isn't something that GCC does. I don't know about other compilers.

Dan.

#17998 - tepples - Thu Mar 18, 2004 5:29 am

poslundc wrote:
the compiler needs to keep everything word-aligned to be compatible with the hardware. Theoretically it could see a combination of 8-bit or 16-bit variables and pack them together to maintain 32-bit alignment, but this isn't something that GCC does.

GCC does pack variables in structs if they come listed in a naturally aligned order.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#18006 - poslundc - Thu Mar 18, 2004 2:12 pm

tepples wrote:
GCC does pack variables in structs if they come listed in a naturally aligned order.


Are you saying that it will pack my global and local variables as if they were in structs, without just padding each individual variable to 4 bytes?

If so, is there an option to turn this behaviour on/off in GCC? Because this is not the behaviour I have observed when using it.

Dan.

#18011 - ampz - Thu Mar 18, 2004 5:36 pm

poslundc wrote:
ampz wrote:
That is certainly compiler specific. I have not investigated the matter in detail, but if there is no way of making GCC store variables efficiently, then it is not much of a optimizing compiler.
Why write memory consuming code just because your compiler sucks?


Well, the compiler needs to keep everything word-aligned to be compatible with the hardware. Theoretically it could see a combination of 8-bit or 16-bit variables and pack them together to maintain 32-bit alignment, but this isn't something that GCC does. I don't know about other compilers.

Dan.

Other compilers certainly pack variables properly.
I compiled this piece of code on our "IAR Systems" ARM compiler.
Code:
volatile char pelle;
volatile int calle;
volatile char hugo;
volatile short per;

int main(void){
  calle=0;
  pelle=1;
  hugo=2;
  per=3;
}
Four gloabls of different size, they are not even declared in any useful packing order.
The compiler nicely allocated theese memory locations for the variables:
int calle: 0x100000
short per: 0x100004
char pelle: 0x100006
char hugo: 0x100007
This was with optimizations completely turned off.

The only thing a compiler has to do is sort the variables by size. Put the 32bit variables first, then the 16bit variables, and the 8bit variables at the end. That's it.

#18016 - poslundc - Thu Mar 18, 2004 6:32 pm

ampz wrote:
The only thing a compiler has to do is sort the variables by size. Put the 32bit variables first, then the 16bit variables, and the 8bit variables at the end. That's it.


I'm not arguing with the sensibility of it. If anyone knows how to make GCC do this (without using structs), I'd love to hear.

Dan.

#18021 - Miked0801 - Thu Mar 18, 2004 7:50 pm

There are ways to get this to happen with the align command in the link script. I'm no expert there, but playing with where align is called and how (and reading the docs at the gcc site), I managed to get things to align better to save RAM. Check it out.

#19753 - TaleriaKNT - Sat Apr 24, 2004 8:44 pm

You could also make the loop work with the overflow.
Code:
u32 i;
u32 foo[4];

for(i=3; i<4; i--)
{
    foo[i] = 0;
}

#19778 - sgeos - Sun Apr 25, 2004 3:31 pm

"why use unsigned short"

Why not? Somebody has to.

Miked0801 wrote:
Yep. Signed are better unless you have reason to not use them. I just like unsigned because I do :P

I may have to re-evaluate though.

Consider and reject! Consider and reject!

-Brendan

#19791 - sajiimori - Sun Apr 25, 2004 6:33 pm

TaleriaKNT, you did it wrong. Here is the correct code for clearing everything except an array:
Code:

int array[12];
for(u32 i = 0; i != 11; --i)
  array[i] = 0;

Edit: well I guess it clears the first element, but fixing that would be a detrement to readability.

#19798 - DekuTree64 - Sun Apr 25, 2004 7:59 pm

No, I think he was clearing the array itself, using a backward loop with an unsigned counter. Normally you'd just say while i >= 0 for the same effect if it was signed, but saying while i < 4 makes it so when it overflows to 65535, it's greater than 4, and breaks. Same thing going on in the processor either way, just different ways of looking at it.
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku