# If a decimal string with at most 15 significant digits is converted to the # IEEE 754 double-precision format, giving a normal number, and then converted # back to a decimal string with the same number of digits, the final result # should match the original string. # If an IEEE 754 double-precision number is converted to a decimal string with # at least 17 significant digits, and then converted back to double-precision # representation, the final result must match the original number: # ("%.16e" % self).to_f == self class Float def to_scientific sign, significand, exponent = split sign + significand + (exponent != 0 ? "e#{exponent}" : '') end # Converts value to HTML formatted scientific notation. def to_html(show_unity: true) result, significand, exponent = split result += significand if significand != '1' || (exponent == 0 && show_unity) if exponent != 0 result += "×" if significand != '1' result += "10% d" % exponent end result.html_safe end # Assume #finite? is true. def limit(precision = DIG) return 0.0 if precision.zero? sign, significand, exponent = split(DIG_MAX) "#{sign}#{significand[..precision]}e#{exponent}".to_f end private SPLIT_FLOAT = /(-?)(.*?)\.?0*e(.*)/ # Format `%e` displays starting from significant digit (not 0). def split(digits = DIG) return ['', to_s[..2], 0] unless finite? return ['', '0', 0] if digits.zero? ("%.#{digits - 1}e" % self).match(SPLIT_FLOAT).captures .then { |sign, significand, exponent| [sign, significand, exponent.to_i] } end # The maximum number of significant decimal digits in a double-precision # floating point number. DIG_MAX = 17 # Smallest and largest double-precision floating point numbers with DIG # precision. # TODO: change MIN_15 to MIN.ceil(MIN_10_EXP - DIG) after #ceil fix in Ruby # v4.0.5: https://bugs.ruby-lang.org/issues/22079 MIN_15 = MIN.ceil(-(MIN_10_EXP - 1)) MAX_15 = MAX.floor(-(MAX_10_EXP - DIG + 1)) end