1 # depends on: class.rb numeric.rb regexp.rb string.rb
3 def BigDecimal(string, _precs=0)
4 BigDecimal.new(string, _precs)
7 class BigDecimal < Numeric
8 # See stdlib/ext/bigdecimal for MatzRuby implementation.
17 SIGN_POSITIVE_ZERO = 1
18 SIGN_NEGATIVE_ZERO = -1
19 SIGN_POSITIVE_FINITE = 2
20 SIGN_NEGATIVE_FINITE = -2
21 SIGN_POSITIVE_INFINITE = 3
22 SIGN_NEGATIVE_INFINITE = -3
23 SIGN_NaN = 0 # is this correct?
29 SIGNS = {-1 => MINUS, 0 => nil, 1 => PLUS}
31 VERSION = "1.0.1" # like Ruby 1.8.6
41 ###############################
42 # Constructor and basic tests #
43 ###############################
46 # BigDecimal("3.14159") => big_decimal
47 # BigDecimal("3.14159", 10) => big_decimal
48 def initialize(_val, _precs=0)
51 @digits = 0 # decimal point is assumed at beginning; exp is assigned on this basis
53 @special = nil # 'n' for NaN, 'i' for Infinity, nil otherwise
59 elsif v =~ /^[-+]?Infinity$/
61 @sign = MINUS if v =~ /-/
64 v = _val.gsub('_', '')
65 m = /^\s*(([-+]?)(\d*)(?:\.(\d*))?(?:[EeDd]([-+]?\d+))?).*$/.match(v)
67 @sign = m[2] unless m[2].to_s.empty?
68 int = m[3].to_s.gsub(/^0*/, '')
70 fraczeros = /^0*/.match(frac)[0]
71 @exp = m[5].to_i + int.length
73 @exp -= (fraczeros.size == frac.size) ? 0 : fraczeros.length
75 @digits = (int + frac).gsub(/0*$/, '').to_i
77 @precs = [v.length, _precs].max
81 # As for Float.finite? .
83 # BigDecimal.new("Infinity").finite? => false
84 # BigDecimal.new("NaN").finite? => true
86 @special != 'i' && !self.nan?
90 if self.finite? or self.nan?
93 return (@sign + '1').to_i
99 # BigDecimal.new("NaN").nan? => true
100 # BigDecimal.new("123").nan? => false
105 # True if positive or negative zero; false otherwise.
107 # BigDecimal.new("0").zero? =>true
108 # BigDecimal.new("-0").zero? =>true
110 @digits.to_i == 0 and self.finite?
117 sigfigs = @digits.to_s.length
127 if self.sign == SIGN_POSITIVE_INFINITE
129 elsif self.sign == SIGN_NEGATIVE_INFINITE
142 self.fix.to_s("F").to_i
146 # parse the argument for format specs
148 when /\+/ then PLUS.clone
152 format = arg =~ /F/ ? :float : :eng
156 infinity = 'Infinity'
171 # get the decimal point in place
172 if @exp >= value.length
173 value << ('0' * (@exp - value.length)) + RADIX + '0'
175 value = value[0, @exp] + RADIX + value[@exp..-1]
177 value = '0' + RADIX + ('0' * -@exp) + value
180 value = '0' + RADIX + value
182 value << EXP + @exp.to_s
187 m = /^(\d*)(?:(#{RADIX})(\d*)(.*))?$/.match(value)
188 left, myradix, right, extra = m[1, 4].collect{|s| s.to_s}
190 0.step(right.length, spacing) do |n|
191 right_frags.push right[n, spacing]
196 0.step(left.length, spacing) do |n|
197 left_frags.unshift tfel[n, spacing].reverse
200 right = right_frags.join(' ').strip
201 left = left_frags.join(' ').strip
202 value = left.to_s + myradix.to_s + right.to_s + extra.to_s
212 str = '#<BigDecimal:'
213 str << [nil, "'#{self.to_s}'", "#{precs[0]}(#{precs[1]})"].join(',')
219 Ruby.primitive :numeric_coerce
220 if other.kind_of?(BigDecimal)
223 [BigDecimal(other.to_s), self]
227 #########################
228 # Arithmetic operations #
229 #########################
231 # These are stubbed out until we implement them so that their respective specfiles don't crash.
233 def add(other, precs)
234 if !other.kind_of?(BigDecimal)
235 return self.add(BigDecimal(other.to_s), precs)
236 elsif self.nan? or other.nan?
237 return BigDecimal("NaN")
238 elsif !self.finite? and !other.finite? and self.sign != other.sign
239 # infinity + -infinity
240 return BigDecimal("NaN")
241 elsif !self.finite? or other.zero?
243 elsif !other.finite? or self.zero?
245 elsif self.exponent == other.exponent
246 sd, od = self.align(other)
247 sum = (sd.to_i * (self.sign <=> 0)) + (od.to_i * (other.sign <=> 0))
249 sumdiff = s.length - sd.length
251 s = MINUS + RADIX + s
255 BigDecimal(s + EXP + (self.exponent + sumdiff).to_s, precs)
256 elsif self.exponent == 0 or other.exponent == 0
257 if self.exponent == 0
264 # so z is the one with the 0 exponent
270 zd = ('0' * nzx) + zd
272 nzd = ('0' * nzx.abs) + nzd
275 zd, nzd = BigDecimal.align(zd, nzd)
278 sum = (nzd.to_s.to_i * (nz.sign <=> 0)) + (zd.to_s.to_i * (z.sign <=> 0))
279 sumsign = sum < 0 ? MINUS : PLUS
281 sumdiff = s.length - zd.length
282 BigDecimal(sumsign + RADIX + s + EXP + (sumdiff + [nzx, 0].max).to_s, precs)
284 a, b, extra = reduce(self, other)
286 BigDecimal(SIGNS[sum.sign <=> 0].to_s + RADIX + sum.digits.to_s + EXP + (sum.exponent + extra).to_s, precs)
294 def sub(other, precs)
295 self.add(-other, precs)
302 def mult(other, precs)
303 if !other.kind_of?(BigDecimal)
304 return self.mult(BigDecimal(other.to_s), precs)
305 elsif (self.infinite? and other.zero?) or (self.zero? and other.infinite?)
306 return BigDecimal("NaN")
307 elsif (self.nan? or other.nan?)
308 return BigDecimal("NaN")
309 elsif (self.zero? or other.zero?)
310 return BigDecimal("0") if (self.sign * other.sign) > 0
311 return BigDecimal("-0") if (self.sign * other.sign) < 0
313 if (self.sign * other.sign < 0) == self.sign < 0
319 if (self.sign * other.sign < 0) == self.sign < 0
329 # figure out how many decimal places we're dealing with
330 sp = sd.to_s.length - self.exponent
331 op = od.to_s.length - other.exponent
333 a = sd * (self < 0 ? -1 : 1)
334 b = od * (other < 0 ? -1 : 1)
337 BigDecimal([SIGNS[prod <=> 0], RADIX, pa, EXP, pa.to_s.length - (sp + op)].join)
349 def div(other, precs = nil)
350 if !other.kind_of?(BigDecimal)
351 self.quo(BigDecimal(other.to_s))
352 elsif self.nan? or other.nan?
353 return BigDecimal("NaN")
354 elsif other.infinite?
355 if precs.nil? or self.infinite?
356 return BigDecimal("NaN")
358 return BigDecimal("0")
361 return BigDecimal("NaN") if precs.nil? or self.zero?
362 return BigDecimal("Infinity") * other.sign
363 elsif (other.digits == 1) and self.infinite?
365 return BigDecimal("NaN")
367 return self * other.sign
369 elsif !self.exponent.zero? and !other.exponent.zero?
370 a, b, extra = reduce(self, other)
371 q = precs.nil? ? (a / b).floor : (a / b)
372 p = precs.nil? ? q.to_s.length : precs
373 p.zero? ? BigDecimal(q.to_s) : BigDecimal(q.to_s[0..p+1])
375 sa, oa = self.align(other)
376 q = [SIGNS[self <=> 0], sa].join.to_f / [SIGNS[other <=> 0], oa].join.to_f
377 BigDecimal([q, EXP, self.exponent - other.exponent].join)
384 if (self.sign * other.sign < 0)
392 self.divmod(other)[1]
399 raise TypeError if other.kind_of?(String)
400 other = BigDecimal(other.to_s) if other.kind_of?(Integer)
402 if self.infinite? or self.nan?
403 return [BigDecimal("NaN"), BigDecimal("NaN")]
406 if other.infinite? or other.nan? or other.zero?
407 return [BigDecimal("NaN"), BigDecimal("NaN")]
410 first = (self / other).floor
411 second = self - (first * other)
413 arr << first.to_i << second.to_f if other.kind_of?(Float)
414 arr << first << second
420 # Raises self to an integer power.
422 one = BigDecimal("1")
424 return BigDecimal("NaN")
425 elsif other.zero? or self == 1
429 return BigDecimal("0")
431 return BigDecimal("Infinity")
434 return one / (self ** other.abs)
435 elsif !self.exponent.zero?
436 base = BigDecimal([@sign, RADIX, @digits].join)
439 return BigDecimal([SIGNS[n <=> 0], RADIX, n.digits, EXP, (exp * other) + n.exponent].join)
443 return self * (self ** (other - 1))
445 return (self * self) ** (other / 2)
459 BigDecimal(MINUS + s)
466 elsif !other.kind_of?(BigDecimal)
467 return self <=> self.coerce(other)[0]
468 elsif self.nan? or other.nan?
470 elsif self.eql?(other)
473 result = (self.sign <=> other.sign).nonzero? || \
474 (self.exponent <=> other.exponent).nonzero? || \
475 (self.to_i <=> other.to_i).nonzero? || \
476 ((self - other).sign <=> BigDecimal("0").sign)
481 # Apparently, 'include Comparable' doesn't work, so:
483 compare_method(other, 1)
487 return (self > other or self == other)
491 compare_method(other, -1)
495 return (self < other or self == other)
499 compare_method(other, 0)
505 elsif other.respond_to?(:nan?) and other.nan?
507 elsif self.zero? and other.respond_to?(:zero?)
509 elsif self.to_s == other.to_s
511 elsif !other.kind_of?(BigDecimal)
512 return self.eql?(BigDecimal(other.to_s))
523 # I'm trying to keep these in alphabetical order unless a good reason develops to do otherwise.
526 if self.nan? or @sign == PLUS
529 s = self.to_s.sub(/^-/, '') # strip minus sign
538 x = (BigDecimal([@sign, '0', RADIX, @digits, EXP, self.exponent + n].join)).ceil
539 return BigDecimal([@sign, '0', RADIX, x.digits, EXP, x.exponent - n].join)
540 elsif self.frac.zero?
545 return self.fix + BigDecimal("1")
549 # Returns the exponent as a Fixnum (or 0 if out of range), such that the absolute value of the base is between 0 and 1. This is not the power function.
551 # BigDecimal("0.125e3").exponent => 3
552 # BigDecimal("3000").exponent => 4
559 d = @digits.to_s.length
560 if !self.finite? or d <= @exp
563 return BigDecimal("#{@sign}0")
565 s = self.to_s("F").split(RADIX)[0] # this includes the sign
576 elsif @digits.to_s.length <= @exp
577 return BigDecimal("0")
579 s = self.to_s("F").split(RADIX)[1] # the part after the decimal point
580 BigDecimal(@sign + RADIX + s)
587 @sign == PLUS ? SIGN_POSITIVE_ZERO : SIGN_NEGATIVE_ZERO
589 @sign == PLUS ? SIGN_POSITIVE_FINITE : SIGN_NEGATIVE_FINITE
591 @sign == PLUS ? SIGN_POSITIVE_INFINITE : SIGN_NEGATIVE_INFINITE
615 arr << sgn << value << base << @exp
618 def truncate(prec = nil)
624 e = [0, @exp + prec].max
625 s = @digits.to_s[0, e]
626 BigDecimal(@sign + '0' + RADIX + s + EXP + @exp.to_s)
630 ############################
631 # Internal utility methods #
632 ############################
636 # Takes two BigDecimals and returns an array of their digit strings,
637 # with the shorter one right-padded with zeros so they're the same length.
638 # Can also take a digit string itself.
640 # BigDecimal("12").align(BigDecimal("0.0056789")) => ["12000", "56789"]
641 # BigDecimal("8.765").align("43") => ["8765", "4300"]
642 def align(y) #:nodoc:
643 xd = self.digits.to_s
644 yd = y.kind_of?(BigDecimal) ? y.digits.to_s : y
645 BigDecimal.align(xd, yd)
648 # Like BigDecimal#align, but can take two digit strings.
650 # BigDecimal.align("8765", "43") => ["8765", "4300"]
651 def self.align(x, y) #:nodoc:
654 diff = xd.length - yd.length
663 # Wrapper for implementing comparison methods.
664 def compare_method(other, val)
665 # if !self.nan? and other.respond_to?(:nan?) and other.nan?
666 # raise ArgumentError, "Can't compare #{self} to NaN", caller
668 result = (self <=> other)
669 return result.nil? ? nil : result == val
673 # Reduces exponents and returns [a, b, extra].
675 # reduce(BigDecimal("8E5"), BigDecimal("6E2")) => [BigDecimal("8E3"), BigDecimal("6"), 2]
677 extra = [x.exponent, y.exponent].min
678 a = BigDecimal(SIGNS[x.sign <=> 0].to_s + RADIX + x.digits.to_s + EXP + (x.exponent - extra).to_s)
679 b = BigDecimal(SIGNS[y.sign <=> 0].to_s + RADIX + y.digits.to_s + EXP + (y.exponent - extra).to_s)