Class: Float

Inherits:
Object show all
Defined in:
lib/ron.rb,
lib/ron/float_accurate_to_s.rb

Constant Summary collapse

SIZE =
[1.1].pack("d").size
BITSIZE =
SIZE*8
BASE10_DIGITS =
(2**BITSIZE-1).to_s.size

Instance Method Summary collapse

Instance Method Details

#accurate_to_sObject



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/ron/float_accurate_to_s.rb', line 11

def accurate_to_s
  return "#{'-' if self<0}Infinity" if infinite?
  return "NaN" if nan?
  return "0.0e0" if zero?

  as_str=sprintf("%.#{BASE10_DIGITS+2}e",self)

  #decompose self into sign, mantissa, and exponent (in string form)
  all,sign,first,digits,exp=*as_str.match(/^([+-]?)(\d)\.(\d+)e(.*)$/)
  digits=first<<digits
  exp=exp.to_i+1
  lead=sign<<"0."
  return digits=digits if as_str.to_f.zero? #hopeless

  #recompose back to a float
  result=[lead,digits,"e",exp].join
  result_f=result.to_f
  delta=result_f - self
  return digits=digits if delta.zero? #if representation is exact, return here

  #figure out which direction to go to get toward the right answer
  if delta<0
    incr=1
  else #delta>0
    incr=-1
  end

  #keep adding increasing increments to mantissa
  #until we get to a value on the other side of the correct answer
  while true
    while true
      try_digits=digits.to_i.+(incr).to_s
      if try_digits.size>digits.size
        exp+=1
        digits="0"+digits
      end
      fail if try_digits[0]==?- #can't happen... I think?
      trying=[lead,try_digits,"e",exp].join
      trying_f=trying.to_f
      break unless trying_f.zero?
      digits[-1,1]='' #workaround 1.8 bug
    end
    return digits=try_digits if trying_f==self
    break if self.between?(*[trying_f,result_f].sort) #(trying_f-self)*delta<0
    incr*=2
  end

  #we now have lower and upper bounds on the correct answer
  lower,upper=*[digits.to_i, digits.to_i.+(incr)].sort

  #maybe one of the bounds is already the correct answer?
  result=[lead,lower,"e",exp].join
  return digits=lower if result.to_f==self
  result=[lead,upper,"e",exp].join
  return digits=upper if result.to_f==self

  #binary search between lower and upper bounds til we find a correct answer
  digits=nil
  while true
    return as_str if upper-lower <= 1 #hopeless
    mid=(lower+upper)/2
    mid_s=[lead,mid,"e",exp].join
    mid_f=mid_s.to_f
    return digits=mid if mid_f==self
    if mid_f<self
      lower=mid
    else #mid_f>self
      upper=mid
    end
  end
ensure

  #try to drop unneeded trailing digits
  if digits
    digits=digits.to_s
    begin
      last=digits.slice! -1
      result=[lead,digits,"e",exp].join.to_f
    end while result==self or result.zero? && digits.size.nonzero?
    roundup=(digits.to_i+1).to_s
    if roundup.size>digits.size
      exp+=1
      digits="0"+digits
    end
    roundup.slice! /0+\Z/
    roundup=[lead,roundup,"e",exp].join
    return roundup if roundup.to_f==self
    return [lead,digits<<last,"e",exp].join
  end
end

#to_ron_list(session) ⇒ Object



159
160
161
# File 'lib/ron.rb', line 159

def to_ron_list(session) 
  [accurate_to_s]
end