mpusz / mp-units

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

Should units support `<=>`? #490

Closed mpusz closed 1 week ago

mpusz commented 9 months ago

Right now, we only support equality comparisons, but maybe something like the one below makes sense?

if constexpr (q.unit > GHz)
  // ...
if constexpr (q.unit > one)
  // ...
mpusz commented 9 months ago

If we would like to have it, it would probably only make sense for the unit of the same quantity?

Please note that this would be inconsistent with the equality comparison:

static_assert(hour != second);  // Compiles fine
static_assert(hour > second);   // Compiles fine
static_assert(hour != metre);   // Compiles fine
static_assert(hour > metre);    // Compile-time error: trying to compare units of different quantity kinds
uyha commented 8 months ago
static_assert(hour != metre);   // Compiles fine

I'm actually surprised this compiles since it looks nonsensible, quantities having different units should be incomparable, not just different. So I think <=> should be supported but != semantic should be revised.

mpusz commented 8 months ago

I think it has more sense in a generic context when you deal with a function like:

template<Unit U1, Unit U2>
Unit auto divide(U1, U2)
{
  if constexpr(U1{} == U2{})
    return one;
  else
    return U1{} / U2{};
}
uyha commented 8 months ago

I think it's better to have a function to ask if 2 quantity is comparable (concept even) not fusing it with the compilability of the operator== and operator!=

mpusz commented 8 months ago

I see your point. I will make sure it will be discussed in the upcoming ISO C++ Committee meeting in Kona when we will be talking about https://wg21.link/p2982#ordering.

chiphogg commented 8 months ago

I've always felt a little strange about (in)equality comparisons working for hour and meter. I think we're better off with named functions. Especially since there are many different "degrees" of equivalence that we might want to check --- for example, same dimension/magnitude, vs. same kind, vs. same quantity-within-kind.

As for the original question, about supporting <=> with units: I'd say this is a resounding "no". It's really hard to see how we'd implement that in general for vector space magnitudes. I know @JohelEGP has suggested other magnitude implementations, but I think those would have the same difficulty.

JohelEGP commented 8 months ago

A unit of measurement is a reference quantity. ISO 80000-1:2022 6.2 "Units and numerical values" says

If a particular instance of a quantity of a given kind is chosen as a reference quantity called the unit, then any other quantity of the same kind can be expressed in terms of this unit, as a product of this unit and a number. That number is called the numerical value of the quantity expressed in this unit.

Of course, code is different. It's good that the unit is different from a quantity (e.g., base units don't have a number). So it's a question of convenience. Quantities of different kinds aren't comparable. But if there's merit for units, it's worth considering.

IIUC, these are the possible APIs for comparing units:

// Same kind?
if constexpr (same_kind<U1, U2>) { … }
if constexpr (std::totally_ordered<U1, U2>) { … }
if constexpr (U1{} <=> U2{} != std::partial_ordering::unordered) { … }

// Less?
if constexpr (U1{} < U2{}) { … }
if constexpr (std::totally_ordered<U1, U2> && requires { requires U1{} < U2{}; }) { … }
if constexpr (U1{} <=> U2{} == stdd::partial_ordering::less) { … }

U1{} < U2{} always being well-formed would be misleading. If it returns false, it either means U1{} >= U2{} is true, or they're unordered. For correctness, any else branch would also need to test U1{} >= U2{}. IIUC how <=> works.

mpusz commented 8 months ago

Having a kind as a constraint for equality is tricky:

On the other hand, if we ignore the kind, then should Hz and Bq compare equal?

JohelEGP commented 8 months ago

some systems of units do not have associated kinds (e.g. natural units)

IIUC, they're inter-comparable, so they must be of the same kind (https://jcgm.bipm.org/vim/en/1.2.html).

sometimes, the kind may be the same, but units still may not be comparable (see https://github.com/mpusz/mp-units/blob/master/example/currency.cpp)

They would be, by definition, not of the same kind. I'm pretty sure I can compare US$ to €. The code says otherwise for reasons.

On the other hand, if we ignore the kind, then should Hz and Bq compare equal?

If you're literally saying "compare these quantities converted to base units" to explicitly ignore kinds and only rely on dimensions, it should work just fine.

mpusz commented 8 months ago

IIUC, they're inter-comparable, so they must be of the same kind.

The rules you quote are for the ISQ quantities and not units 😉 Often, natural units are about using simpler values by making some constants =1, but still, people would like to know if they deal with length or time, speed or an acceleration. The fact that the unit is the same does not mean that the quantity should be of the same kind. This is why in our example implementation of natural units, they do not have any assigned kind:

https://github.com/mpusz/mp-units/blob/ebc5757835764b78c9b4e9804ac0c95f6237730d/src/systems/natural/include/mp-units/systems/natural/natural.h#L35-L36

I'm pretty sure I can compare US$ to €.

Well, you can compare those, but to so that you have to implement the conversion and comparison by yourself. The library will not help you with that.

it should work just fine.

I have some doubts if that is the correct thing to do. Maybe we should use a kind, but only in case it is available. Otherwise, just compare the units.