Skip to content
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

Fix edge cases where inexact conversions to UInt don't throw #51095

Merged
merged 5 commits into from
Aug 30, 2023

Conversation

LilithHafner
Copy link
Member

Fixes #51063

@LilithHafner LilithHafner added kind:bugfix This change fixes an existing bug domain:maths Mathematical functions labels Aug 28, 2023
@@ -939,7 +939,7 @@ for Ti in (Int8, Int16, Int32, Int64, Int128, UInt8, UInt16, UInt32, UInt64, UIn
end
end
function (::Type{$Ti})(x::$Tf)
if ($(Tf(typemin(Ti))) <= x <= $(Tf(typemax(Ti)))) && isinteger(x)
if ($(Tf(typemin(Ti))) <= x < $(Tf(typemax(Ti))+one(Tf))) && isinteger(x)
Copy link
Member

Choose a reason for hiding this comment

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

I think this deserves a source comment about why this isn't equivalent to <=.

Copy link
Member

@Keno Keno left a comment

Choose a reason for hiding this comment

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

Seems quite clever, but I am slightly concerned about corner cases, since this is generic code. For example, if there happens to be a combination of integer and floating point types, with eps(Tf(typemax(Ti))) == 2.0, I think this might still have the issue of potentially giving the wrong answer. Can we instead just directly compute whether Tf(typemax(Ti)) is larger than typemax(Ti) and if so, use prevfloat(Tf(typemax(Ti))) here?

@LilithHafner
Copy link
Member Author

If eps(Tf(typemax(Ti))) == 2.0, then Tf(typemax(Ti)) will be equal to y = typemax(Ti) + big(1) because y is even and we round ties to even. Then Tf(typemax(Ti)) + one(Tf) will also be equal to y because this time we round down (again toward the even value y). So our comparison is x < y, which holds for prevfloat(y) which is equal to typemax(Ti)-1, but not for y itself, which is good because y is not representable by Ti.

since this is generic code

Technically, this is not generic code, it's metaprogramming to create methods with concrete type signatures; eps(Tf(typemax(Ti))) == 2.0 never holds.

julia> for Ti in Base.BitInteger_types, Tf in (Float16, Float32, Float64)
           if eps(Tf(typemax(Ti))) == 2.0
               println((Ti, Tf))
           end
       end

julia> 

@vtjnash
Copy link
Sponsor Member

vtjnash commented Aug 30, 2023

LLVM also now has a well-behaved intrinsic that may not require these complications anymore to avoid these problems and the added cost of working around the undefined behavior. Would this be helpful? https://llvm.org/docs/LangRef.html#llvm-fptoui-sat-intrinsic

@LilithHafner
Copy link
Member Author

Our semantics here are to throw, so the saturating version would not help.
The overhead should be low as well, simply comparing the input with compile time constant floating point values and if is not between them, throwing.
The saturating version would be great for #51069, though.

@LilithHafner
Copy link
Member Author

I plan to go ahead and merge this when tests pass because it is
a) blocking #50812
b) a bug fix
c) thoroughly tested
d) a 1 LOC change

I'm still happy to receive comments and perform revisions in a followup.

@LilithHafner LilithHafner added the status:merge me PR is reviewed. Merge when all tests are passing label Aug 30, 2023
@Keno
Copy link
Member

Keno commented Aug 30, 2023

Technically, this is not generic code, it's metaprogramming to create methods with concrete type signatures; eps(Tf(typemax(Ti))) == 2.0 never holds.

Good enough for me ;)

@LilithHafner LilithHafner merged commit fb76136 into master Aug 30, 2023
4 of 7 checks passed
@LilithHafner LilithHafner deleted the lh/convert-bugfix branch August 30, 2023 19:38
@LilithHafner LilithHafner removed the status:merge me PR is reviewed. Merge when all tests are passing label Aug 30, 2023
@LilithHafner LilithHafner added the backport 1.10 Change should be backported to the 1.10 release label Jan 20, 2024
KristofferC pushed a commit that referenced this pull request Jan 24, 2024
@KristofferC KristofferC mentioned this pull request Jan 24, 2024
33 tasks
KristofferC added a commit that referenced this pull request Feb 6, 2024
Backported PRs:
- [x] #51095 <!-- Fix edge cases where inexact conversions to UInt don't
throw -->
- [x] #52583 <!-- Don't access parent of triangular matrix in powm -->
- [x] #52645 <!-- update --gcthreads section in command line options -->
- [x] #52423 <!-- update nthreads info in versioninfo -->
- [x] #52721 <!-- inference: Guard TypeVar special case against vararg
-->
- [x] #52637 <!-- fix finding bundled stdlibs even if they are e.g.
devved in an environment higher in the load path -->
- [x] #52752 <!-- staticdata: handle cycles in datatypes -->
- [x] #52758 <!-- use a Dict instead of an IdDict for caching of the
`cwstring` for Windows env variables -->
- [x] #51375 <!-- Insert hardcoded backlinks to stdlib doc pages -->
- [x] #52994 <!-- place work-stealing queue indices on different cache
lines to avoid false-sharing -->
- [x] #53015 <!-- Add type assertion in iterate for logicalindex -->
- [x] #53032 <!-- Fix a list in GC devdocs -->
- [x] #52748 
- [x] #52856 
- [x] #52878
- [x] #52754 
- [x] #52228
- [x] #52924
- [x] #52569 <!-- Fix GC rooting during rehashing of iddict -->
- [x] #52605 <!-- Default uplo in symmetric/hermitian -->
- [x] #52618 <!-- heap snapshot: add gc roots and gc finalist roots to
fix unrooted nodes -->
- [x] #52781 <!-- fix type-stability bugs in Ryu code -->
- [x] #53055 <!-- Profile: use full terminal cols to show function name
-->
- [x] #53096 
- [x] #53076 
- [x] #52841 <!-- Extensions: make loading of extensions independent of
what packages are in the sysimage -->
- [x] #52078 <!-- Replace `&hArr;` by `&harr;` in documentation -->
- [x] #53035 <!-- use proper cache-line size variable in work-stealing
queue -->
- [x] #53066 <!-- doc: replace harr HTML entity by unicode -->
- [x] #52996 <!-- Apple silicon has 128 byte alignment so fix our
defines to match -->
- [x] #53121 

Non-merged PRs with backport label:
- [ ] #52694 <!-- Reinstate similar for AbstractQ for backward
compatibility -->
- [ ] #51479 <!-- prevent code loading from lookin in the versioned
environment when building Julia -->
@KristofferC KristofferC removed the backport 1.10 Change should be backported to the 1.10 release label Feb 6, 2024
Drvi pushed a commit to RelationalAI/julia that referenced this pull request Jun 7, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
domain:maths Mathematical functions kind:bugfix This change fixes an existing bug
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Incorrect conversion to unisigned integer
4 participants