Common Data Types
One of the common problems encountered by newbies, and the more experienced among us to be honest, is to do with choosing suitable data types to store the information we want to hold. Note that this is nothing to do with WebbotLib but is discussed here to help avoid the pitfalls.
The use of unsuitable data types can lead to your program behaving un-expectedly with lots of time wasted in trying to understand what is going on.
What do I mean by a data type? With most micro-controllers the majority of the information you are storing, reading, using in calculations, or having returned from a calculation is some sort of number. Text tends to be read-only in a Robot environment.
There are two fundamental kinds of numbers: real numbers (also called floating point) and integers. Integers are whole numbers (like How many children do you have?) whereas real numbers can have something to the right of the decimal point (like How many dollars and cents have you got?). Note that real number can also be used to hold integers - they just end in '.0'
Since real numbers can store anything (well just about but an over-simplification) then "Can I just use real numbers throughout my program?" Well the quick answer is 'Yes' but the real answer is 'Yes - but don't do it unless you have to!' The reason is partly that real numbers take up more memory and also that micro-controllers don't contain hardware to do calculations with real numbers so some extra software gets automatically added to your code in the build process. This extra software is also slow to run by comparison with integers. Note that the added software overhead is an all or nothing thing - ie if your code doesn't use ANY real numbers then it won't be added, but as soon as you add a single real number to your program then the whole extra code gets added.
The 'C' standard defines two data types for real numbers: 'float' and 'double'. The avr-gcc C compiler treats them both the same so they are inter-changeable. So I could do the following:-
float a = 1.27; // a real number
float b = 3.45; // another real number
float c = a * b; // do a real number multiplication and store the result: 4.3815
Real numbers aren't the real issue - you either have to use them or you don't. If you've got lots of memory available (such as on the Axon series) then you may not care about the overhead and slower processing.
What about integers?
Well the first sub-division of integers is to decide whether they can hold negative numbers or not. If they can then they are called 'signed integers' but if they only hold positive numbers then they are called 'unsigned integers'. Once again - we could take the careful approach and assume that everything is 'signed'. But sometimes this doesn't make sense: eg if we wanted to store the distance between two objects then it cannot be negative.
The next division of integers is the range of values they can store. This is important as it dictates how much space is required to store the value. The available values are historically hardware dependent but for AVR chips the choice is: 1 byte, 2 byte, 4 byte. Given that there are 8 bits per byte then these can also be called: 8 bits, 16 bits, 32 bits.
You will see in the description that follows that an 'unsigned 16 bit' number can store a maximum value that is almost twice the value that a 'signed 16 bit' number can hold and that the higher the number of bits (ie the more space required) then the higher the maximum value that can be stored.
The integer data types used within WebbotLib are as follows:
uint8_t Requires 1 byte of storage, can hold an unsigned number between 0 and +255
int8_t Requires 1 byte of storage, can hold a signed number between -128 and +127
uint16_t Requires 2 bytes of storage, can hold an unsigned number between 0 and +65535
int16_t Requires 2 bytes of storage, can hold a signed number between -32768 and +32767
uint32_t Requires 4 bytes of storage, can hold an unsigned number between 0 and +4294967295
int32_t Requires 4 bytes of storage, can hold a signed number between -2147483648 and +2147483647
So when deciding what kind of data type to use then start at the top of the list and work your way down until you find the first entry that matches the values you may need to store.
The problems I eluded to earlier are caused by choosing the wrong data type. The avr-gcc compiler is quite relaxed about what value you store in a variable and there are no runtime checks at all to make sure that the value you are storing in a variable can be stored correctly.
int8_t myVar = 130; // oops an int8_t cannot store that value
int8_t var1 = 100; // yep that stores ok
int8_t var2 = 3; // yep that stores ok
int8_t result = var1 * var2; // answer is +300 and that won't fit !!
So the quick rule of thumb when you are doing things like add or multiply is to look at the two values that are being worked with and work out what the range of the result could be.
ie if you are multiplying two int8_t together then worst cases are -128 x +127 or +127 x +127 giving results of -16,256 and +16,129. Scanning down the table then the best type to use to hold the answer correctly is an int16_t
However if you know that the two int8_t will only actually store a maximum used range of -10 to +10 then the worst case results are -10 x +10 = -100 and +10 x +10 = +100 in which case you could store the result in an int8_t.