[前][次][番号順一覧][スレッド一覧]

ruby-changes:4209

From: ko1@a...
Date: Thu, 6 Mar 2008 01:24:25 +0900 (JST)
Subject: [ruby-changes:4209] akr - Ruby:r15699 (trunk): add tests for float format.

akr	2008-03-06 01:24:09 +0900 (Thu, 06 Mar 2008)

  New Revision: 15699

  Modified files:
    trunk/test/ruby/test_sprintf_comb.rb

  Log:
    add tests for float format.


  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/test/ruby/test_sprintf_comb.rb?r1=15699&r2=15698&diff_format=u

Index: test/ruby/test_sprintf_comb.rb
===================================================================
--- test/ruby/test_sprintf_comb.rb	(revision 15698)
+++ test/ruby/test_sprintf_comb.rb	(revision 15699)
@@ -109,11 +109,12 @@
   VS.reverse!
 
   def combination(*args, &b)
+    #AllPairs.exhaustive_each(*args, &b)
     AllPairs.each(*args, &b)
   end
 
-  def emu(format, v)
-    /\A%( )?(\#)?(\+)?(-)?(0)?(\d+)?(?:\.(\d+))?(.)\z/ =~ format
+  def emu_int(format, v)
+    /\A%( )?(\#)?(\+)?(-)?(0)?(\d+)?(?:\.(\d*))?(.)\z/ =~ format
     sp = $1
     hs = $2
     pl = $3
@@ -263,23 +264,20 @@
     str
   end
 
-  def test_format
+  def test_format_integer
     combination(
         %w[b d o X x],
         [nil, 0, 5, 20],
-        [nil, 0, 8, 20],
+        ["", ".", ".0", ".8", ".20"],
         ['', ' '],
         ['', '#'],
         ['', '+'],
         ['', '-'],
         ['', '0']) {|type, width, precision, sp, hs, pl, mi, zr|
-      if precision
-        precision = ".#{precision}"
-      end
       format = "%#{sp}#{hs}#{pl}#{mi}#{zr}#{width}#{precision}#{type}"
       VS.each {|v|
         r = sprintf format, v
-        e = emu format, v
+        e = emu_int format, v
         if true
           assert_equal(e, r, "sprintf(#{format.dump}, #{v})")
         else
@@ -290,4 +288,261 @@
       }
     }
   end
+
+  FLOAT_VALUES = [
+    -1e100,
+    -123456789.0,
+    -1.0,
+    -0.0,
+    0.0,
+    0.01,
+    1/3.0,
+    1.0,
+    2.0,
+    9.99999999,
+    123456789.0,
+    1e100,
+    Float::MAX,
+    Float::MIN,
+    Float::EPSILON,
+    1+Float::EPSILON,
+    #1-Float::EPSILON/2,
+    10 + Float::EPSILON*10,
+    10 - Float::EPSILON*5,
+    1.0/0.0,
+    -1.0/0.0,
+    0.0/0.0,
+  ]
+
+  def split_float10(v)
+    if v == 0
+      if 1/v < 0
+        sign = -1
+        v = -v
+      else
+        sign = 1
+      end
+    else
+      if v < 0
+        sign = -1
+        v = -v
+      else
+        sign = 1
+      end
+    end
+    exp = 0
+    int = v.floor
+    v -= int
+    while v != 0
+      v *= 2
+      int *= 2
+      i = v.floor
+      v -= i
+      int += i
+      exp -= 1
+    end
+    int *= 5 ** (-exp)
+    [sign, int, exp]
+  end
+
+  def emu_e(sp, hs, pl, mi, zr, width, precision, type, v, sign, int, exp)
+    precision = 6 unless precision
+    if int == 0
+      if precision == 0 && !hs
+        result = "0#{type}+00"
+      else
+        result = "0." + "0" * precision + "#{type}+00"
+      end
+    else
+      if int < 10**precision
+        int *= 10**precision
+        exp -= precision
+      end
+      digits = int.to_s.length
+      discard = digits - (precision+1)
+      if discard != 0
+        q, r = int.divmod(10**discard)
+        if r < 10**discard / 2
+          int = q
+          exp += discard
+        elsif (q+1).to_s.length == q.to_s.length
+          int = q+1
+          exp += discard
+        else
+          discard += 1
+          q, r = int.divmod(10**discard)
+          int = q+1
+          exp += discard
+        end
+      end
+      ints = int.to_s
+      frac = ints[1..-1]
+      result = ints[0,1]
+      e = exp + frac.length
+      if precision != 0 || hs
+        result << "."
+        if precision != 0
+          result << frac
+        end
+      end
+      result << type
+      if e == 0
+        if v.abs < 1
+          result << '-00' # glibc 2.7 causes '+00'
+        else
+          result << '+00'
+        end
+      else
+        result << sprintf("%+03d", e)
+      end
+      result
+    end
+    result
+  end
+
+  def emu_f(sp, hs, pl, mi, zr, width, precision, type, sign, int, exp)
+    precision = 6 unless precision
+    if int == 0
+      if precision == 0 && !hs
+        result = '0'
+      else
+        result = '0.' + '0' * precision
+      end
+    else
+      if -precision < exp
+        int *= 10 ** (precision+exp)
+        exp = -precision
+      end
+      if exp < -precision
+        discard = -exp - precision
+        q, r = int.divmod(10**discard)
+        if 10**discard / 2 <= r
+          q += 1
+        end
+        int = q
+        exp += discard
+      end
+      result = int.to_s
+      if result.length <= precision
+        result = '0' * (precision+1 - result.length) + result
+      end
+      if precision != 0 || hs
+        if precision == 0
+          result << '.'
+        else
+          result[-precision,0] = '.'
+        end
+      end
+    end
+    result
+  end
+
+  def emu_float(format, v)
+    /\A%( )?(\#)?(\+)?(-)?(0)?(\d+)?(?:\.(\d*))?(.)\z/ =~ format
+    sp = $1
+    hs = $2
+    pl = $3
+    mi = $4
+    zr = $5
+    width = $6
+    precision = $7
+    type = $8
+    width = width.to_i if width
+    precision = precision.to_i if precision
+
+    zr = nil if mi && zr
+
+    if v.infinite?
+      sign = v < 0 ? -1 : 1
+      int = :inf
+      hs = zr = nil
+    elsif v.nan?
+      sign = 1
+      int = :nan
+      hs = zr = nil
+    else
+      sign, int, exp = split_float10(v)
+    end
+
+    if sign < 0
+      sign = '-'
+    elsif sign == 0
+      sign = ''
+    elsif pl
+      sign = '+'
+    elsif sp
+      sign = ' '
+    else
+      sign = ''
+    end
+
+    if v.nan?
+      result = 'NaN'
+    elsif v.infinite?
+      result = 'Inf'
+    else
+      case type
+      when /[eE]/
+        result = emu_e(sp, hs, pl, mi, zr, width, precision, type, v, sign, int, exp)
+      when /f/
+        result = emu_f(sp, hs, pl, mi, zr, width, precision, type, sign, int, exp)
+      when /[gG]/
+        precision = 6 unless precision
+        precision = 1 if precision == 0
+        r = emu_e(sp, hs, pl, mi, zr, width, precision-1, type.tr('gG', 'eE'), v, sign, int, exp)
+        /[eE]([+-]\d+)/ =~ r
+        e = $1.to_i
+        if e < -4 || precision <= e
+          result = r
+        else
+          result = emu_f(sp, hs, pl, mi, zr, width, precision-1-e, type, sign, int, exp)
+        end
+        result.sub!(/\.[0-9]*/) { $&.sub(/\.?0*\z/, '') } if !hs
+      else
+        raise "unexpected type: #{type}"
+      end
+    end
+
+    pad = ''
+    if width && sign.length + result.length < width
+      if zr
+        pad = '0' * (width - sign.length - result.length)
+      else
+        pad = ' ' * (width - sign.length - result.length)
+      end
+    end
+    if mi
+      sign + result + pad
+    elsif zr
+      sign + pad + result
+    else
+      pad + sign + result
+    end
+
+  end
+
+  def test_format_float
+    combination(
+        %w[e E f g G],
+        [nil, 0, 5, 20],
+        ["", ".", ".0", ".8", ".20", ".200"],
+        ['', ' '],
+        ['', '#'],
+        ['', '+'],
+        ['', '-'],
+        ['', '0']) {|type, width, precision, sp, hs, pl, mi, zr|
+      format = "%#{sp}#{hs}#{pl}#{mi}#{zr}#{width}#{precision}#{type}"
+      FLOAT_VALUES.each {|v|
+        r = sprintf format, v
+        e = emu_float format, v
+        if true
+          assert_equal(e, r, "sprintf(#{format.dump}, #{'%.20g' % v})")
+        else
+          if e != r
+            puts "#{e.dump}\t#{r.dump}\tsprintf(#{format.dump}, #{'%.20g' % v})"
+          end
+        end
+      }
+    }
+  end
 end

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]