Add unchecked conversions - #49
Conversation
| _convert{U<:UFixed }(::Type{U}, ::Type{UInt128}, x) = U(round(UInt128, rawone(U)*x), 0) | ||
|
|
||
| rem{T<:UFixed}(x::T, ::Type{T}) = x | ||
| rem{T<:UFixed}(x::UFixed, ::Type{T}) = reinterpret(T, rem(Integer(round((rawone(T)/rawone(x))*reinterpret(x))), rawtype(T))) |
There was a problem hiding this comment.
you could use round(Integer, x) directly or reinterpret(T, round((rawone(T)/rawone(x)) * reinterpret(x)) % rawtype(T)) also since this will do floating point division with Float64 I would replace Interger with Int64.
It might be worthwhile thinking about using div(widenmul(rawone(T), reinterpret(x)), rawone(x)) if it doesn't lose to much precision.
There was a problem hiding this comment.
Very interesting. I looked at three "inner" implementations:
@inline iscale1{T}(::Type{T}, x) = round(UInt, (rawone(T)/rawone(x))*reinterpret(x))
@inline iscale1a{T}(::Type{T}, x) = unsafe_trunc(UInt, round((rawone(T)/rawone(x))*reinterpret(x)))
@inline function iscale2{T}(::Type{T}, x)
d, r = divrem(widemul(rawone(T), reinterpret(x)), rawone(x))
d + oftype(d, r >= rawone(x)>>1)
endThe fastest seems to be iscale1a. Here's its LLVM:
julia> @code_llvm iscale1a(UFixed12, one(UFixed10))
define i64 @julia_iscale1a_70902(%jl_value_t*, %UFixed.65*) #0 {
top:
%2 = getelementptr inbounds %UFixed.65, %UFixed.65* %1, i64 0, i32 0
%3 = load i16, i16* %2, align 2
%4 = uitofp i16 %3 to double
%5 = fmul double %4, 0x40100300C0300C03
%6 = call double @llvm.rint.f64(double %5)
%7 = fptoui double %6 to i64
ret i64 %7
}iscale1 looks similar except the last expression is a call, and that call turns out to involve a branch.
There was a problem hiding this comment.
How interesting that going to FP makes this faster. It seems that divrem is killing the performance. Using only rem makes it as fast
These are most useful in conjunction with pre-checking, for example if you want to return a more informative error than InexactError
803ae9e to
4e96201
Compare
|
Wow, with the new implementation, julia> grayold(x) = UFixed8(x)
grayold (generic function with 1 method)
julia> y = rand(10^4);
julia> using BenchmarkTools
julia> gray.(y);
julia> grayold.(y);
julia> @benchmark gray.($y)
BenchmarkTools.Trial:
samples: 10000
evals/sample: 1
time tolerance: 5.00%
memory tolerance: 1.00%
memory estimate: 9.95 kb
allocs estimate: 2
minimum time: 27.08 μs (0.00% GC)
median time: 27.20 μs (0.00% GC)
mean time: 27.53 μs (0.00% GC)
maximum time: 48.52 μs (0.00% GC)
julia> @benchmark grayold.($y)
BenchmarkTools.Trial:
samples: 10000
evals/sample: 1
time tolerance: 5.00%
memory tolerance: 1.00%
memory estimate: 9.95 kb
allocs estimate: 2
minimum time: 33.79 μs (0.00% GC)
median time: 33.89 μs (0.00% GC)
mean time: 34.12 μs (0.00% GC)
maximum time: 67.45 μs (0.00% GC)
julia> gray(1.2)
ERROR: 1.2 is not between 0.0 and 1.0 and cannot be converted to type FixedPointNumbers.UFixed{UInt8,8}
in throw_converterror(::Float64, ::Type{FixedPointNumbers.UFixed{UInt8,8}}) at ./REPL[3]:2
in gray(::Float64) at ./REPL[2]:2
julia> grayold(1.2)
ERROR: InexactError()
in trunc(::Type{UInt8}, ::Float64) at ./float.jl:458
in _convert at /home/tim/.julia/v0.5/FixedPointNumbers/src/ufixed.jl:47 [inlined]
in convert at /home/tim/.julia/v0.5/FixedPointNumbers/src/ufixed.jl:46 [inlined]
in Type at /home/tim/.julia/v0.5/FixedPointNumbers/src/ufixed.jl:8 [inlined]
in grayold(::Float64) at ./REPL[4]:1 |
| _convert{U<:UFixed }(::Type{U}, ::Type{UInt128}, x) = U(round(UInt128, rawone(U)*x), 0) | ||
|
|
||
| rem{T<:UFixed}(x::T, ::Type{T}) = x | ||
| rem{T<:UFixed}(x::UFixed, ::Type{T}) = reinterpret(T, rem(unsafe_trunc(UInt, round((rawone(T)/rawone(x))*reinterpret(x))), rawtype(T))) |
There was a problem hiding this comment.
unsafe_trunc(rawtype(T), ...)
There was a problem hiding this comment.
Oh, shoot, just noticed this. Seems like a good idea. I will try to remember to fix this in the morning.
|
I'll merge this now, and depending on the decision on JuliaLang/julia#18521 we might switch |
These are most useful in conjunction with pre-checking, for example if you want to return a more informative error than InexactError:
The performance of
grayis within spitting distance of just callingconvert(UFixed8, x). I'm going to use this in ColorTypes, since people are perennially confused by this (e.g., JuliaGraphics/Colors.jl#252).