lib/ruby/to_d.rb
# frozen_string_literal: true
require "bigdecimal"
require "rational"
module Stupidedi
module Refinements
refine BigDecimal do
# @return [BigDecimal] self
def to_d
self
end
end
BIGDECIMAL = /\A[+-]? (?# optional leading sign )
(?:
(?:\d+\.?\d*) | (?# whole with optional decimal or ..)
(?:\d*?\.?\d+) ) (?# optional whole with decimal )
(?:E[+-]?\d+)? (?# optional exponent )
\Z/ix
refine String do
# Converts the string to a BigDecimal after validating the format. If the
# string does not match the pattern for a valid number, an `ArgumentError`
# is raised.
#
# @example
# "1.0".to_d #=> BigDecimal("1.0")
#
# @return [BigDecimal]
def to_d
if BIGDECIMAL =~ self
BigDecimal(to_s)
else
raise ArgumentError, "#{inspect} is not a valid number"
end
end
end
refine Integer do
# Converts the integer to a BigDecimal
#
# @example
# 10.to_d #=> BigDecimal("10")
#
# @return [BigDecimal]
def to_d
BigDecimal(to_s)
end
end
refine Rational do
# Converts the rational to a BigDecimal
#
# @example
# Rational(3, 4).to_d #=> BigDecimal("3") / BigDecimal("4")
#
# @return [BigDecimal]
def to_d
numerator.to_d / denominator.to_d
end
end
refine Float do
# Raises a `TypeError` exception. The reason this method is defined at
# all is to produce a more meaningful error than `NoSuchMethod`.
#
# @return [void]
def to_d
# The problem is there isn't a way to know the correct precision,
# since there are (many) values that cannot be represented exactly
# using Floats. For instance, we can't assume which value is correct
#
# "%0.10f" % 1.8 #=> "1.8000000000"
# "%0.20f" % 1.8 #=> "1.80000000000000004441"
#
# The programmer should convert the Float to a String using whatever
# precision he chooses, and call #to_d on the String.
raise TypeError, "cannot convert Float to BigDecimal"
end
end
end
end