Skip to content

Instantly share code, notes, and snippets.

@chriskillpack
Last active December 26, 2015 22:59
Show Gist options
  • Save chriskillpack/7227089 to your computer and use it in GitHub Desktop.
Save chriskillpack/7227089 to your computer and use it in GitHub Desktop.
To start my exploration of IEEE754 (floating-point numbers) I decided to write a program that converts a decimal number into it's IEEE754 32-bit counterpart.
#!/usr/bin/env ruby
# Convert a decimal number into its 32-bit IEEE754 floating-point
# representation.
# Example:
# $ ./floating-point.rb -1313.3125
# Decimal: -1313.3125
# As binary fraction: 010100100001.0101
# Exponent = ^10 = 1024 = 10001001
# Mantissa = 01001000010101000000000
# 11000100101001000010101000000000
# seeeeeeeemmmmmmmmmmmmmmmmmmmmmmm
# 0xC4A42A00
#
# When cross-checked with the output of the truth program for 39887.562 the
# results differ: 0x471BCF8F versus 0x471BCF90 (truth). Need to investigate but
# I suspect rounding modes are affecting fraction computation. (e.g. Nearest vs
# Zero).
MANTISSA_DIGITS = 23
def number_components(number)
[number.to_i, number.modulo(1.0)]
end
def integral_to_binary(number)
# Work from LSB to MSB of the number
index = 0
mask = nil
digits = []
begin
mask = 1 << index
digits.push((number & mask) >> index)
index += 1
end while mask <= number
digits.reverse.join('')
end
def fraction_to_binary_fraction(number)
# We know that the number is < 1 because it is the fractional component. If
# we repeatedly double the number and harvest the integer part of the result
# we can generate a sequence of 0's and 1's that is the binary fraction.
x = number
digits = []
MANTISSA_DIGITS.times do |i|
x *= 2
digits << x.to_i # Take the integer component
x = x.modulo(1.0) # Discard integer component
break if x.zero? # Unlikely to happen due to nature of the beast, but
# here for clean fractions.
end
digits.join('')
end
def normalize_parts(integral, fractional)
# Form is integral.fractional
# 1001101111001111.10010000000000000000000
# IEEE Normalized Form: adjust exponent so that left-most 1
# is only bit before decimal point.
# 1.00110111100111110010000000000000000000
# This function does not return the leading 1 because it is implicit in the
# IEEE spec.
# This code isn't the best, I will rewrite when I can think of something
# more cleaner.
# Combine the two parts into a single representation with a decimal
# point.
combined = integral + '.' + fractional
# Find the left-most 1-bit.
left_most_one = (combined.index('1'))
return [-127, '0' * 23] unless left_most_one # Zero is a special case.
decimal_point = integral.length
# Exponent is the number of digits between the left-most 1-bit and the
# decimal point.
exponent = decimal_point - left_most_one - 1
# Which side of the decimal point does the 1-bit lie?
if left_most_one < decimal_point
# Integral part.
# Take everything to the right of the 1-bit.
integral_remainder = integral[(left_most_one+1)..-1]
# Join it with the fractional part.
mantissa = integral_remainder + fractional
else
# Fractional part.
# We add one extra exponent power so the 1-bit crosses the decimal point.
exponent += 1
# Take everything to the right of the 1-bit.
fractional_remainder = fractional[(left_most_one - decimal_point)..-1]
# By definition the integral part will be all 0-bits, so it can be ignored.
mantissa = fractional_remainder
end
# Trim mantissa to 23 bits and 0 pad short mantissas.
normalized_mantissa = mantissa[0...23].ljust(23, '0')
[exponent, normalized_mantissa]
end
if ARGV.length.zero?
puts "Usage: #{$0} number"
exit 1
end
input_number = ARGV[0].to_f
puts "Decimal: #{input_number}"
integral, fraction = number_components(input_number.abs)
integral_bits = integral_to_binary(integral)
fraction_bits = fraction_to_binary_fraction(fraction)
puts "As binary fraction: #{integral_bits}.#{fraction_bits}"
# Determine sign bit
sign = (input_number >= 0) ? 0 : 1
# Normalize integral and fractional parts
exponent, mantissa = normalize_parts(integral_bits, fraction_bits)
exponent_s = (exponent+127).to_s(2).rjust(8, '0')
print "Exponent = ^#{exponent} = #{2 ** exponent} = #{exponent_s}"
puts
puts "Mantissa = #{mantissa}"
ieee754 = sign.to_s + exponent_s + mantissa
puts ieee754
puts 'seeeeeeeemmmmmmmmmmmmmmmmmmmmmmm'
puts "0x#{ieee754.to_i(2).to_s(16).upcase}"
// The source of truth for floating point number representation.
// For a given decimal number, print out the IEEE754 number in
// hex and binary.
//
// Compile:
// $ g++ -Wall -std=c++11 -o truth floating-point.cc
// Example output:
// $ ./truth
// -1313.312500
// 0xC4A42A00
// 11000100101001000010101000000000
// seeeeeeeemmmmmmmmmmmmmmmmmmmmmmm
// sign = 1
// exponent = 10001011 = ^10 = 1024
// mantissa = 001001000010101000000000
// 1.001001000010101000000000 x 2*10
#include <stdio.h>
#include <cstdint>
// TODO: Take this as a command-line argument.
const float DECIMAL_NUMBER = -1313.3125;
// Print a number as binary.
// Taken from http://stackoverflow.com/a/3974138 with minor adaptions.
void printBits(size_t const size, void const * const ptr) {
uint8_t *b = (unsigned char*) ptr;
uint8_t byte;
int i, j;
for (i=size-1;i>=0;i--)
{
for (j=7;j>=0;j--)
{
byte = b[i] & (1<<j);
byte >>= j;
printf("%u", byte);
}
}
}
void destructure_IEEE754(float number, bool* negative,
uint32_t* exponent, uint32_t* mantissa) {
uint32_t num = *((uint32_t*)&number);
// Sign bit is bit 31.
*negative = (num & 0x80000000) >> 31;
// Exponent in bits 23-30. Subtract the exponent offset (+127).
*exponent = ((num >> 23) & 255) - 127;
// Mantissa in bits 0-23.
*mantissa = num & 0x7FFFFF;
}
int main(int argc, char** argv) {
printf("%f\n", DECIMAL_NUMBER);
unsigned int num = *((unsigned int*)&DECIMAL_NUMBER);
printf("0x%0X\n", num);
printBits(sizeof(num), &num);
puts("");
printf("seeeeeeeemmmmmmmmmmmmmmmmmmmmmmm\n");
// Display the components of the number.
bool sign;
uint32_t exponent, exponent_offset, mantissa;
destructure_IEEE754(DECIMAL_NUMBER, &sign, &exponent, &mantissa);
printf("sign = %u\n", (int)sign);
printf("exponent = ");
exponent_offset = exponent - 127;
printBits(1, &exponent_offset);
printf(" = ^%d = %u", exponent, 1 << exponent);
puts("");
printf("mantissa = ");
printBits(3, &mantissa);
puts("");
// Display as scientific notation.
printf("1.");
printBits(3, &mantissa);
printf(" x 2*%d\n", exponent);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment