Can I use variable types such as:
float x;
int x;
or should I use
int16_t x;
int32_t x;
I am using STM32F401RE. And how to decide whether to use int16_t or int32_t or int/float.
You can use any types you have available to make the job done. All those types are available and nothing limits you from using any of them.
Use integers if you don't need float, and use float when you need it. The other thing is, some people might choose differently what to use in which situation so it's really opinion-based what you want to use and when. It's not that different from writing software for PC.
int
vs uint32_t
is opinion-based (from my software engineer point of view) is like saying that picking a LM317 vs switched buck regulator is opinion-based and either is fine for a generic PCB.
\$\endgroup\$
Just to note - the STM32F401 is an arm Cortex M4 core with single precision hardware floating point unit. With the arm, the native type is a 32 bit integer. If you wish to use 8 or 16 bit integers, then there maybe a cost in terms of code size. Nevertheless it is a tradeoff of ram usage for storage vs flash usage for code. For the most part, just write the code to use what is required. You can optimise later if necessary ( normally not necessary). Refer to the arm Cortex M4 architecture documents to better understand the tradeoffs.
As for float vs int. Floating point isn’t quite the magic bullet you’d like to think it is. The further the value moves away from zero, the less precision it has. As well, it is unwise to do equal to comparisons with floating point as there can be inherent rounding errors. So I’d suggest for the most part you use integer variables and only use float when needed.
Whilst the STM32F401 has a hardware floating point unit, using this vs software based floating point might mean you need to consider whether to save/restore the FPU state for interrupts. Enabling this means the entry/exit to the interrupt service routines are slower and more stack memory is consumed.
For an example, if you wanted a loop counter, you’d probably choose a 32bit int variable. Integer so that there are no rounding errors and 32bit as that is the native arm CM4 type.
For the most part the choice of variable types is much the same if you were writing code for an x86.
Check out this: https://stackoverflow.com/a/77105610/584518. The conclusion from that is that in a professional context:
int
, since it is non-portable. Signed numbers in general are also quite rare on embedded systems, compared to PC programming. They should only be used when you actually need them.unsigned int
.intn_t
/uintn_t
from stdint.h
. Or in some cases the uint_fastn_t
.The next question is what "n" you should be picking, i.e uint16_t
, uint32_t
etc. That's not a trivial question either.
uint_fast8_t
in case you are worried).uint32_t
.uint32_t
on a 32 bitter also have the great benefit that you don't have to worry much about Implicit type promotion rules, so it is overall a safer type to use, particularly so for beginner- to intermediately experienced C programmers.Regarding float numbers, we may note that your specific Cortex M4 comes with a single precision FPU. This is one of the few cases where float
vs double
might matter. Because as long as you stick to float
and 1.0f
floating point constants, use the dedicated single precision float
functions, i.e sqrtf
not sqrt
, sinf
not sin
, then the appropriate and fast hardware FPU instructions will be generated.
But if you mix in double
or 1.0
constants or call double
-typed library functions like sqrt
, then the compiler might decide to instead of the FPU utilize software floating point libs, which are way more inefficient. Your specific compiler could either be reasonable or unreasonable here, so read up on the compiler docs regarding how to deal with floating point if you plan to use it.
So that's quite different from PC programming where the general best practice is pretty much to always use double
and never float
.
No matter system, it is considered good practice to avoid mixing float
, double
and/or integer types in the same expression. Again because of implicit promotion pitfalls, but also because you might get very inefficient code just by accident.
The default types such as int and long can vary in size depending on the CPU architecture and compiler, leading to potential issues when transitioning between platforms. The 'stdint.h' library provides a solution by offering fixed-size integer types, ensuring clarity on the number of bits in a variable. This not only enhances code readability but also promotes portability across different platforms and CPU architectures.
Using the stdint library becomes especially beneficial in scenarios like programming for an ESP32 or Arduino, where the default int size differs (32 bits for ESP32 and 16 bits for Arduino), minimizing the risk of unnoticed discrepancies and improving overall code robustness.