mpusz / mp-units

The quantities and units library for C++
https://mpusz.github.io/mp-units/
MIT License
1.09k stars 86 forks source link

`min()`, `max()`, `epsilon()` and others #408

Open mpusz opened 1 year ago

mpusz commented 1 year ago

Consider providing a partial specialization for std::numeric_limits to provide all the numeric properties of underlying representation types. Remove min() and max() from quantity and also remove standalone epsilon() from math.h.

The only problem is what to do with the remaining zero() and one() as those are not provided via std::numeric_limits.

burnpanck commented 1 year ago

I'm not even sure there should be a one() at all. What is it supposed to return? To me, the "one" of an algebra is the identity object of the multiplication group. The set of physical quantities representable by in a mp-units quantity of dimension meter doesn't form an algebra at all (it isn't closed under multiplication:length * (1 meter) isn't a length anymore). Now that I think of it, maybe one() should actually return the dimensionless quantity of 1, in the same representation type?

JohelEGP commented 1 year ago

Now that I think of it, maybe one() should actually return the dimensionless quantity of 1, in the same representation type?

So maybe that should be part of something like a representation_traits instead.

mpusz commented 1 year ago

To me, the "one" of an algebra is the identity object of the multiplication group

Yes, this was the original intent suggested by one of the ISO members. But in practice it turns out that in entire code base we have for V1 one() is being used only in the functions like ceil() and floor():

https://github.com/mpusz/units/blob/49ba4891183e65263ad6cd0f2a8ea334d1f9faa0/src/core/include/units/math.h#L216-L219

mpusz commented 1 year ago

So maybe that should be part of something like a representation_traits instead.

We have something similar already:

https://github.com/mpusz/units/blob/49ba4891183e65263ad6cd0f2a8ea334d1f9faa0/src/core/include/units/customization_points.h#L56-L89

The question is do we want to keep it and if the numeric-like types should provide specializations of std::numeric_limits as a general rule. I asked this question to the chair of SG6 Numerics group but got no specific answer for now. Probably I will have to write a paper about that and other similar questions I have in this domain.

mpusz commented 1 year ago

Just to elaborate a bit more on what I have in mind here:

  1. The physical quantities library should work with any numeric-like representation type.
  2. Right now, there is no official guidance on how those types should look and feel. There is no guideline if they should have value_type or element_type member types, or min(), max() member functions, or if numeric_limits should be specialized for them.
  3. The lack of such guidelines makes it harder for customers of such types as we cannot reason about the underlying type in a generic way. Of course, we can expose our own customization points like quantity_values, but a similar thing will need to be done for linear algebra library, type-safe numerics, fixed precision integers, and so on.... If there was a specific interface mandated by the standard (like, for example, all STD containers have), then it would be much easier to work with those types.
  4. quantity is just yet another numerical type that can be used as an underlying representation type for some other library. So we also should obey the rules of such interfaces. Said that, I believe that, for example, quantity::rep is a really bad name as probably no other numerical type will choose such a member type to expose the type of its underlying value. Maybe, we should use value_type instead. Maybe we should provide specializations of numeric_limits for quantity and expect such specialization for our representation type as well.
burnpanck commented 1 year ago

I agree with this desire. As always, we can only reason about a "good API" if we have a mental model of the concept that we want to implement. You ask for a "numeric-like representation type", but what does it mean? What should it even mean?

I really believe defining this API is outside of the scope of mp-units; we should be basing of concepts defined elsewhere.

As for acceptable types for the representation of a quantity, I believe std::is_arithmetic does the job.

But is a quantity even "just yet another numerical type" itself? What does that mean? It probably is arithmetic, so std::is_arithmetic does the job. That just requires std::numeric_limits to be specialised and the four arithmetic operators to be defined. If we want to model a stronger concept, then we should probably look at real use-cases, and then decide if a physical quantity actually models the concept expected by the use-case, and then we have a reference for the API.

On rep/value_type/member_type: To me, this has nothing to do with a "numeric-like" concept (what does it mean for a "number" to ask for it's "value_type"?). Conceptually, it is the C++ representation that we use to represent the number, which, together with a unit provides a representation for a physical quantity. I would thus like to call it number_repr_type. Having it named member_type on it's own doesn't make it anymore interoperable with other libraries unless it also follows the expected semantics (quantity is still not a container).

You may want to have a look at the compositional numbers library, which composes actual "number-like" types from various aspects. One important feature are fixed-point numbers: a signed 12.4 bit fixed-point number is an arithmetic object that is able to represent certain rational numbers, and shares the bit pattern of a 16 bit signed integer that is semantically interpreted as if counting in "units of 1/16". It also does support more general scaled numbers, i.e. scaling by powers of 10, similar to decimal prefixes in SI. However, contrary to quantity, it still generally models the concept of (rational) numbers, where quantity models "physical quantities".

mpusz commented 1 year ago

As for acceptable types for the representation of a quantity, I believe std::is_arithmetic does the job.

No, it does not. It limits the type only to fundamental types. We already have plenty of examples using linear algebra, `measurement class, or ranged_representation.

quantity does not satisfy std::is_arithmetic as well. Moreover, it is an Undefined Behavior to provide specialization of most type traits from the <type_traits> header for user-defined types.

mpusz commented 1 year ago

You may want to have a look at the compositional numbers library, which composes actual "number-like" types from various aspects.

Yes, I am aware of that and other projects (although I did not study them too much). Yesterday, I asked their authors and SG6 Numerics reflector for feedback, and I am getting the first answers already. We will see if we will be able to standardize some common look and feel here or if we just continue to do our local stuff.