2015-12-16 00:07:31 -05:00
|
|
|
# frozen_string_literal: false
|
2008-01-24 23:42:36 -05:00
|
|
|
require 'test/unit'
|
2008-02-29 23:12:13 -05:00
|
|
|
require_relative 'allpairs'
|
2008-01-24 23:42:36 -05:00
|
|
|
|
|
|
|
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!
|
|
|
|
|
2013-09-10 23:00:42 -04:00
|
|
|
FLAGS = [['', ' '], ['', '#'], ['', '+'], ['', '-'], ['', '0']]
|
|
|
|
|
|
|
|
def self.combination(*args, &b)
|
2008-03-05 11:24:09 -05:00
|
|
|
#AllPairs.exhaustive_each(*args, &b)
|
2008-02-29 23:12:13 -05:00
|
|
|
AllPairs.each(*args, &b)
|
2008-01-24 23:42:36 -05:00
|
|
|
end
|
|
|
|
|
2008-03-05 11:24:09 -05:00
|
|
|
def emu_int(format, v)
|
|
|
|
/\A%( )?(\#)?(\+)?(-)?(0)?(\d+)?(?:\.(\d*))?(.)\z/ =~ format
|
2008-01-24 23:42:36 -05:00
|
|
|
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
|
2008-10-14 10:08:36 -04:00
|
|
|
when 'B'
|
|
|
|
radix = 2
|
|
|
|
digitmap = {0 => '0', 1 => '1'}
|
|
|
|
complement = !pl && !sp
|
|
|
|
prefix = '0B' if hs && v != 0
|
2008-01-24 23:42:36 -05:00
|
|
|
when 'b'
|
|
|
|
radix = 2
|
|
|
|
digitmap = {0 => '0', 1 => '1'}
|
|
|
|
complement = !pl && !sp
|
2008-02-29 19:38:33 -05:00
|
|
|
prefix = '0b' if hs && v != 0
|
2008-01-24 23:42:36 -05:00
|
|
|
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
|
2008-02-29 19:38:33 -05:00
|
|
|
prefix = '0X' if hs && v != 0
|
2008-01-24 23:42:36 -05:00
|
|
|
when 'x'
|
|
|
|
radix = 16
|
|
|
|
digitmap = {}
|
|
|
|
16.times {|i| digitmap[i] = i.to_s(16) }
|
|
|
|
complement = !pl && !sp
|
2008-02-29 19:38:33 -05:00
|
|
|
prefix = '0x' if hs && v != 0
|
2008-01-24 23:42:36 -05:00
|
|
|
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
|
2010-10-15 04:31:58 -04:00
|
|
|
sign = '..'
|
2008-01-24 23:42:36 -05:00
|
|
|
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
|
2010-10-15 04:31:58 -04:00
|
|
|
if digits.empty? || digits.last != d
|
|
|
|
digits << d
|
2008-01-24 23:42:36 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
digits.reverse!
|
|
|
|
|
2010-01-25 18:12:50 -05:00
|
|
|
str = digits.map {|digit| digitmap[digit] }.join
|
2008-01-24 23:42:36 -05:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2013-09-10 23:00:42 -04:00
|
|
|
def self.assertions_format_integer(format)
|
|
|
|
proc {
|
2008-01-24 23:42:36 -05:00
|
|
|
VS.each {|v|
|
|
|
|
r = sprintf format, v
|
2008-03-05 11:24:09 -05:00
|
|
|
e = emu_int format, v
|
2008-01-24 23:42:36 -05:00
|
|
|
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
|
2008-03-05 11:24:09 -05:00
|
|
|
|
2013-09-10 23:00:42 -04:00
|
|
|
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))
|
|
|
|
}
|
|
|
|
|
2008-03-05 11:24:09 -05:00
|
|
|
FLOAT_VALUES = [
|
|
|
|
-1e100,
|
|
|
|
-123456789.0,
|
|
|
|
-1.0,
|
|
|
|
-0.0,
|
|
|
|
0.0,
|
|
|
|
0.01,
|
|
|
|
1/3.0,
|
2008-03-07 03:31:41 -05:00
|
|
|
2/3.0,
|
2008-03-05 11:24:09 -05:00
|
|
|
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
|
|
|
|
|
2013-09-10 23:00:42 -04:00
|
|
|
def self.assertions_format_float(format)
|
|
|
|
proc {
|
2008-03-05 11:24:09 -05:00
|
|
|
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
|
2013-09-10 23:00:42 -04:00
|
|
|
|
|
|
|
combination(%w[e E f g G],
|
|
|
|
[nil, 0, 5, 20],
|
2013-09-12 07:07:30 -04:00
|
|
|
["", ".", ".0", ".8", ".20", ".200", ".9999"],
|
2013-09-10 23:00:42 -04:00
|
|
|
*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))
|
|
|
|
}
|
2008-01-24 23:42:36 -05:00
|
|
|
end
|