-
Notifications
You must be signed in to change notification settings - Fork 22
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
Reimplement show #60
Reimplement show #60
Conversation
Windows build failures unrelated to this PR. |
src/DecFP.jl
Outdated
unsafe_write(io, pointer(_buffer, 2), lastnonzeroindex - 1) | ||
write(io, '0'^(normalized_exponent - lastnonzeroindex + 2), ".0") | ||
elseif normalized_exponent == lastnonzeroindex - 2 | ||
unsafe_write(io, pointer(_buffer, 2), lastnonzeroindex - 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should wrap all of the code that does unsafe_write(..., pointer(_buffer, n))
in GC.@preserve _buffer ....
in Julia 0.7. (In Julia 0.6 it is Base.@gc_preserve
; unfortunately this is not in Compat yet).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GC.@preserve
was added with JuliaLang/julia#25616. Base.@gc_preserve
was added with JuliaLang/julia#23610 in 0.7.0-DEV.1795
. Is there an equivalent for 0.6?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it can be a no-op in 0.6.
The need to be more cautious about pointer
apparently occurred because the gc became more aggressive in 0.7 (JuliaLang/julia#23562), as I understand it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point - I think I've got to look at a lot of my code in Strs.jl
for just that issue, because of the more aggressive GC in master.
src/DecFP.jl
Outdated
if normalized_exponent == -1 | ||
write(io, "0.") | ||
else | ||
write(io, "0.", '0'^(-normalized_exponent - 1)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that this allocates a temporary string. Might be better to define a writezeros(io, n) = for i=1:n; write(io, UInt8('0')); end
function
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That might be a bit slow, even some small tables would help a lot, say strings of 0
s, 1:9
, then strings of 0
's, 10:10:90
,
then 100:100:1000
. The exponent for Dec128
can be -6143..6144
, so at most you'd need to write out the 1000 "0" string 6 times, the length 100 "0" once, a 40 "0" once, and a 4 "0" string, but all with no allocation (with a table size of > 6105, but it could be used by other numeric output packages)
src/DecFP.jl
Outdated
r = normalized_exponent | ||
while b > 0 | ||
q, r = divrem(r, b) | ||
write(io, Char('0' + q)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is the call to Char
needed? '0' + q
should already give a Char, no? Maybe more efficient to do UInt8('0')+(q%UInt8)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, Steven is correct. Converting what is an ASCII character to Char
, and then have to have that checked to see how many bytes it might be, and deal with the new representation of Char
, would be inefficient. I'd just put write(io, q%UInt8 + 0x30)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
write(io, 0x30 + q%UInt8)
would also work just fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(UInt8('0')
will get inlined as 0x30
and is more readable to people who don't have ASCII memorized 😉.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As long as it still gets in-lined as 0x30
, I was afraid with the changes to Char
, it might not on master.
Note that if you have UInt8('0' + q) instead of UInt8('0') + UInt8(q), it generates positively horrible code since #24999.
On v0.6.2:
julia> f(q) = ('0' + q)%UInt8
f (generic function with 1 method)
julia> @code_native f(0x9)
.section __TEXT,__text,regular,pure_instructions
Filename: REPL[4]
pushq %rbp
movq %rsp, %rbp
Source line: 4
addb $48, %dil
Source line: 1
movl %edi, %eax
popq %rbp
retq
On v0.7 (built today) (much too long to paste here!):
https://gist.github.com/ScottPJones/8531146104d22a3a46b6777e90968b42
src/DecFP.jl
Outdated
@@ -105,6 +97,83 @@ for w in (32,64,128) | |||
|
|||
$BID(x::AbstractString) = parse($BID, x) | |||
|
|||
function Base.show(io::IO, x::$BID) | |||
if isnan(x) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You might consider this stylistic, but this code is so long. What do you think of shortening it up a bit, i.e.:
isnan(x) && return write(io, "NaN")
isinf(x) && return write(io, signbit(x) ? "-Inf" : "Inf")
x == 0 && return write(io, signbit(x) ? "-0.0" : "0.0")
ccall(($(bidsym(w,"to_string")), libbid), Cvoid, (Ptr{UInt8}, $BID), _buffer, x)
_buffer[1] == '-'%UInt8 && write(io, '-'%UInt8)
5 lines instead of 24, and IMO easier to read.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can appreciate the shorter code but your use of return
has me considering something that I had overlooked before. Even in my original PR, show
sometimes returns nothing
and sometimes returns an Int
with no consistent meaning due to a lack of return
at the end of the function. Should I add an explicit return
while keeping my other returns as is to ensure show
always returns nothing
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, instead of && return write(io, ...
, you could have && (write(io, ...); return)
instead, to make sure it's always returning nothing
src/DecFP.jl
Outdated
if normalized_exponent == -1 | ||
write(io, "0.") | ||
else | ||
write(io, "0.", '0'^(-normalized_exponent - 1)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That might be a bit slow, even some small tables would help a lot, say strings of 0
s, 1:9
, then strings of 0
's, 10:10:90
,
then 100:100:1000
. The exponent for Dec128
can be -6143..6144
, so at most you'd need to write out the 1000 "0" string 6 times, the length 100 "0" once, a 40 "0" once, and a 4 "0" string, but all with no allocation (with a table size of > 6105, but it could be used by other numeric output packages)
normalized_exponent = -normalized_exponent | ||
end | ||
b_lb = div(normalized_exponent, 10) | ||
b = 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this use a table, or in some way do fewer multiplications?
b *= 10 | ||
end | ||
r = normalized_exponent | ||
while b > 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This whole loop concerns me, it seems to be doing quite a lot of divisions for each digit output.
Can't you start outputting from the end, and then divide by 10, and get the remainder, every time through the loop.
src/DecFP.jl
Outdated
# %f | ||
if normalized_exponent >= 0 | ||
if normalized_exponent > lastnonzeroindex - 2 | ||
unsafe_write(io, pointer(_buffer, 2), lastnonzeroindex - 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this always starting at 2? If it's positive, does the library "to_string" function output a +
?
normalized_exponent = nox(ccall(($(bidsym(w,"ilogb")), libbid), Cint, ($BID,), x)) | ||
lastdigitindex = Compat.findfirst(equalto(UInt8('E')), _buffer) - 1 | ||
lastnonzeroindex = Compat.findlast(!equalto(UInt8('0')), view(_buffer, 1:lastdigitindex)) | ||
if -5 < normalized_exponent < 6 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just set a variable here for the pointer to _buffer
, i.e. pnt = pointer(buffer)
?
pointer(_buffer, 2)
would become pnt + 1
.
src/DecFP.jl
Outdated
unsafe_write(io, pointer(_buffer, 2), lastnonzeroindex - 1) | ||
write(io, '0'^(normalized_exponent - lastnonzeroindex + 2), ".0") | ||
elseif normalized_exponent == lastnonzeroindex - 2 | ||
unsafe_write(io, pointer(_buffer, 2), lastnonzeroindex - 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point - I think I've got to look at a lot of my code in Strs.jl
for just that issue, because of the more aggressive GC in master.
src/DecFP.jl
Outdated
r = normalized_exponent | ||
while b > 0 | ||
q, r = divrem(r, b) | ||
write(io, Char('0' + q)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
write(io, 0x30 + q%UInt8)
would also work just fine.
Don't mind the detailed review - this is very good that you are doing this! 👍💯 |
Unrelated CI errors. |
Reimplement
show
without using@sprintf
. The motivation for doing this is toshow
values without rounding.A side effect of the reimplementation is some recently reported display issues also go away
Please keep issue #59 open since I still need to address this in
@sprintf
cc @ScottPJones