Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add hexfloat (%a) support for printf #8729

Merged
merged 1 commit into from
Nov 10, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion base/math.jl
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ sqrt(x::Float32) = box(Float32,sqrt_llvm(unbox(Float32,x)))
sqrt(x::Real) = sqrt(float(x))
@vectorize_1arg Number sqrt

for f in (:ceil, :trunc, :significand) # :rint, :nearbyint
for f in (:ceil, :trunc, :significand, :rint) # :nearbyint
@eval begin
($f)(x::Float64) = ccall(($(string(f)),libm), Float64, (Float64,), x)
($f)(x::Float32) = ccall(($(string(f,"f")),libm), Float32, (Float32,), x)
Expand Down
186 changes: 183 additions & 3 deletions base/printf.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ function gen(s::String)
c = lowercase(x[end])
f = c=='f' ? gen_f :
c=='e' ? gen_e :
c=='a' ? gen_a :
c=='g' ? gen_g :
c=='c' ? gen_c :
c=='s' ? gen_s :
Expand All @@ -42,7 +43,6 @@ function parse(s::String)
isempty(s[i:j-1]) || push!(list, s[i:j-1])
flags, width, precision, conversion, k = parse1(s,k)
'\'' in flags && error("printf format flag ' not yet supported")
conversion == 'a' && error("printf feature %a not yet supported")
conversion == 'n' && error("printf feature %n not supported")
push!(list, conversion == '%' ? "%" : (flags,width,precision,conversion))
i = j = k
Expand Down Expand Up @@ -198,7 +198,7 @@ function print_fixed(out, precision, pt, ndigits)
end
end

function print_exp(out, exp::Integer)
function print_exp_e(out, exp::Integer)
write(out, exp < 0 ? '-' : '+')
exp = abs(exp)
d = div(exp,100)
Expand All @@ -214,6 +214,13 @@ function print_exp(out, exp::Integer)
write(out, char('0'+rem(exp,10)))
end

function print_exp_a(out, exp::Integer)
write(out, exp < 0 ? '-' : '+')
exp = abs(exp)
print(out, exp)
end


function gen_d(flags::ASCIIString, width::Int, precision::Int, c::Char)
# print integer:
# [dDiu]: print decimal digits
Expand Down Expand Up @@ -442,7 +449,115 @@ function gen_e(flags::ASCIIString, width::Int, precision::Int, c::Char)
for ch in expmark
push!(blk.args, :(write(out, $ch)))
end
push!(blk.args, :(print_exp(out, exp)))
push!(blk.args, :(print_exp_e(out, exp)))
# print space padding
if padding != nothing && '-' in flags
push!(blk.args, pad(width, padding, ' '))
end
# return arg, expr
:(($x)::Real), ex
end

function gen_a(flags::ASCIIString, width::Int, precision::Int, c::Char)
# print float in hexadecimal format
# [a]: lowercase hex float, e.g. -0x1.cfp-2
# [A]: uppercase hex float, e.g. -0X1.CFP-2
#
# flags:
# (#): always print a decimal point
# (0): pad left with zeros
# (-): left justify
# ( ): precede non-negative values with " "
# (+): precede non-negative values with "+"
#
x, ex, blk = special_handler(flags,width)
if c == 'A'
hexmark, expmark = "0X", "P"
fn = :ini_HEX
else
hexmark, expmark = "0x", "p"
fn = :ini_hex
end
# if no precision, print max non-zero
if precision < 0
push!(blk.args, :((do_out, args) = $fn(out,$x, $flags, $width, $precision, $c)))
else
ndigits = min(precision+1,length(DIGITS)-1)
push!(blk.args, :((do_out, args) = $fn(out,$x,$ndigits, $flags, $width, $precision, $c)))
end
ifblk = Expr(:if, :do_out, Expr(:block))
push!(blk.args, ifblk)
blk = ifblk.args[2]
push!(blk.args, :((len, exp, neg) = args))
if precision==0 && '#' in flags
expmark = string(".",expmark)
end
# calculate padding
padding = nothing
if precision > 0
width -= precision+length(hexmark)+length(expmark)+4
# 4 = leading + expsign + 1 exp digit + decimal
else
width -= length(hexmark)+length(expmark)+3+(precision<0 && '#' in flags)
# 3 = leading + expsign + 1 exp digit
end
if '+' in flags || ' ' in flags
width -= 1 # for the sign indicator
if width > 0
padding = :($(width+1) - Base.ndigits(exp))
end
else
if width > 0
padding = :($(width+1) - neg - Base.ndigits(exp))
end
end
if precision < 0 && width > 0
if '#' in flags
padding = :($padding - (len-1))
else
padding = :($padding - (len>1?len:0))
end
end
# print space padding
if padding != nothing && !('-' in flags) && !('0' in flags)
push!(blk.args, pad(width, padding, ' '))
end
# print sign
'+' in flags ? push!(blk.args, :(write(out, neg?'-':'+'))) :
' ' in flags ? push!(blk.args, :(write(out, neg?'-':' '))) :
push!(blk.args, :(neg && write(out, '-')))
# hex prefix
for ch in hexmark
push!(blk.args, :(write(out, $ch)))
end
# print zero padding
if padding != nothing && !('-' in flags) && '0' in flags
push!(blk.args, pad(width, padding, '0'))
end
# print digits
push!(blk.args, :(write(out, DIGITS[1])))
if precision > 0
push!(blk.args, :(write(out, '.')))
push!(blk.args, :(write(out, pointer(DIGITS)+1, $(ndigits-1))))
if ndigits < precision+1
n = precision+1-ndigits
push!(blk.args, pad(n, n, '0'))
end
elseif precision < 0
ifvpblk = Expr(:if, :(len > 1), Expr(:block))
vpblk = ifvpblk.args[2]
if '#' in flags
push!(blk.args, :(write(out, '.')))
else
push!(vpblk.args, :(write(out, '.')))
end
push!(vpblk.args, :(write(out, pointer(DIGITS)+1, len-1)))
push!(blk.args, ifvpblk)
end
for ch in expmark
push!(blk.args, :(write(out, $ch)))
end
push!(blk.args, :(print_exp_a(out, exp)))
# print space padding
if padding != nothing && '-' in flags
push!(blk.args, pad(width, padding, ' '))
Expand Down Expand Up @@ -549,6 +664,10 @@ decode_hex(out, d, flags::ASCIIString, width::Int, precision::Int, c::Char) = (t
decode_HEX(out, d, flags::ASCIIString, width::Int, precision::Int, c::Char) = (true, decode_HEX(d))
fix_dec(out, d, flags::ASCIIString, width::Int, precision::Int, c::Char) = (true, fix_dec(d, precision))
ini_dec(out, d, ndigits::Int, flags::ASCIIString, width::Int, precision::Int, c::Char) = (true, ini_dec(d, ndigits))
ini_hex(out, d, ndigits::Int, flags::ASCIIString, width::Int, precision::Int, c::Char) = (true, ini_hex(d, ndigits))
ini_HEX(out, d, ndigits::Int, flags::ASCIIString, width::Int, precision::Int, c::Char) = (true, ini_HEX(d, ndigits))
ini_hex(out, d, flags::ASCIIString, width::Int, precision::Int, c::Char) = (true, ini_hex(d))
ini_HEX(out, d, flags::ASCIIString, width::Int, precision::Int, c::Char) = (true, ini_HEX(d))


# fallbacks for Real types without explicit decode_* implementation
Expand Down Expand Up @@ -772,9 +891,70 @@ function ini_dec(x::BigInt, n::Int)
return (n, d, decode_dec(iround(x/big(10)^(d-n)))[3])
end


ini_hex(x::Real, n::Int) = ini_hex(x,n,hex_symbols)
ini_HEX(x::Real, n::Int) = ini_hex(x,n,HEX_symbols)

ini_hex(x::Real) = ini_hex(x,hex_symbols)
ini_HEX(x::Real) = ini_hex(x,HEX_symbols)

ini_hex(x::Real, n::Int, symbols::Array{Uint8,1}) = ini_hex(float(x), n, symbols)
ini_hex(x::Real, symbols::Array{Uint8,1}) = ini_hex(float(x), symbols)

function ini_hex(x::SmallFloatingPoint, n::Int, symbols::Array{Uint8,1})
x = float64(x)
if x == 0.0
ccall(:memset, Ptr{Void}, (Ptr{Void}, Cint, Csize_t), DIGITS, '0', n)
return int32(1), int32(0), signbit(x)
else
s, p = frexp(x)
sigbits = 4*min(n-1,13)
s = 0.25*Base.Math.rint(ldexp(s,1+sigbits))
# ensure last 2 exponent bits either 01 or 10
u = (reinterpret(Uint64,s) & 0x003f_ffff_ffff_ffff) >> (52-sigbits)
if n > 14
ccall(:memset, Ptr{Void}, (Ptr{Void}, Cint, Csize_t), DIGITS, '0', n)
end
i = (sizeof(u)<<1)-(leading_zeros(u)>>2)
while i > 0
DIGITS[i] = symbols[(u&0xf)+1]
u >>= 4
i -= 1
end
# pt is the binary exponent
return int32(n), int32(p-1), x < 0.0
end
end

function ini_hex(x::SmallFloatingPoint, symbols::Array{Uint8,1})
x = float64(x)
if x == 0.0
ccall(:memset, Ptr{Void}, (Ptr{Void}, Cint, Csize_t), DIGITS, '0', 1)
return int32(1), int32(0), signbit(x)
else
s, p = frexp(x)
s *= 2.0
u = (reinterpret(Uint64,s) & 0x001f_ffff_ffff_ffff)
t = (trailing_zeros(u) >> 2)
u >>= (t<<2)
n = 14-t
for i = n:-1:1
DIGITS[i] = symbols[(u&0xf)+1]
u >>= 4
end
# pt is the binary exponent
return int32(n), int32(p-1), x < 0.0
end
end


#BigFloat
fix_dec(out, d::BigFloat, flags::ASCIIString, width::Int, precision::Int, c::Char) = bigfloat_printf(out, d, flags, width, precision, c)
ini_dec(out, d::BigFloat, ndigits::Int, flags::ASCIIString, width::Int, precision::Int, c::Char) = bigfloat_printf(out, d, flags, width, precision, c)
ini_hex(out, d::BigFloat, ndigits::Int, flags::ASCIIString, width::Int, precision::Int, c::Char) = bigfloat_printf(out, d, flags, width, precision, c)
ini_HEX(out, d::BigFloat, ndigits::Int, flags::ASCIIString, width::Int, precision::Int, c::Char) = bigfloat_printf(out, d, flags, width, precision, c)
ini_hex(out, d::BigFloat, flags::ASCIIString, width::Int, precision::Int, c::Char) = bigfloat_printf(out, d, flags, width, precision, c)
ini_HEX(out, d::BigFloat, flags::ASCIIString, width::Int, precision::Int, c::Char) = bigfloat_printf(out, d, flags, width, precision, c)
function bigfloat_printf(out, d, flags::ASCIIString, width::Int, precision::Int, c::Char)
fmt_len = sizeof(flags)+4
if width > 0
Expand Down
4 changes: 4 additions & 0 deletions test/strings.jl
Original file line number Diff line number Diff line change
Expand Up @@ -885,6 +885,10 @@ bin_val = hex2bytes("07bf")
@test (@sprintf "%.4e" 1.2345) == "1.2345e+00"
@test (@sprintf "%.0e" 3e142) == "3e+142"
@test (@sprintf "%#.0e" 3e142) == "3.e+142"
# hex float
@test (@sprintf "%a" 1.5) == "0x1.8p+0"
@test (@sprintf "%#.0a" 1.5) == "0x2.p+0"
@test (@sprintf "%+30a" 1/3) == " +0x1.5555555555555p-2"
# chars
@test (@sprintf "%c" 65) == "A"
@test (@sprintf "%c" 'A') == "A"
Expand Down