[#20036] Re: Roundoff problem with Float and Marshal — matz@... (Yukihiro Matsumoto)

まつもと ゆきひろです

16 messages 2003/04/18
[#20045] Re: Roundoff problem with Float and Marshal — nobu.nakada@... 2003/04/20

なかだです。

[#20063] Re: Roundoff problem with Float and Marshal — matz@... (Yukihiro Matsumoto) 2003/04/22

まつもと ゆきひろです

[#20097] jcode.rb — akira yamada / やまだあきら <akira@...>

25 messages 2003/04/26
[#20098] Re: jcode.rb — matz@... (Yukihiro Matsumoto) 2003/04/27

まつもと ゆきひろです

[#20105] Re: jcode.rb — WATANABE Hirofumi <eban@...> 2003/04/28

わたなべです。

[#20108] Re: jcode.rb — matz@... (Yukihiro Matsumoto) 2003/04/28

まつもと ゆきひろです

[ruby-dev:20086] Re: Roundoff problem with Float and Marshal

From: nobu.nakada@...
Date: 2003-04-25 02:49:45 UTC
List: ruby-dev #20086
なかだです。

At Thu, 24 Apr 2003 02:38:48 +0900,
Nobuyoshi-Nakada wrote:
> http://www.cwi.nl/~cilibrar/floatingpointconv.pdf
> http://ftp.ccs.neu.edu/pub/people/will/howtoread.ps

上記のBellerophonとAlgorithmRを、試しにRubyで実装してみました。
しかしこれ、SchemeとかRubyならいいけど、CだとBignumが面倒なこと
になりそうな。

# ついでにデバッグ用のFloatの16進変換も。


-- 
--- 僕の前にBugはない。
--- 僕の後ろにBugはできる。
    中田 伸悦

Attachments (2)

bellerophon.rb (1.91 KB, text/x-ruby)
# $Id: bellerophon.rb,v 1.1 2003/04/25 02:45:07 nobu Exp $

class Float
  def split
    m, k = Math.frexp(abs())
    [self <=> 0, Math.ldexp(m, MANT_DIG).to_i, k - DIG]
  end

  def significand
    Math.ldexp(Math.frexp(self)[0], MANT_DIG).to_i
  end

  def exponent
    Math.frexp(self)[1] - MANT_DIG
  end

  def prev_delta
    m, e = Math.frexp(self)
    self - Math.ldexp(m == 0.5 ? 0.5 : 1.0, e - MANT_DIG)
  end

  def succ_delta
    m, e = Math.frexp(self)
    self + Math.ldexp(m == 0.5 ? 0.5 : 1.0, e - MANT_DIG)
  end
end

class Integer
  N = Float::MANT_DIG
  P = ((Float::MANT_DIG+16)&~15)
  TWO_P_N_1 = 1<<(P-Float::MANT_DIG-1)
  extra_prec = P - Float::MANT_DIG
  EXTRA_PREC_MASK = (1 << extra_prec) - 1
  TWO_N = 1<<Float::MANT_DIG
  TWO_N_1 = 1<<(Float::MANT_DIG-1)
  LOG5_OF_TWO_N = ((Float::MANT_DIG*Math.log(2))/Math.log(5)).ceil
  H = 23
  H2 = H < LOG5_OF_TWO_N ? H : LOG5_OF_TWO_N

  def to_float(e = 0)
    return 0.0 if self.zero?
    return -(-self).bellerophon(e) if self < 0
    if self < TWO_N
      return to_f() if e.zero?
      return e > 0 ? (to_f() * 10**e) : (to_f() / 10**-e) if e.abs < H2
      slop = 0
    else
      slop = 1
    end
    slop += 3 if e < 0 or e >= H
    z = to_f() * 10**e
    if ((z.significand & EXTRA_PREC_MASK) - TWO_P_N_1).abs > slop
      return z
    end
    e10 = 10**(e.abs)
    while true
      m = z.significand
      k = z.exponent
      x = self
      y = m
      if e < 0
        y *= e10
      else
        x *= e10
      end
      if k < 0
        x <<= -k
      else
        y <<= k
      end
      d = x - y
      d2 = 2 * m * d.abs
      if d2 < y
        break unless m == TWO_N_1 and d < 0 and 2*d2 > y
        z = z.prev_delta
      elsif d2 == y and (m & 1).zero?
        break unless m == TWO_N_1 and d < 0
        z = z.prev_delta
      elsif d < 0
        z = z.prev_delta
      elsif d > 0
        z = z.succ_delta
      else
        break
      end
    end
    z
  end
end
hexfloat.rb (1.02 KB, text/x-ruby)
# $Id: hexfloat.rb,v 1.1 2003/04/25 02:45:07 nobu Exp $

class Float
  def hex
    return "nan" if nan?
    case infinite?
    when 1
      return "inf"
    when -1
      return "-inf"
    end
    s = self < 0 ? "-0x" : "0x"
    m, e = Math.frexp(abs())
    m = Math.ldexp(m, n = ((e - 1) & 3) + 1)
    e -= n
    n = m.floor
    s << "%x" % n
    if (m -= n) > 0
      s << "."
      begin
        s << "%.4x" % (n = (m *= 0x10000).floor)
      end while (m -= n) > 0
    end
    s.sub!(/0+$/, '')
    e.zero? or s << "p%+d" % e
    s
  end

  def bin
    [self].pack("G").gsub(/./) {|s| "%.2x" % s[0]}
  end
end

class String
  alias to_float to_f
  def to_f
    return to_float() unless m = /\A\s*([-+])?0x/i.match(self)
    sign = m[1]
    if m = /\A([[:xdigit:]]+)(?:\.([[:xdigit:]]+))?(?:p([-+]\d+))?/i.match(m.post_match)
      f = m[1]
      e = m[3].to_i
      if m[2]
        f << m[2]
        e -= 4*m[2].size
      end
      f = f.to_i(16)
      f = Math.ldexp(f, e)
    else
      f = 0.0
    end
    sign == '-' ? -f : f
  end
end

In This Thread