Please tell me how do MCUs handle decimal numbers like '23.3', '3.24' etc? How is it stored in a memory register? I know that I have to use float data type when handling these numbers while programming. But actually what is happening inside an MCU while handling these types. Also tell me how do MCUs without FPU unit handle Float datatype.
5 Answers
Numbers inside typical micrcontrollers don't have decimal points at all. They are binary integers. There is no decimal going on inside the machine. The compiler or assembler may let you specify constants that way, but they get converted to binary before the machine sees them.
However, you can decide whatever units you like for the integer values. For example, suppose you wanted to represent dollars inside a micro. It can't natively do $3.21, but it could do 321 cents. The micro is just operating on the value 321, but you know that it represents units of 1/100 dollars.
That's just one example to illustrate the concept of arbitrary units. Often numbers are represented with several binary fraction bits. That's the same as saying each count represents a value of 2-N, where N is the number of fraction bits. This representation is called "fixed point". You decide up front how much resolution you need, and pretend there are enough bits to the right of the imagined binary point to support that resolution. For example, lets say you need to represent something to at least a resolution of 1/100. In that case you'd use at least 7 fraction bits since 27 = 128. That will actually give you a resolution of 1/128.
The machine has no idea this is going on. It will add and subtract these numbers as ordinary integers, but everything still works out. It gets a little tricky when you multiply and divide fixed point values. The product of two fixed point values with N fraction bits will have 2N fraction bits. Sometimes you just keep track of the fact that the new number has 2N fraction bits, or sometimes you might shift it right by N bits to get back to the same representation as before.
Floating point is the same thing, but the number of fraction bits are stored along with the integer part so that this adjustment can be made at runtime. Performing math operations on floating point numbers can take a bunch of cycles. Floating point hardware does all this for you so that the operations complete quickly. However, the same manipulations can be performed in software too. There is no reason you can't write a subroutine to add two floating point numbers, just that it would take a lot longer than dedicated hardware doing the same thing.
I have defined a 3-byte floating point format for 8 bit PICs and written a bunch of routines to manipulate them. Microcontrollers are usually dealing with real world values with 10 or 12 bits precision at most. My floating point format uses 16 bits of precision, which is good enough for several intermediate calculations.
I also have a 32-bit format for the 16 bit PICs. This uses one 16-bit word for the mantissa, which speeds calculations since these PICs can operate on 16 bits at a time.
These routines are included in my PIC Development Tools release. After installation, look at files with "fp24" in their name in the SOURCE > PIC directory, and "fp32f" in the SOURCE > DSPIC directory.
-
\$\begingroup\$ Floating Point Standard There is also an IBM standard which just messed around with the bias. \$\endgroup\$ Commented Jun 23, 2011 at 18:15
Fixed point arithmetic is usually used for performing fractional calculations in MCUs.
The trick is to say that (for example), the upper 16 bits of a uint32_t
are before the decimal point and the lower 16 are after, ie. the stored integer is in 1/2^16ths. With some small caveats, regular arithmetic "just works".
Here's an overview.
-
\$\begingroup\$ up vote from me for that "Overview" link. \$\endgroup\$– 0xakhilCommented Jun 24, 2011 at 17:38
Unless you MCU is a DSP with floating point multiplier, everything is stored as 16 bit (or 8 or 32 depending on your platform) numbers. That is all the actual MCU knows about.
Above this you have your "C" code and C compiler. The compiler "knows" about various data types such as char, int's, uint's, floats, doubles and so on.
The most common representation of floats on hardware is with a IEEE format. This separates the mantissa from the exponent and uses two 16 bit words to store the information. Check out this wiki article on IEEE number formats.
So it is the compiler that knows where the mantissa and exponent is and applies the maths to it. Remeber learning about logarithms? how they made maths easier by adding power when you wanted to multiple? well the c compiler does something similar with the exponnents and multiplies the mantissa to calculate the answer. So for a floating point multiplication the compiler will create assembler code that adds the exponents and performs multiplication of mantissa.
the MCU knows nothing of the number!!! just what it is told to do, load a memory into a register, add a memory to the register and set the carry flag if required, and so on until the multiplication is complete.
It is the C compiler and your code that "abstracts" the concept of numbers, decimal points and so on from the MCU.
On a side note some language also support the "decimal" data type which is useful for financial systems - not common on embedded platforms as floats use less memory and perform efficently.
The same way full blown processors without an fpu (most ARM's for example) handle floating point. With a software fpu. There is a library that performs the math/bitwise operations. If you remember doing addition, multiplication, etc in grade school with pencil and paper, not much changed the day you went from whole numbers to numbers with a decimal point. You did the math the same way you just had to either adjust the numbers so the decimal points lined up before you started (addition and subtraction) or after you finished (multiplication or division). Hard and soft fpus are no different they adjust the bits before and after the operation but the operation is basically a whole number operation.
I dont remember exactly where to find it now, but texas instruments had a very good document related to their DSP products. It explained their floating point format and went so far as to explain how the operations worked. Their format does not have rounding and denormals and infinity and quiet and signaling nans like IEEE, so it is much easier to understand as well as significantly faster than IEEE formats. Once seeing that format in action you have taken your first step toward the IEEE format. Rounding takes some explanation and thought, but the rest of it, the basics of sign, exponent, and mantissa are the same.
It is very expensive, resource wise (memory, flash, cpu cycles) to use soft float libraries and I would discourage it in an embedded system or microcontroller. 12.3 and 24.5 are quite easy to manage as the integers 123 and 245, for example, so long as you remember or perhaps if you make sure all your associated math understands that the numbers are all multiplied by ten and if/when you display to a user on that conversion you add the decimal point. Saves tons of code and performance. Of course integer division is a bad thing with microcontrollers and embedded systems as well as most processors do not have a divide instruction, and that includes the divide by 10 to convert to decimal to display to the user. And the same answer, the division that you get from your C code is done using a library. So floating point division on these systems is expensive squared.
Floats are stored in binary 32 bit format and the 1st bit is to indicate if the float is pos/neg number, the next 8 bits are the exponent -127, then there are 23 bits that are the complete number including the decimal place, ie:
1 00010001 00010001000000000000000
So 1 is that it is negative, the next 8 bits are to indicate the exponent in this case:
0001 0001 = 17 (17-127 = -110)
Then the mantissa is divided:
(1+1/4+1/128)2^5
2^5
was the movement of the decimal place when the float was moved to binary.
Yhe result does loose some digits when converting but they are close. 1.5ex10-110
I may have made errors following others, but this is the general idea of how floats are saved in memory.
-
2\$\begingroup\$ This post may contain traces of information, but it's hidden in some unformatted chat message wall-of-text. Stack Exchange is not Facebook, so please try to use a more professional language and please use the formatting tools made available to make the information legible, starting with the paragraphs. \$\endgroup\$– pipeCommented Apr 4, 2017 at 23:13