4

Given an arbitrary large (or small) Rational number that has a finite decimal representation, e.g.:

r = Rational(1, 2**15)
#=> (1/32768)

How can I get its full decimal value as a string?

The expected output for the above number is:

"0.000030517578125"

to_f apparently doesn't work:

r.to_f
#=> 3.0517578125e-05

And sprintf requires me to specify the number of digits:

sprintf('%.30f', r)
#=> "0.000030517578125000000000000000"
4

3 回答 3

1

大多数十岁的孩子都知道如何做到这一点:使用长除法!1

代码

def finite_long_division(n,d)
  return nil if d.zero?
  sign = n*d >= 0 ? '' : '-'
  n, d = n.abs, d.abs
  pwr =
  case n <=> d
  when 1 then power(n,d)
  when 0 then 0
  else        -power(d,n)-1
  end            
  n *= 10**(-pwr) if pwr < 0
  d *= 10**(pwr)  if pwr >= 0
  s = ld(n,d)
  t = s.size == 1 ? '0' : s[1..-1]
  "%s%s.%s x 10^%d" % [sign, s[0], t, pwr]
end

def power(n, d)
  # n > d
  ns = n.to_s
  ds = d.to_s
  pwr = ns.size - ds.size - 1
  pwr += 1 if ns[0, ds.size].to_i >= ds.to_i
  pwr
end

def ld(n,d)
  s = ''
  loop do # .with_object('') do |s|
    m,n = n.divmod(d)
    s << m.to_s
    return s if n.zero?
    n *= 10
  end
end

示例2

finite_long_division(1, 2**15)
  #=> "3.0517578125 x 10^-5"
finite_long_division(-1, 2**15)
  #=> "-3.0517578125 x 10^-5"
finite_long_division(-1, -2**15)
  #=> "3.0517578125 x 10^-5"

finite_long_division(143, 16777216)
  #=> "8.523464202880859375 x 10^-6"
143/16777216.0
  #=> 8.52346420288086e-06 

finite_long_division(8671,
  803469022129495137770981046170581301261101496891396417650688)
  #=> "1.079195309486679194852923588206549145803161531099624\
  #      804222395643336829571798416196370119711226461255452\
  #      67714596064934085006825625896453857421875 x 10^-56"      

回想一下,每个有理数要么有十进制表示,要么包含无限重复的数字序列(例如1/3 #=> 0.33333..., 3227/555 #=> 5.8144144144...1/9967 #=> 0.00010033109260559848...3)。因此,如果有理数是重复序列变体,则此方法将永远不会终止。由于通常事先不知道有理数是哪种类型,因此修改该方法以首先确定有理数是否具有有限十进制表示可能很有用。n/d众所周知,一个不能减少(通过去除公因数)的有理数具有这个性质当且仅当d它可以被2或被5任何其他素数整除并且不能被任何其他素数整除。4我们可以很容易地构造一个方法来确定一个已经减少的有理数是否具有该属性。

require 'prime'

def decimal_representation?(n, d)
  primes = Prime.prime_division(d).map(&:first)
  (primes & [2,5]).any? && (primes - [2, 5]).empty?
end

1 至少在我小时候是这样。

2 请参阅此处以获取具有有限十进制表示的有理数的部分列表

3 这个有理数的重复序列包含 9,966 位。

4参考

于 2016-12-21T03:29:16.603 回答
1

Bigdecimalto_s有一个“F”选项。然而,需要一些转变才能将这种理性转化为形状。

require "bigdecimal"
r = Rational(1, 2**15)
p   BigDecimal.new(r.to_f.to_s).to_s("F") # => "0.000030517578125"
于 2016-11-25T21:38:59.610 回答
1
a = sprintf('%.30f', r)
a.gsub(/0*\z/,'')

就是这样:)(或者应该:P)这不是最好的方法,如果值有超过 30 个小数,则需要在 sprintf 中添加超过 30 个零。我认为有更好的方法可以做到这一点,但这种方式有效

已编辑

require 'bigdecimal'
require 'bigdecimal/util'
b = BigDecimal.new(r, (r.denominator * r.numerator))
b.to_digits

请注意此解决方案。(r.denominator * r.numerator)这是精度,精度永远不会大于分母*分子(我认为,但数学家可以告诉你)

编辑 2

r = BigDecimal("1") / (BigDecimal("2") ** BigDecimal("99"))
r.to_digits
# Example
r = BigDecimal("1") / (BigDecimal("2")**BigDecimal("99"))
r.to_digits
# "0.000000000000000000000000000001577721810442023610823457130565572459346412870218046009540557861328125"

但是非常大的数字,例如:

r = BigDecimal("1") / (BigDecimal("2")**BigDecimal("999999999999"))
# RangeError: integer 999999999999 too big to convert to `int'

如果您需要更好的东西,我认为您需要自己实现“字符串除法”。

于 2016-11-25T17:07:28.640 回答