🔒 Locked
signed/unsigned char/short/int/long
Advertisement
Original Post
I would like to know when to prefer each of these data types. It would be nice if there was a default rule with various exceptions. Maybe something like, "always use int unless...", but I get the impression that the issue is more complex than this.
I have always heard various contradictory things. One person tells me that int is faster than short or char. Another tells me that unsigned char is faster than anything. Someone tells me that signed is faster than unsigned. Another tells me the opposite. Someone says that int is fastest, but you shouldn''t use it unless you need 32-bits, because that is wasteful and using smaller data types is much better for the cache. Someone tells me that you should always use unsigned if you are doing any kind of bitwise operations, and always use signed if you are doing arithmetic operations.
I read stuff online that talks about data conversion assembly instructions, which makes me think that the data must be converted at runtime, which doesn''t seem to be the fastest. So maybe it''s better to always use the same type to avoid runtime conversion? Of course another website says all of the conversion is done at runtime in C/C++.
One time I showed someone a struct that had four ints. He said I should change them to unsigned chars so the size of the struct would fit into 32 bits so it could be copied faster. Another person told me to forget the struct altogether and pack the data into an unsigned int. I have no idea which is fastest. Maybe there are penalties for accessing a particular byte out of an unsigned int, or maybe packing is the best choice, since it will be fast to copy around, and to unpack it only requires very fast branchless bitwise instructions.
I''m not sure I know enough to be able to form any rules about this. For instance, is signed or unsigned a better default choice? Should using signed or unsigned be the exception? If signed is faster, then use signed unless you need unsigned or if you need to do bitwise operations. If unsigned is faster, then use unsigned unless you need signed. But I don''t know which is faster. Similarly, I don''t know whether it is any faster or slower to use 8-bit, 16-bit, or 32-bit data on a 32-bit cpu.
I''m confused about this issue, and it seems like whatever I decide to go with, I feel like it was the wrong choice later on. So, any advice?
My advice is to write your code in such a manner that it''s easy to switch representations (typedefs are your friend). Then profile your code and play with the types and see how things improve/degrade.
Remember: first make it work, then make it work fast. Too much playing around with bitshifting and memory games stand a good chance of damaging program correctness.
Remember: first make it work, then make it work fast. Too much playing around with bitshifting and memory games stand a good chance of damaging program correctness.
its all based around options, if you know your not going to need the negative sign in any type, make it unsigned, this will
increase its upper limit.
as for speed, i dont think it would make much difference, esp. on todays CPUs (basicaly anything abouve a Pentium Pro
)
the different data types are provided for storage of different types of numbers.
the smaller 8bit and 16bit types are basically space saving.
everyone else, feel free to jump in and correct me!
increase its upper limit.
as for speed, i dont think it would make much difference, esp. on todays CPUs (basicaly anything abouve a Pentium Pro
the different data types are provided for storage of different types of numbers.
the smaller 8bit and 16bit types are basically space saving.
everyone else, feel free to jump in and correct me!
I''m no guru, so take this with a grain of salt, but here''s what I do and what seems fairly common:
Always use (signed) int unless:
- big numbers, use long or long long
- big non-negative numbers, use unsigned variants (just because it''s non-negative doesn''t mean unsigned is the best choice, however)
- predictable overflow/underflow behavior, use unsigned
- bitwise operations, especially shifting, use unsigned
- all bits to be value bits, use char
- C style strings, use char (but use int for the return from IO functions because EOF need not fit in a char)
- char as a number, specify signed or unsigned (plain char can be an alias for either, it''s implementation dependent)
- save space, use short or char
I think that about covers it for general cases. Oh yeah, if you''re using the isalpha, isblank, etc. functions, and you want to pass it a signed or plain char, you should cast it to unsigned char. As for your struct with four ints vs. struct with four char vs. one unsigned int, this is probably a premature optimization, and the last one isn''t standard (int need not be 32 bits, and char need not be 8 bits (however, this is true on most desktop machines, but int could conceivably be 16 or 64 bits))
Always use (signed) int unless:
- big numbers, use long or long long
- big non-negative numbers, use unsigned variants (just because it''s non-negative doesn''t mean unsigned is the best choice, however)
- predictable overflow/underflow behavior, use unsigned
- bitwise operations, especially shifting, use unsigned
- all bits to be value bits, use char
- C style strings, use char (but use int for the return from IO functions because EOF need not fit in a char)
- char as a number, specify signed or unsigned (plain char can be an alias for either, it''s implementation dependent)
- save space, use short or char
I think that about covers it for general cases. Oh yeah, if you''re using the isalpha, isblank, etc. functions, and you want to pass it a signed or plain char, you should cast it to unsigned char. As for your struct with four ints vs. struct with four char vs. one unsigned int, this is probably a premature optimization, and the last one isn''t standard (int need not be 32 bits, and char need not be 8 bits (however, this is true on most desktop machines, but int could conceivably be 16 or 64 bits))
One person tells me that int is faster than short or char. Another tells me that unsigned char is faster than anything.
Historically, int was intended to be the fast integral type, the one matching the native register size in your CPU.
Someone tells me that signed is faster than unsigned. Another tells me the opposite.
There are strictly no differences between signed and unsigned.
Someone says that int is fastest, but you shouldn''t use it unless you need 32-bits, because that is wasteful and using smaller data types is much better for the cache.
That would depend on the way your data is laid out, the size of your cache lines, your access patterns ... there are no good a-priori answers.
Someone tells me that you should always use unsigned if you are doing any kind of bitwise operations, and always use signed if you are doing arithmetic operations.
When you are doing bit twiddling and compare your variables to integer literals, sign changes can be the source of subtle errors, so I can see why people would prefer unsigned integers.
As for using signed or unsigned with arithmetic operations, it depends on the range of your values. If you just add up numbers, you may have need of the extra positive range provided by unsigned integers. But if you may have negative results (substractions), then you probably need signed integers.
I read stuff online that talks about data conversion assembly instructions, which makes me think that the data must be converted at runtime, which doesn''t seem to be the fastest.
floating-point to integer is an expensive conversion. Integral promotions (from smaller to larger type) is a trivial zero-expansion (unsigned, pad with zeros to the left of the number) or sign-expansion (signed, pad with copies of the sign bit). While I am no ASM guru, I assume that MOV instructions take care of that as they copy data around (can anybody confirm or correct ?), with no extra cost.
So maybe it''s better to always use the same type to avoid runtime conversion?
You''ll have to run tests to see if there is any cost.
Of course another website says all of the conversion is done at runtime in C/C++.
That should be an easy optimization. Possibly just an extension of alignment routines.
One time I showed someone a struct that had four ints. He said I should change them to unsigned chars so the size of the struct would fit into 32 bits so it could be copied faster.
If you expect to copy it around a lot, packing may make it faster - assuming you don''t need the full int range, and are happy with unsigned char.
As a side note, char is unique in that there are 3 char types: char, unsigned char, signed char. Not only plain ''char'' is considered its own type, but it is undefined as to whether it is signed or unsigned (all others default to signed). Conclusion - if it matters, specify which you mean.
Another person told me to forget the struct altogether and pack the data into an unsigned int. I have no idea which is fastest.
Unnecessary, and you''ll end up writing bit twiddling functions to access the components. Don''t do it, a struct is just fine.
Maybe there are penalties for accessing a particular byte out of an unsigned int, or maybe packing is the best choice, since it will be fast to copy around, and to unpack it only requires very fast branchless bitwise instructions.
Addressing bytes that are not on a word boundary (word size == register size) is potentially more expensive. The lowest two bytes should be easy to access individually (they would map to the old AL/AH registers, so I assume there is still logic in the CPU dedicated to accessing them). I don''t know how it would work for the upper two.
For instance, is signed or unsigned a better default choice?
Doesn''t matter performance-wise. It just depends on whether you need negative numbers or an extended high positive range.
Should using signed or unsigned be the exception?
Use signed unless you want an unsigned integer type (e.g. for sizes).
If signed is faster, then use signed unless you need unsigned or if you need to do bitwise operations. If unsigned is faster, then use unsigned unless you need signed. But I don''t know which is faster.
Neither.
Similarly, I don''t know whether it is any faster or slower to use 8-bit, 16-bit, or 32-bit data on a 32-bit cpu.
32-bit is faster.
I''m confused about this issue, and it seems like whatever I decide to go with, I feel like it was the wrong choice later on. So, any advice?
Use whatever type best represent your data. For local variables, stick to ints unless you find out you need something else. Packing is not the Panacea. Use <csdtint> where available (you can just grab it from Boost if necessary).
And if you''re still confused, I guess it''s not a good idea for me to tell you about bitfields.
[disclaimer - I don''t know if this really is a good explanatory link.]
“Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.” — Brian W. Kernighan (C programming language co-inventor)
Historically, int was intended to be the fast integral type, the one matching the native register size in your CPU.
Someone tells me that signed is faster than unsigned. Another tells me the opposite.
There are strictly no differences between signed and unsigned.
Someone says that int is fastest, but you shouldn''t use it unless you need 32-bits, because that is wasteful and using smaller data types is much better for the cache.
That would depend on the way your data is laid out, the size of your cache lines, your access patterns ... there are no good a-priori answers.
Someone tells me that you should always use unsigned if you are doing any kind of bitwise operations, and always use signed if you are doing arithmetic operations.
When you are doing bit twiddling and compare your variables to integer literals, sign changes can be the source of subtle errors, so I can see why people would prefer unsigned integers.
As for using signed or unsigned with arithmetic operations, it depends on the range of your values. If you just add up numbers, you may have need of the extra positive range provided by unsigned integers. But if you may have negative results (substractions), then you probably need signed integers.
I read stuff online that talks about data conversion assembly instructions, which makes me think that the data must be converted at runtime, which doesn''t seem to be the fastest.
floating-point to integer is an expensive conversion. Integral promotions (from smaller to larger type) is a trivial zero-expansion (unsigned, pad with zeros to the left of the number) or sign-expansion (signed, pad with copies of the sign bit). While I am no ASM guru, I assume that MOV instructions take care of that as they copy data around (can anybody confirm or correct ?), with no extra cost.
So maybe it''s better to always use the same type to avoid runtime conversion?
You''ll have to run tests to see if there is any cost.
Of course another website says all of the conversion is done at runtime in C/C++.
That should be an easy optimization. Possibly just an extension of alignment routines.
One time I showed someone a struct that had four ints. He said I should change them to unsigned chars so the size of the struct would fit into 32 bits so it could be copied faster.
If you expect to copy it around a lot, packing may make it faster - assuming you don''t need the full int range, and are happy with unsigned char.
As a side note, char is unique in that there are 3 char types: char, unsigned char, signed char. Not only plain ''char'' is considered its own type, but it is undefined as to whether it is signed or unsigned (all others default to signed). Conclusion - if it matters, specify which you mean.
Another person told me to forget the struct altogether and pack the data into an unsigned int. I have no idea which is fastest.
Unnecessary, and you''ll end up writing bit twiddling functions to access the components. Don''t do it, a struct is just fine.
Maybe there are penalties for accessing a particular byte out of an unsigned int, or maybe packing is the best choice, since it will be fast to copy around, and to unpack it only requires very fast branchless bitwise instructions.
Addressing bytes that are not on a word boundary (word size == register size) is potentially more expensive. The lowest two bytes should be easy to access individually (they would map to the old AL/AH registers, so I assume there is still logic in the CPU dedicated to accessing them). I don''t know how it would work for the upper two.
For instance, is signed or unsigned a better default choice?
Doesn''t matter performance-wise. It just depends on whether you need negative numbers or an extended high positive range.
Should using signed or unsigned be the exception?
Use signed unless you want an unsigned integer type (e.g. for sizes).
If signed is faster, then use signed unless you need unsigned or if you need to do bitwise operations. If unsigned is faster, then use unsigned unless you need signed. But I don''t know which is faster.
Neither.
Similarly, I don''t know whether it is any faster or slower to use 8-bit, 16-bit, or 32-bit data on a 32-bit cpu.
32-bit is faster.
I''m confused about this issue, and it seems like whatever I decide to go with, I feel like it was the wrong choice later on. So, any advice?
Use whatever type best represent your data. For local variables, stick to ints unless you find out you need something else. Packing is not the Panacea. Use <csdtint> where available (you can just grab it from Boost if necessary).
And if you''re still confused, I guess it''s not a good idea for me to tell you about bitfields.
“Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.” — Brian W. Kernighan (C programming language co-inventor)
quote:
Original post by Russell
Thanks Fruny. Would it be a good idea to wrap this kind of stuff up in a class so that I can switch between signed/unsigned, char/short/int/long, struct, packed bits, bitfields, or whatever else?
No!
Where necessary, use typedef s (which can be provided through class templates, yes), and check Boost's integer facilities
�Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.� � Brian W. Kernighan (C programming language co-inventor)
[edited by - Fruny on March 4, 2004 3:21:04 AM]
> There are strictly no differences between signed and unsigned.
It certainly matters when promoting integral types to float - x86 FILD loads signed data. If converting from unsigned, you either have to widen it to the next larger (signed) type, or load it normally and load+add 2*value[30:0] + (value[31] << 31) if the MSB is set.
> floating-point to integer is an expensive conversion.
It doesn''t have to be. Storing to integer without changing rounding control flags can be done in 4 clocks (as fast as any other FPU op). That means you have to set round mode to truncate at program start, or accept round-to-even mode.
MOV instructions copy same-sized data around. But yes, MOVSX and MOVZX do what you want, at no cost over MOV (on ''good'' CPUs
).
Addressing and fetching them is easy (and free) - x86 has BE (byte enable) lines that allow sub-bus-size (and unaligned also) memory accesses. It''s when they are in a register that you have a bit more trouble: either shift them down, or use the BSWAP instruction (swaps the register from bytes (0 1 2 3) -> (3 2 1 0)).
>> Similarly, I don''t know whether it is any faster or slower to use 8-bit, 16-bit, or 32-bit data on a 32-bit cpu.
> 32-bit is faster.
Not necessarily true. 1) you can always extend those to the full 32 bits at no cost. 2) 8 bit ops are equally fast, as long as you watch out for partial reg stalls (i.e. restrict yourself to al, bl, cl, dl).
It certainly matters when promoting integral types to float - x86 FILD loads signed data. If converting from unsigned, you either have to widen it to the next larger (signed) type, or load it normally and load+add 2*value[30:0] + (value[31] << 31) if the MSB is set.
> floating-point to integer is an expensive conversion.
It doesn''t have to be. Storing to integer without changing rounding control flags can be done in 4 clocks (as fast as any other FPU op). That means you have to set round mode to truncate at program start, or accept round-to-even mode.
quote:
Integral promotions (from smaller to larger type) is a trivial zero-expansion (unsigned, pad with zeros to the left of the number) or sign-expansion (signed, pad with copies of the sign bit). While I am no ASM guru, I assume that MOV instructions take care of that as they copy data around (can anybody confirm or correct ?), with no extra cost.
MOV instructions copy same-sized data around. But yes, MOVSX and MOVZX do what you want, at no cost over MOV (on ''good'' CPUs
quote:
Addressing bytes that are not on a word boundary (word size == register size) is potentially more expensive. The lowest two bytes should be easy to access individually (they would map to the old AL/AH registers, so I assume there is still logic in the CPU dedicated to accessing them). I don''t know how it would work for the upper two.
Addressing and fetching them is easy (and free) - x86 has BE (byte enable) lines that allow sub-bus-size (and unaligned also) memory accesses. It''s when they are in a register that you have a bit more trouble: either shift them down, or use the BSWAP instruction (swaps the register from bytes (0 1 2 3) -> (3 2 1 0)).
>> Similarly, I don''t know whether it is any faster or slower to use 8-bit, 16-bit, or 32-bit data on a 32-bit cpu.
> 32-bit is faster.
Not necessarily true. 1) you can always extend those to the full 32 bits at no cost. 2) 8 bit ops are equally fast, as long as you watch out for partial reg stalls (i.e. restrict yourself to al, bl, cl, dl).
quote:
Original post by Fruny
No!
Where necessary, use typedef s (which can be provided through class templates, yes), and check Boost's integer facilities
Hmmm. That boost stuff looks either confusing or overkill. I don't see the point in it.
When I said wrap it up in a class, I didn't mean create a new integer class. I meant that if I representing some integral type, do something like this:
class MyThing {
private:
typedef unsigned int MyThingType;
MyThingType myThing_;
public:
MyThing();
// ...
};
I thought this was interesting. I've never heard that there is a recursion limit in C++. I use recursion a lot, and it goes well over 17 calls...
quote:
Avoid recursion because of concern about C++'s limited guaranteed recursion depth (17).
[edited by - Russell on March 4, 2004 6:25:28 PM]
quote:
Original post by Russell
I thought this was interesting. I''ve never heard that there is a recursion limit in C++. I use recursion a lot, and it goes well over 17 calls...
That''s the template recursion limit
template<int n> struct fact
{
enum { value = n*fact<n-1>::value };
};
template<> struct fact<0>
{
enum { value = 1 };
};
int fact10 = fact<10>::value;
quote:
Hmmm. That boost stuff looks either confusing or overkill. I don''t see the point in it.
It''s a way to get information out of types, and conversely, to transparently get a type based on requirements. Sure, the code itself is complicated, but in the end, it all boils down to a typedef
As to being overkill, if you start developing code that''s intended to work regardless of the types its operating on (generic programming), these kind of utilities become extremely useful.
Did I mention that all that work is done at compile-time ?
quote:
Original post by Russellquote:
Original post by Way Walker
- all bits to be value bits, use char
What do you mean by "value bits"? I thought all bits had value...
It''s me being pedantic
Topic Locked
This topic has been locked by a moderator. New replies are not allowed.
Sign in to reply to this topic.
Advertisement