1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/test/ruby/test_sprintf_comb.rb

554 lines
11 KiB
Ruby
Raw Normal View History

require 'test/unit'
require_relative 'allpairs'
class TestSprintfComb < Test::Unit::TestCase
VS = [
#-0x1000000000000000000000000000000000000000000000002,
#-0x1000000000000000000000000000000000000000000000001,
#-0x1000000000000000000000000000000000000000000000000,
#-0xffffffffffffffffffffffffffffffffffffffffffffffff,
#-0x1000000000000000000000002,
#-0x1000000000000000000000001,
#-0x1000000000000000000000000,
#-0xffffffffffffffffffffffff,
-0x10000000000000002,
-0x10000000000000001,
-0x10000000000000000,
-0xffffffffffffffff,
-0x4000000000000002,
-0x4000000000000001,
-0x4000000000000000,
-0x3fffffffffffffff,
-0x100000002,
-0x100000001,
-0x100000000,
-0xffffffff,
#-0xc717a08d, # 0xc717a08d * 0x524b2245 = 0x4000000000000001
-0x80000002,
-0x80000001,
-0x80000000,
-0x7fffffff,
#-0x524b2245,
-0x40000002,
-0x40000001,
-0x40000000,
-0x3fffffff,
#-0x10002,
#-0x10001,
#-0x10000,
#-0xffff,
#-0x8101, # 0x8101 * 0x7f01 = 0x40000001
#-0x8002,
#-0x8001,
#-0x8000,
#-0x7fff,
#-0x7f01,
#-65,
#-64,
#-63,
#-62,
#-33,
#-32,
#-31,
#-30,
-3,
-2,
-1,
0,
1,
2,
3,
#30,
#31,
#32,
#33,
#62,
#63,
#64,
#65,
#0x7f01,
#0x7ffe,
#0x7fff,
#0x8000,
#0x8001,
#0x8101,
#0xfffe,
#0xffff,
#0x10000,
#0x10001,
0x3ffffffe,
0x3fffffff,
0x40000000,
0x40000001,
#0x524b2245,
0x7ffffffe,
0x7fffffff,
0x80000000,
0x80000001,
#0xc717a08d,
0xfffffffe,
0xffffffff,
0x100000000,
0x100000001,
0x3ffffffffffffffe,
0x3fffffffffffffff,
0x4000000000000000,
0x4000000000000001,
0xfffffffffffffffe,
0xffffffffffffffff,
0x10000000000000000,
0x10000000000000001,
#0xffffffffffffffffffffffff,
#0x1000000000000000000000000,
#0x1000000000000000000000001,
#0xffffffffffffffffffffffffffffffffffffffffffffffff,
#0x1000000000000000000000000000000000000000000000000,
#0x1000000000000000000000000000000000000000000000001
]
VS.reverse!
FLAGS = [['', ' '], ['', '#'], ['', '+'], ['', '-'], ['', '0']]
def self.combination(*args, &b)
#AllPairs.exhaustive_each(*args, &b)
AllPairs.each(*args, &b)
end
def emu_int(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
prefix = ''
zr = nil if precision
zr = nil if mi && zr
case type
when 'B'
radix = 2
digitmap = {0 => '0', 1 => '1'}
complement = !pl && !sp
prefix = '0B' if hs && v != 0
when 'b'
radix = 2
digitmap = {0 => '0', 1 => '1'}
complement = !pl && !sp
prefix = '0b' if hs && v != 0
when 'd'
radix = 10
digitmap = {}
10.times {|i| digitmap[i] = i.to_s }
complement = false
when 'o'
radix = 8
digitmap = {}
8.times {|i| digitmap[i] = i.to_s }
complement = !pl && !sp
when 'X'
radix = 16
digitmap = {}
16.times {|i| digitmap[i] = i.to_s(16).upcase }
complement = !pl && !sp
prefix = '0X' if hs && v != 0
when 'x'
radix = 16
digitmap = {}
16.times {|i| digitmap[i] = i.to_s(16) }
complement = !pl && !sp
prefix = '0x' if hs && v != 0
else
raise "unexpected type: #{type.inspect}"
end
digits = []
abs = v.abs
sign = ''
while 0 < abs
digits << (abs % radix)
abs /= radix
end
if v < 0
if complement
digits.map! {|d| radix-1 - d }
carry = 1
digits.each_index {|i|
digits[i] += carry
carry = 0
if radix <= digits[i]
digits[i] -= radix
carry = 1
end
}
if digits.last != radix-1
digits << (radix-1)
end
sign = '..'
else
sign = '-'
end
else
if pl
sign = '+'
elsif sp
sign = ' '
end
end
dlen = digits.length
dlen += 2 if sign == '..'
if v < 0 && complement
d = radix - 1
else
d = 0
end
if precision
if dlen < precision
(precision - dlen).times {
digits << d
}
end
else
if dlen == 0
digits << d
end
end
if type == 'o' && hs
if digits.empty? || digits.last != d
digits << d
end
end
digits.reverse!
str = digits.map {|digit| digitmap[digit] }.join
pad = ''
nlen = prefix.length + sign.length + str.length
if width && nlen < width
len = width - nlen
if zr
if complement && v < 0
pad = digitmap[radix-1] * len
else
pad = '0' * len
end
else
pad = ' ' * len
end
end
if / / =~ pad
if sign == '..'
str = prefix + sign + str
else
str = sign + prefix + str
end
if mi
str = str + pad
else
str = pad + str
end
else
if sign == '..'
str = prefix + sign + pad + str
else
str = sign + prefix + pad + str
end
end
str
end
def self.assertions_format_integer(format)
proc {
VS.each {|v|
r = sprintf format, v
e = emu_int format, v
if true
assert_equal(e, r, "sprintf(#{format.dump}, #{v})")
else
if e != r
puts "#{e.dump}\t#{r.dump}\tsprintf(#{format.dump}, #{v})"
end
end
}
}
end
combination(%w[B b d o X x],
[nil, 0, 5, 20],
["", ".", ".0", ".8", ".20"],
*FLAGS) {|type, width, precision, sp, hs, pl, mi, zr|
format = "%#{sp}#{hs}#{pl}#{mi}#{zr}#{width}#{precision}#{type}"
define_method("test_format_integer(#{format})", assertions_format_integer(format))
}
FLOAT_VALUES = [
-1e100,
-123456789.0,
-1.0,
-0.0,
0.0,
0.01,
1/3.0,
2/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 self.assertions_format_float(format)
proc {
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
combination(%w[e E f g G],
[nil, 0, 5, 20],
["", ".", ".0", ".8", ".20", ".200", ".9999"],
*FLAGS) {|type, width, precision, sp, hs, pl, mi, zr|
format = "%#{sp}#{hs}#{pl}#{mi}#{zr}#{width}#{precision}#{type}"
define_method("test_format_float(#{format})", assertions_format_float(format))
}
end