• Martin Thoma
  • Home
  • Categories
  • Tags
  • Archives
  • Support me

A practical approach to floats

Contents

  • A practical approach to floats
    • Python Solution
    • Zero Representation

If you make a computer science degree, you will have to learn how numbers are internally represented. Most of the time, you get explanations like the pictures below:

IEEE 754 single precision
IEEE 754 single precision
Example of a floating point number
Example of a floating point number

You will (have to) learn how IEEE 754 floats are structured on a bit-wise level. But I also like to check if it is correct, what I've learned.

So this is how you can check it:

#include <stdint.h>
#include <stdio.h> // printf
#include <limits.h> // INT_MAX, UINT_MAX, ...
#include <math.h>   // needed for NAN
union myUnion {
    uint32_t i; // unsigned integer 32-bit type (on every machine)
    float f; // a type you want to play with
};

void printValue(union myUnion u) {
    printf("uint32_t\t:\t%u\n", u.i);
    printf("Bits\t\t:\t");
    for (int i = 31; i >= 0; i--) {
        printf("%i", (u.i >> i) % 2);
        if (i != 0 && i % 4 == 0) {
            printf(".");
        }
        if (i == 31 || i == 23) {
            printf("|");
        }
    }
    printf("\nNumber\t\t:\t%0.10f\n\n", u.f);
}

void setSign(union myUnion *u, char sign) {
    u->i = (u->i & (0xffffffff - (1 << 31))) + (sign << 31);
}

/**
 * The exponent has 8 bits.
 * When all bits are 0, you switch to denormalized numbers.
 * When all bits are 1, you get either NaN or infinity, depending on
 * your characteristic. If the characteristic is 0, you get infinity.
 * Otherwise NaN.
 */
void setExponent(union myUnion *u, char exponent) {
    u->i = (u->i & (0xffffffff - (0xff << 23))) + (exponent << 23);
}

/**
 * The mantissa has 23 bits.
 */
void setMantissa(union myUnion *u, int mantissa) {
    u->i = (u->i & (0xffffffff - (0xff << 0))) + (mantissa << 0);
}

int main() {
    union myUnion testVar;

    printf("Manual guessing\n");
    testVar.i = 0;
    setSign(&testVar, 1);
    setExponent(&testVar, 0x01);
    setMantissa(&testVar, 0x00);
    printValue(testVar);

    printf("What does UINT_MAX evaluate to?\n");
    testVar.i = UINT_MAX;
    printValue(testVar);

    printf("What does nan evaluate to?\n");
    testVar.f = NAN;
    printValue(testVar);

    printf("The example above and switched first bit on\n");
    testVar.i = 0xbf200000;
    printValue(testVar);
}

I think I have tried all interesting values. Have fun trying it yourself ☺

(hmm ... I could also try to make a visualization ... I will think about this when I have more time)

Python Solution

Prerequesites:

pip install bitstring

Python code convert.py:

# Third party modules
import bitstring


def float_to_bin(number: float) -> str:
    bit_array = bitstring.BitArray(float=number, length=32)
    return bit_array.bin


def bin_to_float(number: str) -> float:
    bit_array = bitstring.BitArray(bin=number)
    return bit_array.float


for number in [0, -0, +0, 12, 12.0]:
    print(f"{number:4.0f}: {float_to_bin(number)}")

for bits in ["00000000000000000000000000000000"]:
    print(f"{bits}: {bin_to_float(bits)}")

Zero Representation

Zero has two representations: 00000000000000000000000000000000 which is positive 0 and 10000000000000000000000000000000 which is negative zero. They are considered equal in all programming languages I know.

You can get the sign in Python like this:

def get_sign(number):
    """
    >>> get_sign(-12)
    -1.0
    >>> get_sign(12)
    1.0
    >>> get_sign(-0.0)
    -1.0
    >>> get_sign(+0.0)
    1.0
    """
    return math.copysign(1.0, number)

Published

Okt 23, 2012
by Martin Thoma

Category

Code

Tags

  • C 23
  • float 1
  • IEEE 754 1
  • Python 141

Contact

  • Martin Thoma - A blog about Code, the Web and Cyberculture
  • E-mail subscription
  • RSS-Feed
  • Privacy/Datenschutzerklärung
  • Impressum
  • Powered by Pelican. Theme: Elegant by Talha Mansoor