Created
November 30, 2025 01:47
-
-
Save kozy4324/6beaa7f5fd5395f56a0b963934427715 to your computer and use it in GitHub Desktop.
decimal_string_to_binary_expression_float
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # 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