julia icon indicating copy to clipboard operation
julia copied to clipboard

Detailed description on floatmin and floatmax

Open udohjeremiah opened this issue 3 years ago • 12 comments
trafficstars

It's intuitive to think that typemin will do the opposite of what typemax does - which it does:

help> typemin
  typemin(T)

  The lowest value representable by the given (real) numeric DataType T.

help?> typemax
  typemax(T)

  The highest value representable by the given (real) numeric DataType.

It's intuitive to think that Base.isless will do the opposite of what Base.isgreater does - which it does not (and the docs spelled it out):

help> isless
  isless(x, y)

  Test whether x is less than y, according to a fixed total order (defined
  together with isequal).

help> Base.isgreater
  isgreater(x, y)

  Not the inverse of isless! Test whether x is greater than y, according to
  a fixed total order compatible with min.

It intuitive to think that floatmin will do the opposite of what floatmax does - which it does not (and the docs didn't spelled it out):

help?> floatmin
  floatmin(T = Float64)

  Return the smallest positive normal number representable by the
  floating-point type T.

help?> floatmax
  floatmax(T = Float64)

  Return the largest finite number representable by the floating-point type
  T.

Both floatmin and floatmax return positive values, which isn't intuitive for anyone first using the function. So its best to spell this out, as have been done for other cases like Base.isless and Base.isgreater.

Moreover, since floatmin doesn't return the "smallest finite number representable by the floating-point type", a link in the see also section should be provided for users pointing to prevfloat, nextfloat, because:

julia> prevfloat(Inf)
1.7976931348623157e308

julia> nextfloat(-Inf)
-1.7976931348623157e308

helps return the largest and smallest finite number representable, which users can easily call other than the one provided for only largest finite numbers in floatmax.

udohjeremiah avatar Sep 17 '22 10:09 udohjeremiah

I'm not a native speaker, but shouldn't it be "converse" rather than "inverse" in any case?

dkarrasch avatar Sep 17 '22 15:09 dkarrasch

"converse" is great but then is it a "frequently-used" mathematical term?

"inverse" for anyone would mean the opposite or turn upside lol. But anyways, as I previously said, I was only following the isless and Base.isgreater format.

Anyone should get the wording right, but at least an info should be provided.

udohjeremiah avatar Sep 17 '22 16:09 udohjeremiah

I was just thinking that in the context of functions, the inverse function is one that returns the argument to a given outcome of the original function. In this context, one might be led to think of a function like "which minimal floating point type do I need to represent a given number?" as the inverse function, so instead of Type -> Number have Number -> Type. Or maybe something related to "complementary", or, in fact, "negation"?

Actually, given that it seems so hard to precisely tell what these functions are not, perhaps we should remove those sentences and, if anything, demonstrate what we mean by an example below?

dkarrasch avatar Sep 17 '22 17:09 dkarrasch

Demonstrating by example is great, but I think it should be an "add-on". I think its a bad way trying to tell what a function does from an example - the docs should do it.

Anyways, like I said earlier, its open to better wordings; but the fact that it should be removed all together would be devastating, because we humans assume a lot.

Anyone would assume isless and isgreater are opposite in concepts no matter how many examples you give. I think looking at it from your angle, maybe a !!! note should be provided instead, so the wordings can be placed there.

udohjeremiah avatar Sep 17 '22 19:09 udohjeremiah

I would say the relevant meaning of "inverse" would be this, which is close but not quite right:

julia> inv(floatmax(Float32))
2.938736f-39

julia> floatmin(Float32)
1.1754944f-38

julia> nextfloat(0f0)
1.0f-45

If a negative explanation is needed, why not just state that typemin(T) will be ~~a large negative number~~ -Inf for any AbstractFloat type.

mcabbott avatar Sep 19 '22 00:09 mcabbott

It seems the "inverse" is taking the main point from this PR.

The PR was initially opened to address the issue that the floatmin function should somehow indicate that it's not doing the opposite of floatmax, just like the Base.isgreater was stated as not necessarily doing the opposite of isless.

help?> Base.isgreater
  isgreater(x, y)

  Not the inverse of isless!

The word "inverse" was used as the same format was used on isless and Base.isgreater relationship. Maybe its not the best word, and "opposite" should be the best word I guess; but then we should change the word "inverse" to be "opposite" in Base.isgreater as well?

What do you think?

udohjeremiah avatar Sep 19 '22 05:09 udohjeremiah

isgreater takes two arguments. I presume "inverse" there means you reverse them. That may not be an ideal word but it is clarified extensively just below. And is irrelevant to this PR.

PR was initially opened to address the issue that the floatmin function should somehow indicate that it's not doing the opposite of floatmax

But it is in some sense the opposite. Largest & smallest full-precision numbers. There are several other ways you could guess the wrong sense of "opposite" from the name, and get a less useful function. Picking one of these to make the very first sentence doesn't sound like the path to clarity.

julia> let T = Float32
         x = floatmax(T)
         @show eps(x)/x
         y = floatmin(T)
         @show eps(y)/y
         eps(one(T))
       end
eps(x) / x = 5.960465f-8
eps(y) / y = 1.1920929f-7  # all 3 same precision, to the precision of my demonstration
1.1920929f-7

julia> let T = Float32
         x = nextfloat(zero(T))
         @show eps(x)/x
       end;
eps(x) / x = 1.0f0

mcabbott avatar Sep 19 '22 06:09 mcabbott

@mcabbott your example above is very great, but you're an "insider". I think, for anyone not fully equipped with Julia, it assumes floatmin does the opposite of what floatmax does - I fell into this trap until I read the docs carefully.

floatmax does this:

Return the largest finite number representable by the floating-point type T.

So why would anyone not believe floatmin does this?

Return the smallest finite number representable by the floating-point type T.

But it doesn't and that's what I'm trying to address. Point a way in the docs to let the user know it's not; just as isless and Base.isgreater was.

To see my point, check out some of the examples for typemax:

help?> typemax

  julia> typemax(Float64)
  Inf
  
  julia> floatmax(Float32)  # largest finite floating point number
  3.4028235f38

Why was floatmax introduced there with a meaningful comment? SIMPLE: to point users who didn't see what they were looking for with typemax to use floatmax. As for typemin, this isn't available. I've opened #46827 on that though.

But the point is clear:

  • give users "little hint" on functions with possible "name collisions" e.g. floatmin and floatmax, where the behaviour isn't what is intuitively expected;
  • functions with names where we intuitively expect it to return "some-kind-of" value based on its name, e.g typemax would be expected to do what floatmax does, but it doesn't based on special values, and the "little hint" was provided - which is great.

udohjeremiah avatar Sep 19 '22 09:09 udohjeremiah

For what is worth, I've been using Julia for more than 6 years and I never came across the Base.isgreater function, which, as far as I can tell from https://docs.julialang.org/en/v1/search/?q=isgreater, isn't even part of the public API. So maybe that isn't the best example to look at.

giordano avatar Sep 19 '22 09:09 giordano

"I never came come across it" you said. Do you think its great if we generalise your experience for everyone?

I use that A LOT, particularly for it behaviour with NaN and missing. I'm aware it's not part of the public API and its no issue.

udohjeremiah avatar Sep 19 '22 09:09 udohjeremiah

Do you have another example to consider?

udohjeremiah avatar Sep 19 '22 09:09 udohjeremiah

Return the smallest finite number representable by the floating-point type T.

But it doesn't

But it does! Here "smallest" means smallest in absolute value, large negative numbers are not small.

This is pretty standard, but again, mis-reading this as allowing large negative is one possible mis-reading, which the second paragraph can aim to warn you against, without assuming that you mis-read it exactly this way.

mcabbott avatar Sep 19 '22 11:09 mcabbott

Moreover I changed the "largest" in floatmax to say "highest" so its consistent with typemax and typemin:

help?> typemin

  typemin(T)

  The lowest value representable by the given (real) numeric DataType T.

help?> typemax
search: typemax typemin

  typemax(T)

  The highest value representable by the given (real) numeric DataType T.

udohjeremiah avatar Oct 06 '22 06:10 udohjeremiah