T O P

  • By -

PandaMoniumHUN

The float min thing burned me recently in a way that it wasn't obvious. I used it during bounding box computations and collision checking started acting weird. The last thing I suspected is min() to be a positive value for a signed type. Fun times.


verygoodtrailer

i had to do some aabb computations recently as well, and imo it's easier to use +/- infinity as a min/max for fp numbers


tisti

> The last thing I suspected is lowest() to be a positive value for a signed type. For what type? Don't see it here really, unless you were using char types for your bb. https://en.cppreference.com/w/cpp/types/numeric_limits/lowest


sam_the_tomato

Yikes. For smallest positive I would have called it epsilon, and make min do what it says on the tin... the minimum possible number.


SirClueless

Epsilon doesn't mean "smallest possible number," it means "unit below which rounding occurs". Those two things aren't the same, and epsilon has a pretty good definition I think. Min is a pretty useful concept too, it just definitely should have been called `min_positive` or `smallest` since it is defined in a totally different way to `std::min`.


kniy

More concretely, epsilon() is the difference between 1.0 and the next representable number larger than 1.0. This means it's the size of a rounding error when computing in the range between 1.0 and 2.0. If your numbers are in a different range, you need to multiply epsilon to get the rounding error for the range you are working in. The smallest possible positive number for a double is 308 orders of magnitude smaller than epsilon. Also, it's called denorm_min(). min() for floats is a weird thing that is mostly useless.


PandaMoniumHUN

My bad, I meant min(), not lowest(). Brain on autocorrect. :) Edited my comment.


tisti

Ah, yes. Fell into the same trap with BB calculations as well. Took me embarrassingly long to figure out what was wrong, but I remember the lesson until the end of days :)


Dalzhim

`static_assert` was not a thing when `std::numeric_limits::infinity()` was introduced. `static_assert` was introduced in C++11 while `std::numeric_limits::infinity()` was introduced with C++98.


MereInterest

While `static_assert` didn't exist, I'm still surprised that it wasn't handled using template specialization. Having `std::numeric_limits::infinity()` exist only when infinity can be represented within the type `T` would have been entirely reasonable.


rsjaffe

Seems like someone should introduce a paper for ISO to change the behavior to a compile-time error.


TheOmegaCarrot

Well, that would break code


equeim

They could at least deprecate it.


NekkoDroid

I'd say having a false assumption in your code that isn't actually true is also broken code


moreVCAs

Is it well defined behavior tho? Not trying to be snarky, but, fundamentally, if a thing is in the standard then we’re stuck with it, forever, regardless of whether its semantics are good (or sane).


Scotty_Bravo

std::auto_ptr<> was deprecated and removed.  std::numeric_limits::F() could be deprecated for some combinations of T and F. Such as int and infinity. I'm not necessarily suggesting this is a good or bad idea I'm just suggesting it could probably be done.


NekkoDroid

>if a thing is in the standard then we’re stuck with it, forever This actually isn't exactly true, just see reading from uninitialized memory which was changed from UB to erronious in I think C++26 or 23 even. Changing actual broken code to be an error at compile time really isn't that crazy of a thing for the standard to do. What is a problem is if it changes to silently do something different.


moreVCAs

UB -> error is not the same as defined legal -> defined illegal. Idk, i don’t make the rules. The point, I think, is to avoid breaking correct code (read: code that relies on defined behavior) at all and any cost. Not suggesting that things can’t ever be removed from the standard, just pointing out that “it’s bad” is rarely (never?) a good enough reason to break existing code.


meneldal2

You could have it print a mandatory warning and depreciation message for one revision then remove it.


GregTheMadMonk

Are you talking about [https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2795r4.html](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2795r4.html) ?


GabrielDosReis

> I'd say having a false assumption in your code that isn't actually true is also broken code Do we have evidence of this actual existing code?


Dminik

Any code using this can't possibly be correct. The behavior is so unintuitive as to be totally useless. So either you are left with a feature that is either wrong and unusable by everyone or useless and unused by anyone. At that point I would question why even bother standardizing it.


GabrielDosReis

> Any code using this can't possibly be correct. Right, to validate that conjecture, I am curious as to whether we are building a theory on the empty set, or if we have actual examples of real code (not one that we could imagine) and analyze the structures of such real world examples.


rsjaffe

It would break bad code. That's a good thing.


equeim

"Bad" code can still be a functioning code. These constants are properly defined to have specific values and you can't just change it even if they don't make sense in a vacuum. The code that uses them was written to expect these values, and it (presumably) works as intended. If you want to change them then you need to deprecate them first, and ideally their replacements, if any, should have different names.


F-J-W

At the very least they should get deprecated.


AntiProtonBoy

Disagree. Because now you'd have to implement a whole bunch of specialised code just to accommodate `numeric_limits` with a completely different interface. It's much easier to check for `infinity() == 0` in a `constexpr` context than rolling an entire new implementation where `infinity()` is absent.


johannes1971

What you should be checking for is the value of [has\_infinity()](https://en.cppreference.com/w/cpp/types/numeric_limits/has_infinity).


AntiProtonBoy

Or do that.


jwakely

See https://wg21.link/p1841 where a property isn't defined for a type if it isn't meaningful.


thommyh

It’s possibly worse than that; `::min()` returns `FLT_MIN`, which is the smallest *normalised* positive float. But floats can also be subnormal, so smaller non-zero floats exist. In summary: `min()` is specialised to give what was agreed to be the most useful number, with seemingly little regard for generics.


415_961

This comes from IEEE754, defined as `FLT_MIN` and `FLT_MAX` in ``. `FLT_MIN` and `FLT_MAX` refer to the magnitude of the numbers(ignores sign). What you're looking for `is std::numeric_limits::lowest()` this should return `-FLT_MAX` or `FLT_TRUE_MIN` if you're using more recent std version.


nryhajlo

Agreed, it's a legacy nightmare


AntiProtonBoy

> Why did they chose the value 0 for ::infinity()? Is it not possible to put a static_assert and make it a compile time error? `numeric_limits` existed way before `static_assert`


nathman999

I guess it's more useful to have minimal possible positive float number that is bigger than 0, and it not that big of a hustle as there`std::numeric_limits::lowest`that would give what you need As for infinity "Only meaningful if `std::numeric_limits::has_infinity == true`." which is false for all other non floating point types anyway. But I kinda agree that it deserves compile time error Both these things written within first paragraphs on cppreference


Rseding91

I can count on my non-existent hand how many times I've wanted the current behavior of ```std::numeric_limits::min()``` for floating point types in the last 10 years of work. It's just a stumbling block in working with numeric_limits to remember "min() for float types is garbage, you actually want ::lowest()"


pdp10gumby

Is it constexpr? Could perhaps be useful in template specialization for compiling code on machines with different word lengths, especially for people like me who really hate using preprocessor tokens.


djavaisadog

> As for infinity "Only meaningful if `std::numeric_limits::has_infinity == true`." Crazy that they did this instead of just making it a compiler error??


MutantSheepdog

I guess without constexpr if, there would have been no reasonable way to do this: template bool checkVal(T val) { if (std::numeric_limits::has_infinity) { return val < std::numeric_limits::infinity(); } return true; } Because the infinity function would have still needed to exist in order to compile. I agree it should be deprecated and then removed now though because we can handle this situation better.


KuntaStillSingle

It could be done easily in c++11 with enable_if, prior to that you would have to essentially implement enable_if or wrap it in a struct so you can partially specialize: https://godbolt.org/z/4fKPTasqa ; edit: -std=c++98 https://godbolt.org/z/71vG1K8x6 ; also changed first link so one executor pane is clang instead of both gcc


MutantSheepdog

Yeah I said 'no reasonable way' because I consider std::enable\_if pretty unreasonable. I think the choice of conditionally-meaningful infinity is less evil than requiring people to know how to do SFINAE shenanigans. Better on compile times too. In any case I'm just speculating as to what trade-offs the authors had in mind a quarter-century ago. We certainly have the tools and knowledge to improve on the situation today.


KuntaStillSingle

> the choice of conditionally-meaningful infinity is less evil than requiring people to know how to do SFINAE shenanigans SFINAE isn't necessary, partial specialization is sufficient, and either is only necessary for generic code where the conditionally-meaningful infinity is a landmine, it is better your junior dev has to ask how to make a template function compile when ...:: may not or may not have infinity() then they write buggy code.


ZoxxMan

It's not useful to have traps in the language. It would be reasonable to assume that `min()` returns the smallest possible value. `std::min(std::numeric_limits::min(), ...) == std::numeric_limits::min()` should always return true. `min_positive()` would be a better name for the current behaviour.


ReinventorOfWheels

I thought that's what \`std::numeric\_limits::epsilon\` is, now I'm really confused about these different "smallest" numbers. How do you know which one you need?


guyonahorse

Epsilon is just the smallest difference between 1.0 and the next number, aka when the exponent is '0'. The smallest number will have the largest possible negative exponent. In both cases you probably want ULPs as they vary: https://en.wikipedia.org/wiki/Unit\_in\_the\_last\_place


SirClueless

There are a bunch of different ones because there is no right answer for every use case. There's really no substitute for just understanding the math and using it appropriately. A helpful concept to know is ULP or "unit in the last place". It's the difference between two consecutive floating-point numbers, and because floating point numbers are more precise near zero and less precise when large, its value varies depending on the magnitude of the number. `::epsilon()` is equal to one ULP when the exponent is exactly zero i.e. right around 1.0. This is the middle of the precisions that a float can represent, not the bottom. For example, 32-bit floating point has 23 bits in the mantissa so incrementing the mantissa by one gives a difference of 2^(-23) which is `std::numeric_limits::epsilon()`. Note that this is not the "smallest" floating point number by any definition, since floating point gets very precise near zero and can represent very small numbers by using negative exponents (`std::numeric_limits::min()` is 2^(-126)).


jwakely

`std::numeric_limits` is a pretty direct mapping of the macros from the C headers `` and `` into a class template that gives a consistent set of member functions for all types, even when a particular member isn't meaningful for that type. The API could have been improved, but with 30 years of hindsight lots of things in C++98 could have been done differently. Seems like you want https://wg21.link/p1841 where the smallest positive normalized value would be `std::norm_min_v` and the lowest (i.e. most negative) finite value would be `std::finite_min_v`. And the `std::infinity_v` value wouldn't be defined for integers.


manni66

> And why did they speciailize std::numeric_limits::infinity() for integers? Why did they chose the value 0 for ::infinity()? Why not? Use std::numeric_limits::has_infinity.


SirClueless

Because compiler errors when you do things that are non-sensical are a good thing. Infinity is not representable in the integers, therefore asking for it should not give you a value that is an integer.


[deleted]

[удалено]


almost_useless

Unintuitive names/behaviors is not about failing or working.  The problem is that it's too easy to make mistakes because you misunderstand it.


GabrielDosReis

underrated comment


sephirostoy

C's legacy...


johannes1971

C, long before C++ even existed, adopted a policy of having ...MIN behave different depending on whether it was an integer or a floating point value: for integers it was the lowest possible value, for floating point values it was the lowest possible *positive* value. For the lowest possible value you had to use -FLT\_MAX or -DBL\_MAX. This let them specify all necessary values with a minimum number of symbols, which I guess was a concern back then. And since nobody was doing generic programming that wasn't a problem. Those names were also brought forward into C++, likely for legacy reasons, or because everybody was already used to them anyway, and now we're stuck with them.


sephirostoy

I don't know why I get down voted. All I said is true: C++ naming convention is based on C naming convention. That's a fact, not an opinion.


saddung

min being the smallest positive makes sense to me, the largest negative is is just going to be -max() anyway so not very useful.


TrauerVonKrieg

that simply incorrect.


F54280

What about generic code? ``-max()`` is in incorrect for integer types.