Skip to content
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

Comfortable ceil function #385

Open
StefanoD opened this issue Feb 24, 2025 · 3 comments
Open

Comfortable ceil function #385

StefanoD opened this issue Feb 24, 2025 · 3 comments

Comments

@StefanoD
Copy link

Hi,
I would like to have a comfortable ceil function. This has also the advantage that ADL can be used.
Something like this:

template <typename U, typename R>
auto ceil(au::Quantity<U, R> q)
{
    return au::ceil_as(au::QuantityMaker<U>{}, q);
}

This works in most cases, but not when I have a product with au::Percent:

auto mL1 = microliters(12.5);
auto p = percent(110);
// Result is 13.75 uL instead of 14.0 uL
au::QuantityD<au::Micro<au::Liters>> result = Units::ceil(mL1 * p);
EXPECT_EQ(result, microliters(14.0));

My question is, can I somehow prevent such an unexpected result and still have a comportable function like ceil()?

@chiphogg
Copy link
Contributor

Unfortunately, I think you're running into the exact reasons why we didn't provide a bare ceil function without specifying the unit at the callsite.

The principle at work here is that a Quantity<U, R> type should model a quantity, rather than a number. Quantities can be expressed in different units, but the "underlying quantity" is the same: 3 feet is 1 yard is 36 inches. From a design standpoint, this leaves us two kinds of APIs:

  1. APIs that make sense operating on the raw quantity.
  2. APIs that explicitly name the unit at the callsite.

Something like ceil is very much a number-based operation, not a quantity-based one. We want the next-highest integer, but there's no such thing as an "integer quantity", only quantities with integer values when expressed in specific units. If we provided an API that ignored this distinction, and simply applied ceil to the underlying stored value, then we'd run into exactly the problem you discovered: the result is not well-defined independently of the units chosen.

In your example above, you should be able to write ceil_as(microliters, mL1 * p). (Note that ADL should already work here, because mL1 * p is au::Quantity, so you shouldn't need to write au::ceil_as.) If repeating the unit is annoying here, you could also write:

auto result = ceil_as<double>(microliters, mL1 * p);

But if you wanted to write out the full typename, and avoid repeating the unit, then I think you'd be out of luck: the right-hand side (RHS) gets computed without any knowledge of the left-hand side (LHS) it's getting assigned to, so there's no way to get the information from the type in the LHS to affect the computation in the RHS.

Please let me know if you have any further questions here!

@chiphogg
Copy link
Contributor

Also, as a quick aside:

Hi, I would like to have a comfortable ceil function. This has also the advantage that ADL can be used. Something like this:

template <typename U, typename R>
auto ceil(au::Quantity<U, R> q)
{
    return au::ceil_as(au::QuantityMaker<U>{}, q);
}

On the implementation side for this function, I would suggest the following changes:

template <typename U, typename R>
auto ceil(au::Quantity<U, R> q)
{
    return ceil_as(U{}, q);
}

That is:

  • Use ADL for ceil_as.
  • Just pass the unit, instead of constructing an ad hoc quantity maker. Here's our full guidance on how to use unit slots.

@StefanoD
Copy link
Author

Thanks for your reply. I will read the documentation in more detail in the next days.
But in respect to ADL: When I think about ADL, I am thinking about generic template code which works with native number types and Au units. That is, ceil_as() is not ADL in terms of calculation with numbers. I also don't now any other lib which uses this function name and signature. So why are you calling this ADL?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants