Module: Ruby::Rego::GoNumberFormat
- Defined in:
- lib/ruby/rego/go_number_format.rb
Overview
Renders a number’s shortest significant digits the way Go’s strconv / math/big ‘g’ verb does
(the format OPA’s FloatToNumber applies to a non-integer big.Float result): shortest digits, with
scientific notation when the decimal exponent is < -4 or >= 6, and an exponent that is always
signed and zero-padded to at least two digits (1e-05, 1.2345675e+06). Integer-valued results
are formatted by the caller as a full decimal (Go’s ‘f’ verb), so this module only renders the
fractional (‘g’) case.
Both the arbitrary-precision Number (flt-sourced digits) and the YAML emitter (Ruby Float#to_s digits) share this single Go-faithful renderer.
Class Method Summary collapse
-
.fixed(digits, point) ⇒ String
-
.render(digits, point, negative) ⇒ String
Render shortest digits + decimal-point position as Go’s ‘g’ verb would.
-
.scientific(digits, exponent) ⇒ String
-
.shortest_digits(string) ⇒ Array(String, Integer)
Parse a shortest-form numeric string (Ruby Float#to_s or flt’s free format, decimal or scientific, case-insensitive
e) into [digits, point] such that the magnitude equals 0.* 10**point. -
.strip_trailing_zeros(string) ⇒ String
Drop trailing ‘0’ digits via a single reverse scan to the last significant digit.
Class Method Details
.fixed(digits, point) ⇒ String
58 59 60 61 62 63 64 |
# File 'lib/ruby/rego/go_number_format.rb', line 58 def self.fixed(digits, point) length = digits.length return "0.#{"0" * -point}#{digits}" if point <= 0 return digits + ("0" * (point - length)) if point >= length "#{digits[0...point]}.#{digits[point..]}" end |
.render(digits, point, negative) ⇒ String
Render shortest digits + decimal-point position as Go’s ‘g’ verb would.
21 22 23 24 25 |
# File 'lib/ruby/rego/go_number_format.rb', line 21 def self.render(digits, point, negative) exponent = point - 1 body = exponent < -4 || exponent >= 6 ? scientific(digits, exponent) : fixed(digits, point) negative ? "-#{body}" : body end |
.scientific(digits, exponent) ⇒ String
67 68 69 70 |
# File 'lib/ruby/rego/go_number_format.rb', line 67 def self.scientific(digits, exponent) mantissa = digits.length == 1 ? digits : "#{digits[0]}.#{digits[1..]}" "#{mantissa}e#{exponent.negative? ? "-" : "+"}#{format("%02d", exponent.abs)}" end |
.shortest_digits(string) ⇒ Array(String, Integer)
Parse a shortest-form numeric string (Ruby Float#to_s or flt’s free format, decimal or
scientific, case-insensitive e) into [digits, point] such that the magnitude equals
0.
rubocop:disable Metrics/AbcSize
34 35 36 37 38 39 40 41 42 43 |
# File 'lib/ruby/rego/go_number_format.rb', line 34 def self.shortest_digits(string) mantissa, exponent = string.sub(/\A-/, "").split(/e/i) integer_part, fraction = mantissa.to_s.split(".") integer_part = integer_part.to_s combined = integer_part + fraction.to_s without_leading = combined.sub(/\A0+/, "") point = integer_part.length + (exponent || "0").to_i - (combined.length - without_leading.length) digits = strip_trailing_zeros(without_leading) digits.empty? ? ["0", 1] : [digits, point] end |
.strip_trailing_zeros(string) ⇒ String
Drop trailing ‘0’ digits via a single reverse scan to the last significant digit. Avoids an
anchored /0+\z/ (which a regex engine can match in polynomial time on adversarial all-zero
runs); the negated single-character class has no quantifier to backtrack on.
52 53 54 55 |
# File 'lib/ruby/rego/go_number_format.rb', line 52 def self.strip_trailing_zeros(string) last_significant = string.rindex(/[^0]/) last_significant ? string[0..last_significant].to_s : "" end |