DEV Community

Matthew Gong
Matthew Gong

Posted on

Why can a float represent 3,000,000,000 and also 3,000,000,256, but nothing in between?

I read a statement in the book Programming C# 8.0 which I can't understand at first. I quoted it here:

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.

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.

Bit No Size Field Name
31 1 bit Sign (S)
23-30 8 bits Exponent (E)
0-22 23 bits Mantissa (M)

The formula to calculate the value is: (-1)S × (1.0 + 0.M) × 2E-bias
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:

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'));
            }

        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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

S = 0 (This number is a positive number)
E = 158
M = 0.01100101101000001011110
By using the formula (-1)0 × (1.01100101101000001011110) × 231, 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

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

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

Top comments (0)