Use code MD50 on Firmware Engineer's Arsenal to get a 50% off! end in:

-

0.1 is not 0.1. Yups!


Computers can’t store 0.1 accurately. It is stored as 0.1000000015! In this post we explore and take a look at what is really going on.

fig 1. computers cannot accurately store the value 1.0

Don’t trust me? Don’t have to. Compile and run the following C code. Assuming the code above is saved in main.c, you can compile and run the compiled code as shown below -

#include <stdio.h>

void main() {
  float value = 0.1;

  printf("value: %0.10f\n", value);
}
main.c
~/
❯ gcc main.c

~/
❯ ./a.out
0.1000000015
output on the terminal

Computers don’t store 0.1 as 0.1? That is correct!

How is 0.1 represented?

Try another experiment. Compile and run the following -

#include <stdio.h>

void main() {
  float value = 0.1;

  printf("value: %0.10f\n", value);
  printf("In Hex: 0x%x\n", *(unsigned int *)&value);
}
main.c
~/
❯ gcc main.c
❯ ./a.out
value: 0.1000000015
In Hex: 0x3dcccccd
output on the terminal

Whats going on?

Say hello to - IEEE 754 floating-point format :)

A floating-point number is typically represented using the IEEE 754 standard for single-precision floating-point format. This format uses 32 bits (or 4 bytes) of memory to represent a floating-point number.

The single-precision floating-point format consists of three parts: the sign bit, the exponent, and the mantissa (also known as the significand).

The exponent is an 8-bit field that represents the magnitude of the number. It is biased by 127, which means that an exponent of 0 represents a very small number (close to zero), while an exponent of 255 represents a very large number (such as infinity).

The mantissa is a 23-bit field that represents the significant digits of the number. It includes the fractional part of the number and is used to store the precision of the number.

fig 2. IEE754 based interpretation of the 32 bits as a floating point number.

Notice that the fractional part is xxx.. in 1.xxx... and that, every number is reduced to 1.something and the exponent is adjust accordingly.

Back to 0x3dcccccd

32-bit representation of the number 0.1 using the single-precision floating-point format: 0x3dcccccd(hex) which in binary translates to -

0 01111011 10011001100110011001101

Breaking this down, we can see that:

fig 3. Computing the floating point value based on the IEEE754 format.

  • The sign bit is 0, which means that the first bit is 0.
  • The exponent is 01111011, which represents 123 in decimal. Adding the bias of 127 gives us an exponent of -4.
  • The mantissa is 10011001100110011001101. To convert this to decimal, we can interpret it as a binary fraction by placing a binary point at the left end and multiplying each bit by the corresponding power of two:
0 01111011 10011001100110011001101
= 1.10011001100110011001101(binary) x 2^-4
= 1.60000002384(decimal) x 2^-4
= 0.1000000015(decimal)

Try

There are many more decimal numbers that cannot be represented accurately. Can you guess some?


Read more:

Harnessing Volatile and Const in Embedded C