class Float

Constants

BASE10_DIGITS
BITSIZE
SIZE

Public Instance Methods

accurate_to_s() click to toggle source
# File lib/redparse/float_accurate_to_s.rb, line 29
def accurate_to_s
#this shouldn't be needed anymore after 1.9.2
  return "#{'-' if self<0}Infinity" if infinite?
  return "NaN" if nan?
  return to_s if zero? #-0.0 and +0.0

  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+))?(?:[eE](.*))?$/)
  digits=first<<(digits||'0')
  exp=(exp||'0').to_i+1
  lead=sign<<"0."

  #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