Skip to content

Implement promotion among UFixed, make convert errors more informative #53

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

Merged
merged 6 commits into from
Sep 24, 2016

Conversation

timholy
Copy link
Member

@timholy timholy commented Sep 23, 2016

On the master branch, we have the following bug:

julia> UFixed8(0.5) < UFixed16(0.8)
ERROR: no promotion exists for FixedPointNumbers.UFixed{UInt8,8} and FixedPointNumbers.UFixed{UInt16,16}
 in promote(::FixedPointNumbers.UFixed{UInt8,8}, ::FixedPointNumbers.UFixed{UInt16,16}) at ./promotion.jl:153
 in <(::FixedPointNumbers.UFixed{UInt8,8}, ::FixedPointNumbers.UFixed{UInt16,16}) at ./promotion.jl:204

We also have this uninformative message:

julia> convert(UFixed8, 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(::Type{FixedPointNumbers.UFixed{UInt8,8}}, ::Float64) at /home/tim/.julia/v0.5/FixedPointNumbers/src/ufixed.jl:46

This PR fixes the bug by implementing promote_type among UFixed types. It also produces the more verbose error message:

julia> convert(UFixed8, 1.2)
ERROR: ArgumentError: FixedPointNumbers.UFixed{UInt8,8} is an 8-bit type representing 256 values from 0.0 to 1.0; cannot represent 1.2
 in throw_converterror(::Type{FixedPointNumbers.UFixed{UInt8,8}}, ::Float64) at /home/tim/.julia/v0.5/FixedPointNumbers/src/FixedPointNumbers.jl:118
 in _convert(::Type{FixedPointNumbers.UFixed{UInt8,8}}, ::Type{UInt8}, ::Float64) at /home/tim/.julia/v0.5/FixedPointNumbers/src/ufixed.jl:53
 in convert(::Type{FixedPointNumbers.UFixed{UInt8,8}}, ::Float64) at /home/tim/.julia/v0.5/FixedPointNumbers/src/ufixed.jl:50

Assuming people like this, I think it should be merged before #51, and we can tag a new version before making a more incompatible change.

@timholy
Copy link
Member Author

timholy commented Sep 23, 2016

Closing and reopening to see if AppVeyor triggers.

@timholy timholy closed this Sep 23, 2016
@timholy timholy reopened this Sep 23, 2016
@timholy timholy force-pushed the teh/convert_promote branch from 9d2bdf3 to 59331a8 Compare September 23, 2016 13:43
Copy link
Collaborator

@vchuravy vchuravy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

U(_unsafe_trunc(T, y), 0)
end
function _convert{U<:UFixed}(::Type{U}, ::Type{UInt128}, x)
y = round(rawone(U)*x) # for UInt128, we can't widen
Copy link
Collaborator

@vchuravy vchuravy Sep 23, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we definedwiden1(::Type{UInt128}) = UInt128 we wouldn't need to duplicate the code path and we would be forward compatible for the glorious day when we have UInt256.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about that. The flip side is that if one is counting on widen1 to actually protect you, then you'll be mistaken.

But perhaps that's at too low of a level to matter, and that the only issue that matters is whether convert successfully catches the problem. Which refusing to defining widen1 doesn't actually help with. I will change it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, we had a number of problems with high-precision UFixed types.

@timholy timholy force-pushed the teh/convert_promote branch 2 times, most recently from ef03354 to a399254 Compare September 24, 2016 00:51
convert{T1<:UFixed}(::Type{T1}, x::UFixed) = reinterpret(T1, round(rawtype(T1), (rawone(T1)/rawone(x))*reinterpret(x)))
function convert{T<:UFixed}(::Type{T}, x::UFixed)
y = round((rawone(T)/rawone(x))*reinterpret(x))
(0 <= y) & (y <= typemax(rawtype(T))) || throw_converterror(T, x)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not just say (0 <= y <= typemax(rawtype(T))) || ... ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Performance. Currently a <= x <= b gets lowered to (a <= x) && (x <= b) rather than (a <= x) & (x <= b), which means there are two branches rather than one.

This is something that should probably be changed in Julia, but neither I nor anyone else has gotten to it yet.

@timholy timholy force-pushed the teh/convert_promote branch 3 times, most recently from 39ef3f5 to 18615f8 Compare September 24, 2016 10:17
@timholy timholy force-pushed the teh/convert_promote branch 2 times, most recently from a0628e5 to 526a7b9 Compare September 24, 2016 10:44
@timholy timholy merged commit 020c74f into master Sep 24, 2016
@timholy timholy deleted the teh/convert_promote branch September 24, 2016 11:09
f = 2^nbitsfrac(T)-1
:( T($f,0) )
function one{T<:UFixed}(::Type{T})
T(typemax(rawtype(T)) >> (8*sizeof(T)-nbitsfrac(T)), 0)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the generated version might be faster. see #44 (comment). or does the use of >> instead of ^ close the gap?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LLVM inlines this new version to a constant:

julia> @code_llvm one(UFixed{UInt64, 51})

define %UFixed @julia_one_70810(%jl_value_t*) #0 {
top:
  ret %UFixed { i64 2251799813685247 }
}

The bigger issue was that for UInt128 (or even for UInt64 on 32-bit machines), 2^f was returning 0 for f bigger than the WORD_SIZE. This version is guaranteed to be safe no matter what kind of machine or what kind of precision we're using.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants