-
Notifications
You must be signed in to change notification settings - Fork 21
Reimplement show #60
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
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 0s, 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
retqOn v0.7 (built today) (much too long to paste here!):
https://gist.github.com/ScottPJones/8531146104d22a3a46b6777e90968b42
src/DecFP.jl
Outdated
| $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 0s, 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
showwithout using@sprintf. The motivation for doing this is toshowvalues 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
@sprintfcc @ScottPJones