How a JavaScript Number is Defined
JavaScript number is represented in IEEE754 which is double precision binary floating point (binary64), it is in scientific notation and using 2 as base. There are 64 bits in a number, and they are split into 3 parts (from high to low bits):
- The first bit is for sign: 0 - positive; 1 - negative
- The next 11 bits are exponent part
- The last 52 bits are for mantissa / fraction

So, a float number is calculated as: (-1) ^ sign * (2 ^ exponent) * significand
Note: as the exponent part of a scientific notation could be either positive or negative, the actual exponent value for a binary64 number should be calculated by subtracting exponent bias (which is the middle value 1023) from the 11 bit exponent value.
The standard also defines the significand value to be between [1, 2).
As the first number of significand part is always 1, so it is implied and not presented in the above figure. So, basically the significand part has 53 bits precision actually, and the red part in above figure is just the mantissa or fraction part.
0.1, 0.2 and 0.3 in binary64 Format
Based on the standard, it's not hard to find 0.1, 0.2 and 0.3 in binary64 format (you can calculate either manually or by this tool http://bartaz.github.io/ieee754-visualization/):
0.1
0 01111111011 1001100110011001100110011001100110011001100110011010
and in scientific notation, it is
1.1001100110011001100110011001100110011001100110011010 * 2e-4
Note: the significand part is in binary format, and the following numbers are in same format
0.2
0 01111111100 1001100110011001100110011001100110011001100110011010
and in scientific notation, it is
1.1001100110011001100110011001100110011001100110011010 * 2e-3
0.3
0 01111111101 0011001100110011001100110011001100110011001100110011
and in scientific notation, it is
1.0011001100110011001100110011001100110011001100110011 * 2e-2
Steps to Add Up 2 binary64 Numbers
Step 1 - Align the exponents
- Shift the significand for the number which has smaller exponent
- Shift the significand right
- Increase the exponent by 1 for every significand shift until both exponents are same
- After shift, the significand should be round up.
Step 2 - Add up the significand
if the added up significand is not satisfying [1,2)
requirement, shift it into that range and change the exponent
After shift, the significand should be round up.
0.1 + 0.2 + 0.3 == 0.6000000000000001
As above explained, 0.1
has exponent -4
and 0.2
has exponent -3
, so need to do exponent alignment first:
Shift 0.1
from
1.1001100110011001100110011001100110011001100110011010 * 2e-4
to
0.1100110011001100110011001100110011001100110011001101 * 2e-3
Then add the significand
0.1100110011001100110011001100110011001100110011001101
with
1.1001100110011001100110011001100110011001100110011010
we get added up significand value:
10.0110011001100110011001100110011001100110011001100111
But it is not in range [1,2)
so need right shift it (with round up) to:
1.0011001100110011001100110011001100110011001100110100 (* 2e-2)
then add it to
0.3 (1.0011001100110011001100110011001100110011001100110011 * 2e-2)
we get:
10.0110011001100110011001100110011001100110011001100111 * 2e-2
Again, we need shift and round up it, and finally get the value:
1.0011001100110011001100110011001100110011001100110100 * 2e-1
it is exactly the value of 0.6000000000000001
(decimal)
With same workflow, you get calculate 0.1 + (0.2 + 0.3)
Tools
This web page http://bartaz.github.io/ieee754-visualization/ helps you quickly convert a decimal number to binary64 format, you can use it to verify the calculation steps.
If you are processing a single precision binary float number, you would refer to this tool: http://www.h-schmidt.net/FloatConverter/IEEE754.html