Why can a float represent 3,000,000,000 and also 3,000,000,256, but nothing in between?
Tags: float
Published: 2021-02-21 09:48:07

---
title: Why can a float represent 3,000,000,000 and also 3,000,000,256, but nothing in between?
published: true
description: 
tags: float
//cover_image: https://direct_url_to_image.jpg
---

I read a statement in the book **Programming C# 8.0** which I can't understand at first. I quoted it here: <blockquote>As floating-point numbers get larger, the values that can be represented get farther apart—a float can represent 3,000,000,000 and also 3,000,001,024, but nothing in between.</blockquote>
We all know that integer data types such as **int** can store consecutive numbers. Float can not only store integers but also numbers with a fraction. Why can't float represent any in-between number, e.g. 3,000,000,001?

First, let's look how float represents 3,000,000,000 in the memory. A float consists of 4 bytes. The whole bits are divided into three sections.
<table>
    <thead>
        <tr>
            <th>Bit No</th>
            <th>Size</th>
            <th>Field Name</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>31</td>
            <td>1 bit</td>
            <td>Sign (S)</td>
        </tr>
        <tr>
            <td>23-30</td>
            <td>8 bits</td>
            <td>Exponent (E)</td>
        </tr>
        <tr>
            <td>0-22</td>
            <td>23 bits</td>
            <td>Mantissa (M)</td>
        </tr>                
    </tbody>
</table>
The formula to calculate the value is: (-1)<sup>S</sup> × (1.0 + 0.M) × 2<sup>E-bias</sup>
The bias used in the formula is defined as 127 for the **float** data type.

A piece of C# code is used to find out the bytes:
```C#
using System;

namespace FundamentalDataTypes
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(BitConverter.IsLittleEndian); // get the endianness

            byte[] bytes = BitConverter.GetBytes(3_000_000_000F);
            foreach (byte b in bytes)
            {
                Console.WriteLine(Convert.ToString(b, 2).PadLeft(8, '0'));
            }
            
        }
    }
}
```
The output is:
True
01011110
11010000
00110010
01001111

I need to know the endianness so that I can assemble the bytes in a correct order. The **True** in the first line of the output means that the endianness is **Little Endian** so the first byte in the second line of the output is the least-significant byte of the value. 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1dpn7dh7xc05b4ihhga9.png)

S = 0 (This number is a positive number)
E = 158
M = 0.01100101101000001011110
By using the formula (-1)<sup>0</sup> × (1.01100101101000001011110) × 2<sup>31</sup>, we can get the final bits **1011 0010 1101 0000 0101 1110 0000 0000** which are exactly the same as the ones stored in an unsigned int.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zyadwfdj1c2nlsft4srt.png)
  
Let's analyze the above bits in the unsigned int. The first bit in position 31 is 1 which is implicit in the float. The bits from the position 30 to 8 which sums to 23 bits compose the Mantissa in the float. Any in-between number uses the bits from the position 7 to 0. However, these last 8 bits can't be represented by the float because float needs to set aside 8 bits to accommodate the Exponent. That's the reason why float can't accurately represent 3,000,000,001. 

The next number after 3,000,000,000 that can be accurately represented by a float is obtained by setting the bit in position 8 of the above bits to 1. It is 
 3,000,000,256.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/36brghlityw69dc7v8sm.png)

Any in-between number is approximated to either 3,000,000,000 or 3,000,000,256 (not 3,000,001,024, an error in the book). 

# References
https://en.wikipedia.org/wiki/Endianness
https://www.doc.ic.ac.uk/~eedwards/compsys/float