Change Unit.multiplier type from decimal to float

Remove remnants related to BigDecimal.
This commit is contained in:
2026-05-23 17:02:01 +02:00
parent 1ab925f6fa
commit 0e0cfeff10
18 changed files with 134 additions and 100 deletions

View File

@@ -1,19 +0,0 @@
module CoreExt::ActiveModel::Validations::NumericalityValidatesPrecisionAndScale
def validate_each(record, attr_name, value, ...)
super(record, attr_name, value, ...)
if options[:precision] || options[:scale]
attr_type = record.class.type_for_attribute(attr_name)
# For conversion of 'value' to BigDecimal 'ndigits' is not supplied intentionally,
# to avoid silent rounding. It is only required for conversion from Float and
# Rational, which should not happen.
value = BigDecimal(value) unless value.is_a? BigDecimal
if options[:precision] && (value.precision > attr_type.precision)
record.errors.add(attr_name, :precision_exceeded, **filtered_options(attr_type.precision))
end
if options[:scale] && (value.scale > attr_type.scale)
record.errors.add(attr_name, :scale_exceeded, **filtered_options(attr_type.scale))
end
end
end
end

View File

@@ -1,36 +0,0 @@
module CoreExt
module BigDecimalScientificNotation
def to_scientific
return 'NaN' unless finite?
sign, coefficient, base, exponent = split
(sign == -1 ? '-' : '') +
(coefficient.length > 1 ? coefficient.insert(1, '.') : coefficient) +
(exponent != 1 ? "e#{exponent-1}" : '')
end
# Converts value to HTML formatted scientific notation
def to_html
sign, coefficient, base, exponent = split
return 'NaN' unless sign
result = (sign == -1 ? '-' : '')
unless coefficient == '1' && sign == 1
if coefficient.length > 1
result += coefficient.insert(1, '.')
elsif
result += coefficient
end
if exponent != 1
result += "×"
end
end
if exponent != 1
result += "10<sup>% d</sup>" % [exponent-1]
end
result.html_safe
end
end
end
BigDecimal.prepend CoreExt::BigDecimalScientificNotation

54
lib/core_ext/float.rb Normal file
View File

@@ -0,0 +1,54 @@
# 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 += "&times;" if significand != '1'
result += "10<sup>% d</sup>" % 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

31
lib/core_ext/range.rb Normal file
View File

@@ -0,0 +1,31 @@
class Range
# TODO: cleanup comments after commit
# * < nil, true < false
#if a.end == b.end
# a.exclude_end? ^ b.exclude_end? ? (a.exclude_end? ? -1 : 1) : 0
#else
# a.end <=> b.end || (a.end.nil? ? 1 : -1)
#end
#a.end == b.end ? (b.exclude_end? ? b : a) : [a, b].to_h.except(nil).min
#*(l[0] == r[0] ? (r[1] ? r : l) : [l, r].reject{ |e| e[0].nil? }.min)
def &(other)
case other
when Range
return nil unless self.overlap?(other)
both = [self, other]
return Range.new(
both.map(&:begin).compact.max,
*if self.end == other.end
other.exclude_end? ? [other] : [self]
else
both.select(&:end)
end.map { |r| [r.end, r.exclude_end?] }.min
)
when Array
return other.map { |o| self & o }.compact
else
return self.member?(other) ? other : nil
end
end
end