-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
Add nextfloat(::BigFloat, n), prevfloat(::BigFloat,n) #31310
Add nextfloat(::BigFloat, n), prevfloat(::BigFloat,n) #31310
Conversation
de29df0
to
b0a5ccf
Compare
base/mpfr.jl
Outdated
end | ||
|
||
nextfloat(x::BigFloat, n::Int) = nextfloat!(BigFloat(x, new=true), n) |
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.
Technically when n == 0
we could just return x
. Not sure it's worth the complexity though.
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.
Actually, the special case n==0
is already considered in nextfloat!()
. Note that nextfloat()
had to return a copy of x, so anyhow we would need to call Bigfloat constructor. Thus no special case was specifically created for this 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.
Actually, the special case
n==0
is already considered innextfloat!()
But that's too late: BigFloat(x, new=true)
has already allocated a new copy of x
by the time nextfloat!
gets the value.
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.
My argument is that, nextfloat() needs to return a new copy of x, as it does not ends with !
. So even in case n==0, we would need to create a copy of x. Thus, no computation is wasted.
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.
My argument is that,
nextfloat()
needs to return a new copy ofx
Returning the same value is not a mutating operation. Since BigFloats are considered to be immutable it's fine to return the same value instead of allocating a new identical value.
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.
Does this look good?
nextfloat(x::BigFloat, n::Int) = n == 0 ? x : nextfloat!(BigFloat(x, new=true), n)
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, that's just it.
This is looking fairly good. There's a key API question in here: do we want |
We had a long discussion on what the API of the BigFloat should look like here. I believe the summary of the whole discussion can be found here. @JeffreySarnoff can elaborate more. What are your views on it? |
That's fine, I'm calling broader attention to it to get some agreement before merging. |
base/mpfr.jl
Outdated
return z | ||
end | ||
function BigFloat(x::BigFloat, r::MPFRRoundingMode=ROUNDING_MODE[]; | ||
precision::Integer=MPFR.precision(x), new::Bool=false) |
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.
precision::Integer=MPFR.precision(x), new::Bool=false) | |
precision::Integer=MPFR.precision(x), new::Bool=precision!=MPFR.precision(x)) |
base/mpfr.jl
Outdated
end | ||
function BigFloat(x::BigFloat, r::MPFRRoundingMode=ROUNDING_MODE[]; | ||
precision::Integer=MPFR.precision(x), new::Bool=false) | ||
precision == MPFR.precision(x) && !new && return 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.
precision == MPFR.precision(x) && !new && return x | |
new || return 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.
Does this sound good?
precision != MPFR.precision(x) && !new &&
@warn "new=false is ignored because precision differ" &&
return BigFloat(x, precision=precision)
new || return 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.
We definitely don't want random warnings in unexpected corner cases. If you called BigFloat(x)
and didn't ask for a definitely new value it should be fine to either give you a new BigFloat
or to give you the same one since the official API for BigFloats is that they are immutable, so you shouldn't be able to tell the difference anyway.
The only legitimate use case for BigFloat(x, new=true)
is when you're copying an input from somewhere and then are going to mutate that input in the internal implementation of an algorithm. That's why I'm not convinced about this API: if you wanted a new copy of an incoming value so that you can mutate it, wouldn't you also want to copy the precision?
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.
The precision is being copied. In the constructor, default value of precision is precision(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.
Ok, then with @simonbyrne's new default value I'm more ok with this.
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.
We should decide about using new
as a parameter first. imo my prior note gives a way to determine that which strips away some of the dross.
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 this does what we want and avoids the controversial.
defined in mpfr and not exported, these respect the precision of x
function duplicate(x::BigFloat)
z = BigFloat(; precision = precision(x))
ccall((:mpfr_set, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32), z, x, 0)
return z
end
function nextfloat!(x::BigFloat)
ccall((:mpfr_nextabove, :libmpfr), Int32, (Ref{BigFloat},), x) != 0
return x
end
function prevfloat!(x::BigFloat)
ccall((:mpfr_nextbelow, :libmpfr), Int32, (Ref{BigFloat},), x) != 0
return x
end
defined in mpfr.jl and exported, these also respect the precision of x
without influencing any other aspects of the BigFloat
interface.
nextfloat(x::BigFloat) = nextfloat!(duplicate(x))
prevfloat(x::BigFloat) = prevfloat!(duplicate(x))
nextfloat(x,n), prevfloat(x,n), nextfloat!(x,n), prevfloat!(x,n)
accordingly
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.
^ revised so nextfloat(x) != 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.
+:100: to the non-controversial approach of introducing an unexported duplicate
function for this PR. That let's us get this PR merged adding the desired nextfloat
and prevfloat
methods and we can have a separate discussion of the best API for creating copies.
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.
@narendrakpatel please ^, do as we discussed last night.
Don't worry about the failing tests now -- a closer look shows your PR does not trigger the fails.
We took a brief look at this during triage. Two comments: We don't like the |
@Keno If we implement the function _duplicate(x::BigFloat)
z = BigFloat(;precision=precision(x))
ccall((:mpfr_set, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode),
z, x, ROUNDING_MODE[])
return z
end Basically only change in proposed implementation would be the use of |
@narendrakpatel Because there is now exported That change would be "breaking" -- so it is unavailable until Julia v2. All of the good thought and refinement that we have developed over the past days will be useful. Either as contributing to the "reform" of BigFloats for v2, or in providing important principles for a complementary package, say I know others are paying attention to what is and is not "breaking". It's most important to the viability of Julia as the user base grows. My perspective is more about where we might be going, or how I might go where I like with Julia. So .. given this, why not take a shot at cleanly coding the "works like repeated calls to _" versions of these functions for |
Is that ^ where we are at, @Keno? |
I'm just the scribe here ;). If |
As far as I know, nobody uses There is a good deal of support for the PR as @narendrahkpatel is about to provide. |
b0a5ccf
to
b1e5e41
Compare
Until v1.1, there was no That (a) there is no pattern of use for That |
@StefanKarpinski @Keno Does this PR require any other changes? |
base/mpfr.jl
Outdated
ccall((:mpfr_nextabove, :libmpfr), Int32, (Ref{BigFloat},), z) != 0 | ||
return z | ||
function nextfloat!(x::BigFloat, n::Integer) | ||
n==0 && return 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.
For style consistency, it seems better to add space around ==
(same for prevfloat!
).
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.
But actually, this line seems unnecessary, the same result is achieved by an empty for
-loop below.
base/mpfr.jl
Outdated
nextfloat(x::BigFloat, n::Integer) = n==0 ? x : nextfloat!(_duplicate(x), n) | ||
prevfloat(x::BigFloat, n::Integer) = n==0 ? x : prevfloat!(_duplicate(x), n) | ||
|
||
nextfloat!(x::BigFloat) = (ccall((:mpfr_nextabove, :libmpfr), Int32, (Ref{BigFloat},), x); 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.
Why not instead add a default argument, like nextfloat!(x::BigFloat, n::Integer=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.
I think that does not decomplexify; and since nextfloat!
is being introduced and not exported .. keeping it as simple as possible lets the exported function be more reliably maintained.
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.
Well, I proposed this change precisely because that seems simpler to me, since default args are a well understood feature, and this allows to merge two definitions into one, with easier maintainability IMHO.
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 missed some of your earlier comment. You are talking about giving n
the default value of 1
. I'd have no objection to that in any of the four functions: nextfloat!
, prevfloat!
, nextfloat
, prevfloat
were it not the case that float.jl
does not give nextfloat
, prevfloat
default values. I'm sure that's at least in part because float.jl
was written eariler. Nonetheless, there is a proclivity to follow the conventions that are in use.
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.
float.jl does not give nextfloat, prevfloat default values.
My guess would be that it's because those functions are documented separately. By the way, the docstring of nextfloat(x, n)
should be updated here to include BigFloat
(or simply to remove the type constraint IEEEFloat
, which is not even exported or documented).
proclivity
nice new word learnt, thanks!
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.
We can do the same thing here
We can also do it differently, this is what default arguments are for.
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 it a breaking change -- what is the rounding now?
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.
Currently, we can do setrounding(T, Mode)
and the nexfloat()
uses that Mode. What is implemented in this PR is RoundNearest Mode.
I would prefer the method mentioned by @rfourquet but would not like to break the code style.
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, however when there is no setrounding
set, the default is RoundNearest
. So its not clear to me that using RoundNearest
is breaking anything. If setrounding
sets rounding to RoundDown
or to RoundUp
what difference does it make -- the nextfloat is the same as the nextfloat rounded down and the same as the nextfloat rounded up.
Seems to me that by definition nextfloat(x) != x
for finite(x) and similarly for prevfloat
.. if they cannot be equal and presuming that nextfloat(prevfloat(x)) == x
then it is RoundNearest whatever you call 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.
I would prefer the method mentioned by @rfourquet but would not like to break the code style.
I wonder why you would think that this would break code style. It would rather be your current version which breaks style. If there is no speed advantage (which I doubt in this case) to having two separate bodies with repetition, just merge it into one function with default argument.
This new implementation of |
We have reached a meeting of appropriateness (absence of dissent, presence of consent), right? right. @narendrakpatel has been diligent in his incorporation of the best recentest both in test and in src. Let's clear this PR into Base. |
I wish some more tests and documentation like we suggested, and ideally an answer to my comments. But more importantly, regarding the breaking change, an explicit decision should be taken... |
@narendrakpatel I thought you had added those tests already -- please do. And the docs modification too. |
See my previous post, the current behavior of |
This looks good to go aside from needing NEWS 😀 |
supergood -- getting all newsy |
c32800d
to
9db7274
Compare
There is a typo and an inoperative link in the News item:
(reworded to conform to the style of related News items)
Be careful to copy the full link into News: in html it looks like this: @narendrakpatel please make correction and then notify me |
9db7274
to
d43ea31
Compare
@narendrakpatel On behalf of the other participants, thank you for this awesome first PR. |
If this is okay, can this be merged? @StefanKarpinski @simonbyrne |
NEWS.md
Outdated
* `inv(::Missing)` has now been added and returns `missing` ([#31408]). | ||
* `inv(::Missing)` has now been added and returns `missing` ([#31451]). | ||
|
||
* `nextfloat(::BigFloat, n::Int)` and `prevfloat(::BigFloat, n::Int)` have now been added |
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.
nitpick, if you happen to re-push before merge: n::Integer
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.
* `nextfloat(::BigFloat, n::Int)` and `prevfloat(::BigFloat, n::Int)` have now been added | |
* `nextfloat(::BigFloat, n::Integer)` and `prevfloat(::BigFloat, n:: Integer)` methods have been added |
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.
@narendrakpatel is in class and will push this modification when he gets home.
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.
or -- just go ahead and make the change -- we all agree
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 am back. I will make the changes right away.
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.
do you know how to "squash"? I don't.
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.
For now, I will just use git commit --amend
but you can squash commit in the following way:
git rebase -i HEAD~n
- replace
pick
withsquash
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 don't have to do anything. It can be squashed by the person doing the merge.
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.
(But yes, it can be done by you as well with the appropriate git incantations.)
Looks good modulo doc suggestions. Please squash when merging. |
d43ea31
to
899d69d
Compare
@StefanKarpinski I made all the changes that are recommended. |
NEWS.md
Outdated
@@ -49,6 +52,9 @@ Standard library changes | |||
* A no-argument construct to `Ptr{T}` has been added which constructs a null pointer ([#30919]) | |||
* `strip` now accepts a function argument in the same manner as `lstrip` and `rstrip` ([#31211]) | |||
* `mktempdir` now accepts a `prefix` keyword argument to customize the file name ([#31230], [#22922]) | |||
* `nextfloat(::BigFloat)` and `prevfloat(::BigFloat)` now returns a value with the same precision | |||
as their argument, which means that (in particular) `nextfloat(prevfloat(x)) == x` whereas |
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 their argument, which means that (in particular) `nextfloat(prevfloat(x)) == x` whereas | |
as their argument, which means that (in particular) `nextfloat(prevfloat(x)) == x` whereas |
NEWS.md
Outdated
@@ -49,6 +52,9 @@ Standard library changes | |||
* A no-argument construct to `Ptr{T}` has been added which constructs a null pointer ([#30919]) | |||
* `strip` now accepts a function argument in the same manner as `lstrip` and `rstrip` ([#31211]) | |||
* `mktempdir` now accepts a `prefix` keyword argument to customize the file name ([#31230], [#22922]) | |||
* `nextfloat(::BigFloat)` and `prevfloat(::BigFloat)` now returns a value with the same precision | |||
as their argument, which means that (in particular) `nextfloat(prevfloat(x)) == x` whereas | |||
previously this could result in a completely different value with a different precision ([#31310]) |
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.
previously this could result in a completely different value with a different precision ([#31310]) | |
previously this could result in a completely different value with a different precision ([#31310]) |
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.
It is just a markdown thing. I had build
the docs and checked that it looks fine. Do you still want me to push to these changes?
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.
Probably not worth it, although it does match the way the rest of the file is formatted. You can also just merge the suggested changes through the GitHub UI.
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.
Changed the files.
Ok, we're converging here. Now it's just the rebase artifact and markdown indentation. |
899d69d
to
2d053f9
Compare
This is ready to be squash-merged as soon as tests pass. Great work, @narendrakpatel—thanks for sticking it out to the end! |
If this is okay, can this be merged? @StefanKarpinski |
Exciting! Congrats on the PR, @narendrakpatel! |
Closes: #31276