Skip to content

Instantly share code, notes, and snippets.

@kozy4324
Created November 30, 2025 01:47
Show Gist options
  • Select an option

  • Save kozy4324/6beaa7f5fd5395f56a0b963934427715 to your computer and use it in GitHub Desktop.

Select an option

Save kozy4324/6beaa7f5fd5395f56a0b963934427715 to your computer and use it in GitHub Desktop.
decimal_string_to_binary_expression_float
# Based on the algorithm described in:
# https://www.exploringbinary.com/correct-decimal-to-floating-point-using-big-integers/
def decimal_string_to_binary_expression_float(input)
significant_digits = input.gsub(".", "").gsub(/e[0-9]+$/, "")
numerator = significant_digits.to_i
exponent10 = input.index(/e[0-9]+/) ? input.match(/e([0-9]+)/)[1].to_i : 0
exponent10 = input.index(".") ? input.index(".") - significant_digits.size + exponent10 : exponent10
if exponent10 < 0
denominator = 10 ** -exponent10
else
denominator = 1
numerator *= 10 ** exponent10
end
puts "1. Express as an integer times a power of ten"
puts "#{input} = #{numerator} * 10^#{exponent10}"
puts
puts "2. Express as a fraction"
puts "#{numerator} / #{denominator}"
puts
lower = (1 << 52) * denominator
upper = (1 << 53) * denominator
scale = 0
scaled_numerator = numerator
scaled_denominator = denominator
if scaled_numerator <= lower
while scaled_numerator <= lower
scaled_numerator <<= 1
scale += 1
end
elsif scaled_numerator > upper
while scaled_numerator > upper
scaled_numerator >>= 1
scale -= 1
end
scaled_numerator = numerator
scaled_denominator = denominator * 2 ** -scale
end
puts "3. Scale into the range [2^52,2^53)"
if scale == 0
puts "#{numerator} / #{denominator} = #{scaled_numerator} / #{denominator}"
elsif scale > 0
puts "#{numerator} * 2^#{scale} / #{denominator} = #{scaled_numerator} / #{scaled_denominator}"
else
puts "#{numerator} / 2^#{-scale} = #{scaled_numerator} / #{scaled_denominator}"
end
puts
quotient, remainder = scaled_numerator.divmod scaled_denominator
round_up = false
if remainder == 0
# do nothing
elsif remainder < (scaled_denominator / 2)
# do nothing
elsif remainder > (scaled_denominator / 2)
round_up = true
elsif remainder == (scaled_denominator / 2)
if scaled_denominator % 2 == 0
# do nothing
else
round_up = true
end
end
rounded_quotient = quotient + (round_up ? 1 : 0)
puts "4. Divide and round"
puts "#{scaled_numerator} / #{scaled_denominator} = #{quotient} remainder #{remainder}"
puts "quotient rounds #{round_up ? :up : :down} to #{rounded_quotient}"
puts
puts "5. Express in normalized binary scientific notation"
puts "#{rounded_quotient} converts to #{rounded_quotient.to_s(2)}"
puts "Unnormalized binary scientific notation : #{rounded_quotient.to_s(2)} * 2^#{-scale}"
puts "The unscaled and normalized value becomes: #{rounded_quotient.to_s(2).insert(1, ".")} * 2^#{-scale} * 2^52"
puts "Normalized binary scientific notation : #{rounded_quotient.to_s(2).insert(1, ".")} * 2^#{52 - scale}"
puts
sign = 0
exponent = (52 - scale) + 1023
puts "6. Encode as a double"
puts "The sign field : #{sign}"
puts "The exponent field: #{exponent} (#{52 - scale} + 1023)"
puts "The fraction field: #{rounded_quotient - (1 << 52)} (#{rounded_quotient} - 2^52)"
puts
sign = "0"
exponent = (1023 + (52 - scale)).to_s(2)
significand = rounded_quotient.to_s(2)[1..]
binary_expression_float = "#{sign}#{exponent}#{significand}"
puts "input : #{input} => #{[input.to_f].pack("G").unpack1("B*")}"
puts "result: #{" " * input.size} #{binary_expression_float} => #{[binary_expression_float].pack("B*").unpack1("G")}"
puts
binary_expression_float
end
decimal_string_to_binary_expression_float("3.14159")
decimal_string_to_binary_expression_float("1.2345678901234567e22")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment