Type casting and function parameters

Let's say I have a function that takes a parameter of type unsigned char, does some calculations and returns unsigned short. One of the operations the function does is absolute value. The questions are:
1.Do I need to type-cast the parameter to int? abs() doesn't have a version for unsigned char...
2. If yes, does the type casting from unsigned char to int have to be explicit or it can be implicit (in other words, will the compiler use the int version of abs() automatically for short and char)?
3. What is better/faster: making my own version of abs() (that takes an unsigned char parameter, checks whether it's negative and returns -param or +param accordingly) or type-casting the unsigned char parameter to int, calculating abs() and then type-casting again if needed?

4. What is better/faster? Giving variables the minimal needed size in memory or giving them the type that will minimize the number of type-casting operations? I'm asking because I saw this many times in code: variables get more memory than what they need. For example, in the ctime library, all the members of the tm structure are int, even though they don't need four bytes...some only need one.
1. Yes.
2. There's never any need to explicitly convert numeric types for assignments.
3. abs() is actually a really slow function. This is many times faster:
#define ABS(x) ((x)<0?-(x):(x))
4. Classic conversions and casts (e.g. short->long, long->char, void*->int*) generate no code. Often, compilers will automatically change the type of something to the largest possible type. I'm not sure exactly under what circumstances this happens. About ctime, probably a matter of alignment.
Ok, unsigned variables can never be negative... what am I missing?
abs() doesn't have a version for unsigned char
Hahaha. I missed that.
I missed that too...I forgot to mention something important. The calculation is like this:

1
2
3
unsigned char a,b;
/* some code... */
unsigned short c=abs(a-b);


Another question: why is the abs() function slow? why does it exist, if it's slow? And are there other functions, like rounding functions, that are slow? I'd like to know...
It's not slow... it's correct.

1
2
3
template< typename T >
T abs( T a ) 
{ return a < 0 ? -a : a; }


is safer than the macro version and equivalent to the one provided by the runtime library.
Often, compilers will automatically change the type of something to the largest possible type. I'm not sure exactly under what circumstances this happens.


Let us not worry about what the compiler might do for us automatically. Relying on implicit type casts is not a good idea. Just use static_cast and do it yourself. it is clearer and you are making it obvious to the reader that this is your intention.

secondly, what evidence is there that the c-run time library version of abs is "really" slow? What does that mean and how did you arrive at that conclusion? I don't recommend trying to outsmart whoever wrote the STL and C Run time libraries by writing macro replacements for existing functions.
Better yet design the algorithm such that casting isn't needed at all.

I still haven't seen any need to do any explicit casting; is there an example of what is being discussed about casting somewhere?

As posted:
1
2
3
unsigned char a,b;
/* some code... */
unsigned short c=abs(a-b);


1. a-b would become signed implicitly by the compiler
2. The abs function would return unsigned (char or int or whatever the compiler decided previsouly)
3. If necessary, another implicit conversion would cast it to unsigned short.

Isn't that how this would work?
Last edited on
jsmith: It's both correct and slow. I remember this one time, I shaved ~35% off an effect run time just by changing an abs() to an ABS().

seymore: When you're casting a signed type to a larger type, it's sometimes necessary to first cast the value to its unsigned equivalent. For example, when converting a char array to a wchar_t array. I've had some really nasty bugs for forgetting about that.
When I disassembled abs() on my system, it generated only 4-5 instructions. Load, compare, branch, negate, and store. How will the macro generate less code? Yes I know, there is function call overhead which shouldn't matter unless you are calling it millions of times successively (in which case the inlined template function gives you the performance without the double evaluation bug).


The code I was talking about was used to render a transition effect on a 640x480x24 surface. So yes, it was being called an awful lot of times.
Double evaluation is only a concern if you pass a complex expression, which you wouldn't do if you know it's a macro.
In other words, abs() was faster than ABS() just because ABS() is a macro that pastes itself in the code while abs() is not an inline function.

1. My questions is: does the abs() function do the type-casting for me? abs() is defined only for float,double,long double, int and long int. But not for short and char. If a and b are unsigned char, a-b may need to be converted to signed char. Either way, the parameter passed to abs() is not one of the ones listed above. What does abs() do? What type casting does it do (which version does abs() does it use for char) and what is the return type?

2. And another question: Is it wise to always give variables the minimal needed memory size (for example a variable that is always an integer between 0 and 200 will be unsigned char) and do implicit (or explicit if needed) type-casting or the usual, professional way is to give few variables that are used together in a calculation the same type to avoid type-casting even if some of them get more than the minimal memory size they need?
1
2
3
4
5
unsigned char a = 6, b=5;

short c = b-a; 

// c is 255  


You get underflow with unsigned values which is not what you want so you want to do it this way:

1
2
3
4
5
unsigned char a = 6, b=5;

short c = static_cast<short>(b)-static_cast<short>(a); 

// c is -1  



btw, My compiler knows how to inline small runtime functions if it is worth of increased code size. C++ compilers are doing number of optimizations which are makeing them slow compared to some others compilers.

What about using small types to save memory?
It is in fact waste of performance. Modern computers are optimized to function with multi-byte values and it is slower to use char than int. Only time when I would choose smaller integer type over int is when I have large number of small numbers stored in array. Then it is worth having them smaller to increase number of values that fit to processor cache.


Of course this doesn't hold true if you are developing for embed system. :)
Last edited on
1) The compiler does implicit conversions for you based upon its rules of type promotion. Since unsigned char is integral, the only candidates are abs( int ) and abs( long ). In this case the compiler will choose int. Therefore, the unsigned char will be implicitly cast to int, then passed to the function. The return type is int, which you then probably store in something other than int, which means another implicit cast is done from int back to your type.

2) I am a proponent of using the "right" size (type). For example**, the number of elements in a list is an unsigned type. It is nonsensical to think of a list containing -3 elements. Using a signed type just makes you write extra if() statements:

1
2
3
4
   if( numElements < 0 )
       cout << "Whoa, list is even less than empty!" << endl;
   else if( numElements > MAX_SUPPORTED )
       cout << "List too big" << endl;


Whether I use unsigned char, unsigned short, unsigned int, or unsigned long(!) is a matter of how many elements will be stored in the list. If the list is a list of players on the basketball team, probably there will never be
more than 255, so unsigned char works.

IMHO the performance difference is negligible between char and int. [It is worth noting that it is not necessarily faster to access an int than a char, particularly if the int is misaligned]. Only in the 0.0001% of all applications where every ounce of speed is required would I attempt to resort to such micro-optimizations. (And the compiler in most cases will be able to optimize the code better than a human anyway).

And lastly, code that has a lot of explicit typecasting is poorly designed. Code that uses a lot of implicit typecasting is stupidly written. [Ignorance may be bliss, but it can lead to problems that are hard to track down.]

** Ok, yes, yes, yes, I know. When using STL containers, I don't need to store the size in most cases (size() is O(n) on std::list...). And if I did, I'd be using container::size_type anyway.
"Code that uses a lot of implicit typecasting is stupidly written."

So does it mean should use bigger variable sizes to avoid type casting? I have a LOT of implicit type casting: for example in a A* pathfinding code. I'm assuming you know what it is...I have x and y data with which I need to calculate the H value. All the x and y values are stored as signed char because a bigger size is not needed, but the maximal H value is over 1000, so the H calculation function returns unsigned short. H calculations are made many times per an execution of A*, and it's executed very frequently, since it's going to be used in an RTS game. Should I save all the positions as short? It would waste a lot of memory because for each unit in the game there is a signed char array of size 528, and moving to short would double the size to 1056B...~1kB per unit!

@suokko : I tried the first piece of code, and c was -1 ! not 255. Did you try it or you're guessing? Compilers are supposed to be smart: if c was unsigned char too, then I'd get 255, but since c is short, it gets -1. Underflow only happens with the variable taking the result. At least in MSVC.
Last edited on
Well, in your case, probably not since it would be very wasteful. In that case, you might consider using explicit typecasts. Even though the code might work with implicit typecasting, I like to use explicit casts for two reasons:

1) It's extra typing which I hate to do, which means I try to avoid it :)
2) It is immediately obvious to anyone maintaining the code that
something a little weird is going on there.

I spent quite a bit of time tracking down a bug in a million line application where doubles were being assigned to floats and then back to doubles, and the result was a loss of precision. It was not easy to find, but had I been able to grep for something like "static_cast< float >" it could've been easier.
In other words, the conclusion is: for variables or small arrays it's generally better to use a size that minimizes type casting. For large arrays, it's better to save memory and use explicit type casting unless it's simple obvious type casting like short to int, which doesn't produce errors or bugs.

Thanks!
Topic archived. No new replies allowed.