Skip to content

Commit

Permalink
Merge pull request JuliaLang#10460 from JuliaLang/jq/grisu2
Browse files Browse the repository at this point in the history
Add custom Bignum type for internal grisu performance. Fixes JuliaLang#8972
  • Loading branch information
quinnj committed Mar 10, 2015
2 parents 2f334e2 + 724d754 commit d2dfb52
Show file tree
Hide file tree
Showing 7 changed files with 830 additions and 333 deletions.
17 changes: 10 additions & 7 deletions base/grisu.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@ include("grisu/float.jl")
include("grisu/fastshortest.jl")
include("grisu/fastprecision.jl")
include("grisu/fastfixed.jl")
include("grisu/bignums.jl")
include("grisu/bignum.jl")

function grisu(v::FloatingPoint,mode,requested_digits,buffer=DIGITS)
const BIGNUMS = [Bignums.Bignum(),Bignums.Bignum(),Bignums.Bignum(),Bignums.Bignum()]

function grisu(v::FloatingPoint,mode,requested_digits,buffer=DIGITS,bignums=BIGNUMS)
if signbit(v)
neg = true
v = -v
Expand All @@ -34,15 +37,15 @@ function grisu(v::FloatingPoint,mode,requested_digits,buffer=DIGITS)
return len, point, neg, buffer
end
if mode == SHORTEST
status,len,point,buf = fastshortest(v,buffer)
status,len,point = fastshortest(v,buffer)
elseif mode == FIXED
status,len,point,buf = fastfixedtoa(v,0,requested_digits,buffer)
status,len,point = fastfixedtoa(v,0,requested_digits,buffer)
elseif mode == PRECISION
status,len,point,buf = fastprecision(v,requested_digits,buffer)
status,len,point = fastprecision(v,requested_digits,buffer)
end
status && return len-1, point, neg, buf
status, len, point, buf = bignumdtoa(v,mode,requested_digits,buffer)
return len-1, point, neg, buf
status && return len-1, point, neg, buffer
status, len, point = bignumdtoa(v,mode,requested_digits,buffer,bignums)
return len-1, point, neg, buffer
end

_show(io::IO, x::FloatingPoint, mode, n::Int, t) =
Expand Down
208 changes: 99 additions & 109 deletions base/grisu/bignum.jl
Original file line number Diff line number Diff line change
@@ -1,93 +1,91 @@
function normalizedexponent(significand, exponent::Int32)
significand::UInt64
significand = UInt64(significand)
while (significand & HiddenBit(Float64)) == 0
significand <<= 1
exponent -= 1
significand <<= UInt64(1)
exponent -= Int32(1)
end
return Int32(exponent)
return exponent
end

function bignumdtoa(v,mode,requested_digits::Int,buffer)
function bignumdtoa(v,mode,requested_digits::Int,buffer,bignums)
significand = _significand(v)
exponent = _exponent(v)
lower_boundary_is_closer = lowerboundaryiscloser(v)
need_boundary_deltas = mode == SHORTEST

is_even = (significand & 1) == 0
normalized_exponent = normalizedexponent(significand, exponent)
estimated_power = estimatepower(normalized_exponent)
estimated_power = estimatepower(Int(normalized_exponent))

if mode == FIXED && -estimated_power - 1 > requested_digits
buffer[1] = 0
len = 1
decimal_point = -requested_digits
return true, len, decimal_point, buffer
return true, len, decimal_point
end
num = den = minus = plus = BigInt(0)
num, den, minus, plus = initialscaledstartvalues(significand,exponent,lower_boundary_is_closer,
num, den, minus, plus = bignums[1], bignums[2], bignums[3], bignums[4]
initialscaledstartvalues!(significand,exponent,lower_boundary_is_closer,
estimated_power,need_boundary_deltas,
num,den,minus,plus);
num, den, minus, plus, decimal_point = fixupmultiply10(
estimated_power,is_even,num,den,minus,plus);

num,den,minus,plus)
decimal_point = fixupmultiply10!(estimated_power,is_even,num,den,minus,plus)
if mode == SHORTEST
len, buffer = generateshortestdigits(num,den,minus,plus,is_even,buffer)
len = generateshortestdigits!(num,den,minus,plus,is_even,buffer)
elseif mode == FIXED
len, decimal_point, buffer = bignumtofixed(requested_digits,num,den,buffer,decimal_point)
len, decimal_point = bignumtofixed!(requested_digits,num,den,buffer,decimal_point)
elseif mode == PRECISION
len, decimal_point, buffer = generatecounteddigits(requested_digits,num,den,buffer,decimal_point)
len, decimal_point = generatecounteddigits!(requested_digits,num,den,buffer,decimal_point)
end
buffer[len] = 0
return true, len, decimal_point, buffer
return true, len, decimal_point
end

function generateshortestdigits(num,den,minus,plus,is_even,buffer)
function generateshortestdigits!(num,den,minus,plus,is_even,buffer)
minus == plus && (plus = minus)
len = 1
while true
digit, num = divrem(num,den)
buffer[len] = 0x30 + digit
digit = Bignums.dividemodulointbignum!(num,den)
buffer[len] = 0x30 + (digit % UInt8)
len += 1
in_delta_room_minus = is_even ? num <= minus : num < minus
in_delta_room_plus = is_even ? num + plus >= den : num + plus > den

in_delta_room_minus = is_even ?
Bignums.lessequal(num,minus) : Bignums.less(num,minus)
in_delta_room_plus = is_even ?
Bignums.pluscompare(num,plus,den) >= 0: Bignums.pluscompare(num,plus,den) > 0
if !in_delta_room_minus && !in_delta_room_plus
num *= 10
minus *= 10
if minus != plus
plus *= 10
end
Bignums.times10!(num)
Bignums.times10!(minus)
minus != plus && Bignums.times10!(plus)
elseif in_delta_room_minus && in_delta_room_plus
compare = num + num
if compare < den
elseif compare > den
compare = Bignums.pluscompare(num,num,den)
if compare < 0
elseif compare > 0
buffer[len - 1] += 1
else
if (buffer[len - 1] - 0x30) % 2 == 0
else
buffer[len - 1] += 1
end
end
return len, buffer
return len
elseif in_delta_room_minus
return len, buffer
return len
else
buffer[len - 1] += 1
return len, buffer
return len
end
end
end

function generatecounteddigits(count,num,den,buffer,decimal_point)
function generatecounteddigits!(count,num,den,buffer,decimal_point)
for i = 1:(count-1)
digit, num = divrem(num,den)
buffer[i] = 0x30 + digit
num *= 10
digit = Bignums.dividemodulointbignum!(num,den)
buffer[i] = 0x30 + (digit % UInt8)
Bignums.times10!(num)
end
digit, num = divrem(num,den)
if num + num >= den
digit = Bignums.dividemodulointbignum!(num,den)
if Bignums.pluscompare(num,num,den) >= 0
digit += 1
end
buffer[count] = 0x30 + digit
buffer[count] = 0x30 + (digit % UInt8)
for i = count:-1:2
buffer[i] != 0x30 + 10 && break
buffer[i] = 0x30
Expand All @@ -98,130 +96,122 @@ function generatecounteddigits(count,num,den,buffer,decimal_point)
decimal_point += 1
end
len = count+1
return len, decimal_point, buffer
return len, decimal_point
end

function bignumtofixed(requested_digits,num,den,buffer,decimal_point)
function bignumtofixed!(requested_digits,num,den,buffer,decimal_point)
if -decimal_point > requested_digits
decimal_point = -requested_digits
len = 1
return len, decimal_point, buffer
return len, decimal_point
elseif -decimal_point == requested_digits
den *= 10
if num + num >= den
Bignums.times10!(den)
if Bignums.pluscompare(num,num,den) >= 0
buffer[1] = 0x31
len = 2
decimal_point += 1
else
len = 1
end
return len, decimal_point, buffer
return len, decimal_point
else
needed_digits = decimal_point + requested_digits
len, decimal_point, buffer = generatecounteddigits(
len, decimal_point = generatecounteddigits!(
needed_digits,num,den,buffer,decimal_point)
end
return len, decimal_point, buffer
return len, decimal_point
end


const k1Log10 = 0.30102999566398114
function estimatepower(exponent)
kSignificandSize = SignificandSize(Float64)
estimate = ceil((exponent + kSignificandSize - 1) * k1Log10 - 1e-10)
return estimate
end

const kSignificandSize = SignificandSize(Float64)
estimatepower(exponent::Int) = ceil(Int,(exponent + kSignificandSize - 1) * k1Log10 - 1e-10)

function init3(
function init3!(
significand,exponent,estimated_power,need_boundary_deltas,
num,den,minus,plus)
num::BigInt = BigInt(significand)
num <<= exponent
den::BigInt = BigInt(10)^estimated_power
Bignums.assignuint64!(num,UInt64(significand))
Bignums.shiftleft!(num,exponent)
Bignums.assignpoweruint16!(den,UInt16(10),estimated_power)
if need_boundary_deltas
den <<= 1
num <<= 1
plus::BigInt = BigInt(1)
plus <<= exponent
minus::BigInt = BigInt(1)
minus <<= exponent
end
return num, den, minus, plus
Bignums.shiftleft!(den,1)
Bignums.shiftleft!(num,1)
Bignums.assignuint16!(plus,UInt16(1))
Bignums.shiftleft!(plus,exponent)
Bignums.assignuint16!(minus,UInt16(1))
Bignums.shiftleft!(minus,exponent)
end
return
end


function init1(
function init1!(
significand,exponent,estimated_power,need_boundary_deltas,
num,den,minus,plus)
num::BigInt = BigInt(significand)
den::BigInt = BigInt(10)^estimated_power
den <<= -exponent
Bignums.assignuint64!(num,UInt64(significand))
Bignums.assignpoweruint16!(den,UInt16(10),estimated_power)
Bignums.shiftleft!(den,-exponent)
if need_boundary_deltas
den <<= 1
num <<= 1
plus::BigInt = 1
minus::BigInt = 1
Bignums.shiftleft!(den,1)
Bignums.shiftleft!(num,1)
Bignums.assignuint16!(plus,UInt16(1))
Bignums.assignuint16!(minus,UInt16(1))
end
return num, den, minus, plus
return
end

function init2(
function init2!(
significand,exponent,estimated_power,need_boundary_deltas,
num,den,minus,plus)
num::BigInt = BigInt(10)^-estimated_power
power_ten = num
Bignums.assignpoweruint16!(power_ten,UInt16(10),-estimated_power)
if need_boundary_deltas
plus = num
minus = num
Bignums.assignbignum!(plus,power_ten)
Bignums.assignbignum!(minus,power_ten)
end
num *= significand
den::BigInt = BigInt(1)
den <<= -exponent
Bignums.multiplybyuint64!(num,UInt64(significand))
Bignums.assignuint16!(den,UInt16(1))
Bignums.shiftleft!(den,-exponent)
if need_boundary_deltas
num <<= 1
den <<= 1
Bignums.shiftleft!(num,1)
Bignums.shiftleft!(den,1)
end
return num, den, minus, plus
return
end

function initialscaledstartvalues(significand,
function initialscaledstartvalues!(significand,
exponent,lower_boundary_is_closer,estimated_power,
need_boundary_deltas,num,den,minus,plus)
if exponent >= 0
num,den,minus,plus = init3(
significand, exponent, estimated_power, need_boundary_deltas,
num,den,minus,plus)
init3!(significand, exponent, estimated_power, need_boundary_deltas,num,den,minus,plus)
elseif estimated_power >= 0
num,den,minus,plus = init1(
significand, exponent, estimated_power, need_boundary_deltas,
num,den,minus,plus)
init1!(significand, exponent, estimated_power, need_boundary_deltas,num,den,minus,plus)
else
num,den,minus,plus = init2(
significand, exponent, estimated_power, need_boundary_deltas,
num,den,minus,plus)
init2!(significand, exponent, estimated_power, need_boundary_deltas,num,den,minus,plus)
end
if need_boundary_deltas && lower_boundary_is_closer
den::BigInt <<= 1
num::BigInt <<= 1
plus::BigInt <<= 1
Bignums.shiftleft!(den,1)
Bignums.shiftleft!(num,1)
Bignums.shiftleft!(plus,1)
end
return num, den, minus, plus
return
end

function fixupmultiply10(estimated_power,is_even,num,den,minus,plus)
in_range = is_even ? num + plus >= den : num + plus > den
function fixupmultiply10!(estimated_power,is_even,num,den,minus,plus)
in_range = is_even ? Bignums.pluscompare(num,plus,den) >= 0 :
Bignums.pluscompare(num,plus,den) > 0
if in_range
decimal_point::Int32 = estimated_power + 1
decimal_point = estimated_power + 1
else
decimal_point = estimated_power
num::BigInt *= 10
Bignums.times10!(num)
if minus == plus
minus::BigInt *= 10
plus::BigInt = minus
Bignums.times10!(minus)
Bignums.assignbignum!(plus,minus)
else
minus *= 10
plus *= 10
Bignums.times10!(minus)
Bignums.times10!(plus)
end
end
return num, den, minus, plus, decimal_point
return decimal_point
end
Loading

0 comments on commit d2dfb52

Please sign in to comment.