Skip to content

Commit

Permalink
Unit-Safe Implementation of std::hypot (#384)
Browse files Browse the repository at this point in the history
## Summary
Implements a unit-safe version of `std::hypot` within the AU library.
This implementation ensures dimensional consistency when calculating the
hypotenuse/Euclidean norm while preserving the underlying units.

## Implementation Details
- Extends the existing AU library with a unit-safe wrapper around
`std::hypot`
- Maintains dimensional analysis correctness throughout calculations
- Returns results in the common unit and underlying representation as
the input parameters

Related to #383

---------

Co-authored-by: Chip Hogg <[email protected]>
  • Loading branch information
justin-kottinger and chiphogg authored Feb 25, 2025
1 parent 4543859 commit 11fe374
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 0 deletions.
7 changes: 7 additions & 0 deletions au/code/au/math.hh
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ using std::abs;
using std::copysign;
using std::cos;
using std::fmod;
using std::hypot;
using std::isnan;
using std::max;
using std::min;
Expand Down Expand Up @@ -172,6 +173,12 @@ constexpr auto clamp(QuantityPoint<UV, RV> v,
return (v < lo) ? ResultT{lo} : (hi < v) ? ResultT{hi} : ResultT{v};
}

template <typename U1, typename R1, typename U2, typename R2>
auto hypot(Quantity<U1, R1> x, Quantity<U2, R2> y) {
using U = CommonUnitT<U1, U2>;
return make_quantity<U>(std::hypot(x.in(U{}), y.in(U{})));
}

// Copysign where the magnitude has units.
template <typename U, typename R, typename T>
constexpr auto copysign(Quantity<U, R> mag, T sgn) {
Expand Down
29 changes: 29 additions & 0 deletions au/code/au/math_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,35 @@ TEST(clamp, SupportsZeroForMultipleArguments) {
EXPECT_THAT(clamp(feet(6), ZERO, ZERO), SameTypeAndValue(feet(0)));
}

TEST(hypot, QuantityConsistentWithStdHypotWhenTypesAreIdentical) {
auto expect_consistent_with_std_hypot = [](auto u, auto v) {
const auto expected = ohms(std::hypot(u, v));
const auto actual = hypot(ohms(u), ohms(v));
EXPECT_THAT(actual, SameTypeAndValue(expected));
};

// Rep: `int`.
expect_consistent_with_std_hypot(-1, 0);
expect_consistent_with_std_hypot(0, 0);
expect_consistent_with_std_hypot(1, 0);
expect_consistent_with_std_hypot(2, 0);
expect_consistent_with_std_hypot(4, 2);

// Rep: `double`.
expect_consistent_with_std_hypot(-1.0, 0.0);
expect_consistent_with_std_hypot(0.0, 0.0);
expect_consistent_with_std_hypot(1.0, 0.0);
expect_consistent_with_std_hypot(2.0, 0.0);
expect_consistent_with_std_hypot(4.0, 2.0);
}

TEST(hypot, QuantityProducesResultsInCommonUnitOfInputs) {
EXPECT_THAT(hypot(centi(meters)(30), milli(meters)(400)),
SameTypeAndValue(milli(meters)(500.0)));

EXPECT_THAT(hypot(inches(5.f), feet(1.f)), SameTypeAndValue(inches(13.f)));
}

TEST(copysign, ReturnsSameTypesAsStdCopysignForSameUnitInputs) {
auto expect_consistent_with_std_copysign = [](auto mag, auto raw_sgn) {
for (const auto test_sgn : {-1, 0, +1}) {
Expand Down

0 comments on commit 11fe374

Please sign in to comment.