diff --git a/au/math.hh b/au/math.hh index feac9cb5..e9f333d3 100644 --- a/au/math.hh +++ b/au/math.hh @@ -110,6 +110,9 @@ struct RoundingRep, RoundingUnits> { static_assert(std::is_same::value, ""); static_assert(std::is_same::value, ""); }; +template +struct RoundingRep, RoundingUnits> + : RoundingRep, RoundingUnits> {}; } // namespace detail // The absolute value of a Quantity. @@ -414,129 +417,211 @@ auto remainder(Quantity q1, Quantity q2) { } // -// Round the value of this Quantity to the nearest integer in the given units. +// Round the value of this Quantity or QuantityPoint to the nearest integer in the given units. // // This is the "Unit-only" format (i.e., `round_in(rounding_units, q)`). // +// a) Version for Quantity. template auto round_in(RoundingUnits rounding_units, Quantity q) { using OurRoundingRep = detail::RoundingRepT, RoundingUnits>; return std::round(q.template in(rounding_units)); } +// b) Version for QuantityPoint. +template +auto round_in(RoundingUnits rounding_units, QuantityPoint p) { + using OurRoundingRep = detail::RoundingRepT, RoundingUnits>; + return std::round(p.template in(rounding_units)); +} // -// Round the value of this Quantity to the nearest integer in the given units, returning OutputRep. +// Round the value of this Quantity or QuantityPoint to the nearest integer in the given units, +// returning OutputRep. // // This is the "Explicit-Rep" format (e.g., `round_in(rounding_units, q)`). // +// a) Version for Quantity. template auto round_in(RoundingUnits rounding_units, Quantity q) { return static_cast(round_in(rounding_units, q)); } +// b) Version for QuantityPoint. +template +auto round_in(RoundingUnits rounding_units, QuantityPoint p) { + return static_cast(round_in(rounding_units, p)); +} // -// The integral-valued Quantity, in this unit, nearest to the input. +// The integral-valued Quantity or QuantityPoint, in this unit, nearest to the input. // // This is the "Unit-only" format (i.e., `round_as(rounding_units, q)`). // +// a) Version for Quantity. template auto round_as(RoundingUnits rounding_units, Quantity q) { return make_quantity>(round_in(rounding_units, q)); } +// b) Version for QuantityPoint. +template +auto round_as(RoundingUnits rounding_units, QuantityPoint p) { + return make_quantity_point>( + round_in(rounding_units, p)); +} // -// The integral-valued Quantity, in this unit, nearest to the input, using the specified OutputRep. +// The integral-valued Quantity or QuantityPoint, in this unit, nearest to the input, using the +// specified OutputRep. // // This is the "Explicit-Rep" format (e.g., `round_as(rounding_units, q)`). // +// a) Version for Quantity. template auto round_as(RoundingUnits rounding_units, Quantity q) { return make_quantity>(round_in(rounding_units, q)); } +// b) Version for QuantityPoint. +template +auto round_as(RoundingUnits rounding_units, QuantityPoint p) { + return make_quantity_point>( + round_in(rounding_units, p)); +} // // Return the largest integral value in `rounding_units` which is not greater than `q`. // // This is the "Unit-only" format (i.e., `floor_in(rounding_units, q)`). // +// a) Version for Quantity. template auto floor_in(RoundingUnits rounding_units, Quantity q) { using OurRoundingRep = detail::RoundingRepT, RoundingUnits>; return std::floor(q.template in(rounding_units)); } +// b) Version for QuantityPoint. +template +auto floor_in(RoundingUnits rounding_units, QuantityPoint p) { + using OurRoundingRep = detail::RoundingRepT, RoundingUnits>; + return std::floor(p.template in(rounding_units)); +} // // Return `OutputRep` with largest integral value in `rounding_units` which is not greater than `q`. // // This is the "Explicit-Rep" format (e.g., `floor_in(rounding_units, q)`). // +// a) Version for Quantity. template auto floor_in(RoundingUnits rounding_units, Quantity q) { return static_cast(floor_in(rounding_units, q)); } +// b) Version for QuantityPoint. +template +auto floor_in(RoundingUnits rounding_units, QuantityPoint p) { + return static_cast(floor_in(rounding_units, p)); +} // -// The largest integral-valued Quantity, in this unit, not greater than the input. +// The largest integral-valued Quantity or QuantityPoint, in this unit, not greater than the input. // // This is the "Unit-only" format (i.e., `floor_as(rounding_units, q)`). // +// a) Version for Quantity. template auto floor_as(RoundingUnits rounding_units, Quantity q) { return make_quantity>(floor_in(rounding_units, q)); } +// b) Version for QuantityPoint. +template +auto floor_as(RoundingUnits rounding_units, QuantityPoint p) { + return make_quantity_point>( + floor_in(rounding_units, p)); +} // -// The largest integral-valued Quantity, in this unit, not greater than the input, using the -// specified `OutputRep`. +// The largest integral-valued Quantity or QuantityPoint, in this unit, not greater than the input, +// using the specified `OutputRep`. // // This is the "Explicit-Rep" format (e.g., `floor_as(rounding_units, q)`). // +// a) Version for Quantity. template auto floor_as(RoundingUnits rounding_units, Quantity q) { return make_quantity>(floor_in(rounding_units, q)); } +// b) Version for QuantityPoint. +template +auto floor_as(RoundingUnits rounding_units, QuantityPoint p) { + return make_quantity_point>( + floor_in(rounding_units, p)); +} // // Return the smallest integral value in `rounding_units` which is not less than `q`. // // This is the "Unit-only" format (i.e., `ceil_in(rounding_units, q)`). // +// a) Version for Quantity. template auto ceil_in(RoundingUnits rounding_units, Quantity q) { using OurRoundingRep = detail::RoundingRepT, RoundingUnits>; return std::ceil(q.template in(rounding_units)); } +// b) Version for QuantityPoint. +template +auto ceil_in(RoundingUnits rounding_units, QuantityPoint p) { + using OurRoundingRep = detail::RoundingRepT, RoundingUnits>; + return std::ceil(p.template in(rounding_units)); +} // // Return the smallest integral value in `rounding_units` which is not less than `q`. // // This is the "Explicit-Rep" format (e.g., `ceil_in(rounding_units, q)`). // +// a) Version for Quantity. template auto ceil_in(RoundingUnits rounding_units, Quantity q) { return static_cast(ceil_in(rounding_units, q)); } +// b) Version for QuantityPoint. +template +auto ceil_in(RoundingUnits rounding_units, QuantityPoint p) { + return static_cast(ceil_in(rounding_units, p)); +} // -// The smallest integral-valued Quantity, in this unit, not less than the input. +// The smallest integral-valued Quantity or QuantityPoint, in this unit, not less than the input. // // This is the "Unit-only" format (i.e., `ceil_as(rounding_units, q)`). // +// a) Version for Quantity. template auto ceil_as(RoundingUnits rounding_units, Quantity q) { return make_quantity>(ceil_in(rounding_units, q)); } +// b) Version for QuantityPoint. +template +auto ceil_as(RoundingUnits rounding_units, QuantityPoint p) { + return make_quantity_point>(ceil_in(rounding_units, p)); +} // -// The smallest integral-valued Quantity, in this unit, not less than the input, using the specified -// `OutputRep`. +// The smallest integral-valued Quantity or QuantityPoint, in this unit, not less than the input, +// using the specified `OutputRep`. // // This is the "Explicit-Rep" format (e.g., `ceil_as(rounding_units, q)`). // +// a) Version for Quantity. template auto ceil_as(RoundingUnits rounding_units, Quantity q) { return make_quantity>(ceil_in(rounding_units, q)); } +// b) Version for QuantityPoint. +template +auto ceil_as(RoundingUnits rounding_units, QuantityPoint p) { + return make_quantity_point>( + ceil_in(rounding_units, p)); +} // Wrapper for std::sin() which accepts a strongly typed angle quantity. template diff --git a/au/math_test.cc b/au/math_test.cc index 7a8dd2da..d56e75ce 100644 --- a/au/math_test.cc +++ b/au/math_test.cc @@ -762,8 +762,19 @@ TEST(RoundAs, SameAsStdRoundForSameUnits) { EXPECT_THAT(round_as(meters, meters(3.14f)), SameTypeAndValue(meters(std::round(3.14f)))); EXPECT_THAT(round_as(meters, meters(3.14L)), SameTypeAndValue(meters(std::round(3.14L)))); + EXPECT_THAT(round_as(meters_pt, meters_pt(3)), SameTypeAndValue(meters_pt(std::round(3)))); + EXPECT_THAT(round_as(meters_pt, meters_pt(3.14)), + SameTypeAndValue(meters_pt(std::round(3.14)))); + EXPECT_THAT(round_as(meters_pt, meters_pt(3.14f)), + SameTypeAndValue(meters_pt(std::round(3.14f)))); + EXPECT_THAT(round_as(meters_pt, meters_pt(3.14L)), + SameTypeAndValue(meters_pt(std::round(3.14L)))); + EXPECT_THAT(round_as(meters, meters(INTEGER_TOO_BIG_FOR_DOUBLE)), SameTypeAndValue(meters(std::round(INTEGER_TOO_BIG_FOR_DOUBLE)))); + + EXPECT_THAT(round_as(meters_pt, meters_pt(INTEGER_TOO_BIG_FOR_DOUBLE)), + SameTypeAndValue(meters_pt(std::round(INTEGER_TOO_BIG_FOR_DOUBLE)))); } TEST(RoundAs, RoundsAsExpectedForDifferentUnits) { @@ -771,6 +782,14 @@ TEST(RoundAs, RoundsAsExpectedForDifferentUnits) { EXPECT_THAT(round_as(kilo(meters), meters(999.9)), SameTypeAndValue(kilo(meters)(1.0))); EXPECT_THAT(round_as(kilo(meters), meters(999.9f)), SameTypeAndValue(kilo(meters)(1.0f))); EXPECT_THAT(round_as(kilo(meters), meters(999.9L)), SameTypeAndValue(kilo(meters)(1.0L))); + + EXPECT_THAT(round_as(kilo(meters_pt), meters_pt(999)), SameTypeAndValue(kilo(meters_pt)(1.0))); + EXPECT_THAT(round_as(kilo(meters_pt), meters_pt(999.9)), + SameTypeAndValue(kilo(meters_pt)(1.0))); + EXPECT_THAT(round_as(kilo(meters_pt), meters_pt(999.9f)), + SameTypeAndValue(kilo(meters_pt)(1.0f))); + EXPECT_THAT(round_as(kilo(meters_pt), meters_pt(999.9L)), + SameTypeAndValue(kilo(meters_pt)(1.0L))); } TEST(RoundAs, SupportsDifferentOutputTypes) { @@ -781,6 +800,30 @@ TEST(RoundAs, SupportsDifferentOutputTypes) { EXPECT_THAT(round_as(kilo(meters), meters(999.9f)), SameTypeAndValue(kilo(meters)(1.0))); + + EXPECT_THAT(round_as(meters_pt, meters_pt(3)), + SameTypeAndValue(meters_pt(static_cast(std::round(3))))); + EXPECT_THAT(round_as(meters_pt, meters_pt(3.9)), + SameTypeAndValue(meters_pt(static_cast(std::round(3.9))))); + + EXPECT_THAT(round_as(kilo(meters_pt), meters_pt(999.9f)), + SameTypeAndValue(kilo(meters_pt)(1.0))); +} + +TEST(RoundAs, SupportsQuantityPointWithNontrivialOffset) { + EXPECT_THAT(round_as(kelvins_pt, celsius_pt(20.0f)), SameTypeAndValue(kelvins_pt(293.0f))); + EXPECT_THAT(round_as(kelvins_pt, celsius_pt(20.5f)), SameTypeAndValue(kelvins_pt(294.0f))); + + // Each degree Fahrenheit is 5/9 of a degree Celsius. Thus, moving away from an exact + // correspondence by one degree Fahrenheit will be enough to move to the next integer Celsius + // when we round, but moving by half a degree will not. + EXPECT_THAT(round_as(celsius_pt, fahrenheit_pt(31.0)), SameTypeAndValue(celsius_pt(-1))); + EXPECT_THAT(round_as(celsius_pt, fahrenheit_pt(31.5)), SameTypeAndValue(celsius_pt(0))); + + EXPECT_THAT(round_as(celsius_pt, fahrenheit_pt(32.0)), SameTypeAndValue(celsius_pt(0))); + + EXPECT_THAT(round_as(celsius_pt, fahrenheit_pt(32.5)), SameTypeAndValue(celsius_pt(0))); + EXPECT_THAT(round_as(celsius_pt, fahrenheit_pt(33.0)), SameTypeAndValue(celsius_pt(1))); } TEST(RoundIn, SameAsRoundAs) { @@ -788,10 +831,16 @@ TEST(RoundIn, SameAsRoundAs) { EXPECT_THAT(round_in(kilo(meters), meters(754.28)), SameTypeAndValue(1.0)); EXPECT_THAT(round_in(kilo(meters), meters(754.28f)), SameTypeAndValue(1.0f)); EXPECT_THAT(round_in(kilo(meters), meters(754.28L)), SameTypeAndValue(1.0L)); + + EXPECT_THAT(round_in(kilo(meters_pt), meters_pt(754)), SameTypeAndValue(1.0)); + EXPECT_THAT(round_in(kilo(meters_pt), meters_pt(754.28)), SameTypeAndValue(1.0)); + EXPECT_THAT(round_in(kilo(meters_pt), meters_pt(754.28f)), SameTypeAndValue(1.0f)); + EXPECT_THAT(round_in(kilo(meters_pt), meters_pt(754.28L)), SameTypeAndValue(1.0L)); } TEST(RoundIn, SupportsDifferentOutputTypes) { EXPECT_THAT(round_in(kilo(meters), meters(754.28f)), SameTypeAndValue(1.0L)); + EXPECT_THAT(round_in(kilo(meters_pt), meters_pt(754.28f)), SameTypeAndValue(1.0L)); } TEST(FloorAs, SameAsStdFloorForSameUnits) { @@ -800,8 +849,19 @@ TEST(FloorAs, SameAsStdFloorForSameUnits) { EXPECT_THAT(floor_as(meters, meters(3.14f)), SameTypeAndValue(meters(std::floor(3.14f)))); EXPECT_THAT(floor_as(meters, meters(3.14L)), SameTypeAndValue(meters(std::floor(3.14L)))); + EXPECT_THAT(floor_as(meters_pt, meters_pt(3)), SameTypeAndValue(meters_pt(std::floor(3)))); + EXPECT_THAT(floor_as(meters_pt, meters_pt(3.14)), + SameTypeAndValue(meters_pt(std::floor(3.14)))); + EXPECT_THAT(floor_as(meters_pt, meters_pt(3.14f)), + SameTypeAndValue(meters_pt(std::floor(3.14f)))); + EXPECT_THAT(floor_as(meters_pt, meters_pt(3.14L)), + SameTypeAndValue(meters_pt(std::floor(3.14L)))); + EXPECT_THAT(floor_as(meters, meters(INTEGER_TOO_BIG_FOR_DOUBLE)), SameTypeAndValue(meters(std::floor(INTEGER_TOO_BIG_FOR_DOUBLE)))); + + EXPECT_THAT(floor_as(meters_pt, meters_pt(INTEGER_TOO_BIG_FOR_DOUBLE)), + SameTypeAndValue(meters_pt(std::floor(INTEGER_TOO_BIG_FOR_DOUBLE)))); } TEST(FloorAs, RoundsDownAsExpectedForDifferentUnits) { @@ -810,10 +870,26 @@ TEST(FloorAs, RoundsDownAsExpectedForDifferentUnits) { EXPECT_THAT(floor_as(kilo(meters), meters(999.9f)), SameTypeAndValue(kilo(meters)(0.0f))); EXPECT_THAT(floor_as(kilo(meters), meters(999.9L)), SameTypeAndValue(kilo(meters)(0.0L))); + EXPECT_THAT(floor_as(kilo(meters_pt), meters_pt(999)), SameTypeAndValue(kilo(meters_pt)(0.0))); + EXPECT_THAT(floor_as(kilo(meters_pt), meters_pt(999.9)), + SameTypeAndValue(kilo(meters_pt)(0.0))); + EXPECT_THAT(floor_as(kilo(meters_pt), meters_pt(999.9f)), + SameTypeAndValue(kilo(meters_pt)(0.0f))); + EXPECT_THAT(floor_as(kilo(meters_pt), meters_pt(999.9L)), + SameTypeAndValue(kilo(meters_pt)(0.0L))); + EXPECT_THAT(floor_as(kilo(meters), meters(1001)), SameTypeAndValue(kilo(meters)(1.0))); EXPECT_THAT(floor_as(kilo(meters), meters(1000.1)), SameTypeAndValue(kilo(meters)(1.0))); EXPECT_THAT(floor_as(kilo(meters), meters(1000.1f)), SameTypeAndValue(kilo(meters)(1.0f))); EXPECT_THAT(floor_as(kilo(meters), meters(1000.1L)), SameTypeAndValue(kilo(meters)(1.0L))); + + EXPECT_THAT(floor_as(kilo(meters_pt), meters_pt(1001)), SameTypeAndValue(kilo(meters_pt)(1.0))); + EXPECT_THAT(floor_as(kilo(meters_pt), meters_pt(1000.1)), + SameTypeAndValue(kilo(meters_pt)(1.0))); + EXPECT_THAT(floor_as(kilo(meters_pt), meters_pt(1000.1f)), + SameTypeAndValue(kilo(meters_pt)(1.0f))); + EXPECT_THAT(floor_as(kilo(meters_pt), meters_pt(1000.1L)), + SameTypeAndValue(kilo(meters_pt)(1.0L))); } TEST(FloorAs, SupportsDifferentOutputTypes) { @@ -822,8 +898,32 @@ TEST(FloorAs, SupportsDifferentOutputTypes) { EXPECT_THAT(floor_as(meters, meters(3.9)), SameTypeAndValue(meters(static_cast(std::floor(3.9))))); + EXPECT_THAT(floor_as(meters_pt, meters_pt(3)), + SameTypeAndValue(meters_pt(static_cast(std::floor(3))))); + EXPECT_THAT(floor_as(meters_pt, meters_pt(3.9)), + SameTypeAndValue(meters_pt(static_cast(std::floor(3.9))))); + EXPECT_THAT(floor_as(kilo(meters), meters(1000.1f)), SameTypeAndValue(kilo(meters)(1.0))); + + EXPECT_THAT(floor_as(kilo(meters_pt), meters_pt(1000.1f)), + SameTypeAndValue(kilo(meters_pt)(1.0))); +} + +TEST(FloorAs, SupportsQuantityPointWithNontrivialOffset) { + EXPECT_THAT(floor_as(kelvins_pt, celsius_pt(20.0f)), SameTypeAndValue(kelvins_pt(293.0f))); + EXPECT_THAT(floor_as(kelvins_pt, celsius_pt(20.8f)), SameTypeAndValue(kelvins_pt(293.0f))); + EXPECT_THAT(floor_as(kelvins_pt, celsius_pt(20.9f)), SameTypeAndValue(kelvins_pt(294.0f))); + + EXPECT_THAT(floor_as(celsius_pt, fahrenheit_pt(31.0)), SameTypeAndValue(celsius_pt(-1))); + EXPECT_THAT(floor_as(celsius_pt, fahrenheit_pt(31.5)), SameTypeAndValue(celsius_pt(-1))); + + EXPECT_THAT(floor_as(celsius_pt, fahrenheit_pt(32.0)), SameTypeAndValue(celsius_pt(0))); + + EXPECT_THAT(floor_as(celsius_pt, fahrenheit_pt(32.5)), SameTypeAndValue(celsius_pt(0))); + EXPECT_THAT(floor_as(celsius_pt, fahrenheit_pt(33.0)), SameTypeAndValue(celsius_pt(0))); + EXPECT_THAT(floor_as(celsius_pt, fahrenheit_pt(33.5)), SameTypeAndValue(celsius_pt(0))); + EXPECT_THAT(floor_as(celsius_pt, fahrenheit_pt(34.0)), SameTypeAndValue(celsius_pt(1))); } TEST(FloorIn, SameAsFloorAs) { @@ -831,10 +931,17 @@ TEST(FloorIn, SameAsFloorAs) { EXPECT_THAT(floor_in(kilo(meters), meters(1154.28)), SameTypeAndValue(1.0)); EXPECT_THAT(floor_in(kilo(meters), meters(1154.28f)), SameTypeAndValue(1.0f)); EXPECT_THAT(floor_in(kilo(meters), meters(1154.28L)), SameTypeAndValue(1.0L)); + + EXPECT_THAT(floor_in(kilo(meters_pt), meters_pt(1154)), SameTypeAndValue(1.0)); + EXPECT_THAT(floor_in(kilo(meters_pt), meters_pt(1154.28)), SameTypeAndValue(1.0)); + EXPECT_THAT(floor_in(kilo(meters_pt), meters_pt(1154.28f)), SameTypeAndValue(1.0f)); + EXPECT_THAT(floor_in(kilo(meters_pt), meters_pt(1154.28L)), SameTypeAndValue(1.0L)); } TEST(FloorIn, SupportsDifferentOutputTypes) { EXPECT_THAT(floor_in(kilo(meters), meters(1154.28f)), SameTypeAndValue(1.0L)); + EXPECT_THAT(floor_in(kilo(meters_pt), meters_pt(1154.28f)), + SameTypeAndValue(1.0L)); } TEST(CeilAs, SameAsStdCeilForSameUnits) { @@ -843,8 +950,18 @@ TEST(CeilAs, SameAsStdCeilForSameUnits) { EXPECT_THAT(ceil_as(meters, meters(3.14f)), SameTypeAndValue(meters(std::ceil(3.14f)))); EXPECT_THAT(ceil_as(meters, meters(3.14L)), SameTypeAndValue(meters(std::ceil(3.14L)))); + EXPECT_THAT(ceil_as(meters_pt, meters_pt(3)), SameTypeAndValue(meters_pt(std::ceil(3)))); + EXPECT_THAT(ceil_as(meters_pt, meters_pt(3.14)), SameTypeAndValue(meters_pt(std::ceil(3.14)))); + EXPECT_THAT(ceil_as(meters_pt, meters_pt(3.14f)), + SameTypeAndValue(meters_pt(std::ceil(3.14f)))); + EXPECT_THAT(ceil_as(meters_pt, meters_pt(3.14L)), + SameTypeAndValue(meters_pt(std::ceil(3.14L)))); + EXPECT_THAT(ceil_as(meters, meters(INTEGER_TOO_BIG_FOR_DOUBLE)), SameTypeAndValue(meters(std::ceil(INTEGER_TOO_BIG_FOR_DOUBLE)))); + + EXPECT_THAT(ceil_as(meters_pt, meters_pt(INTEGER_TOO_BIG_FOR_DOUBLE)), + SameTypeAndValue(meters_pt(std::ceil(INTEGER_TOO_BIG_FOR_DOUBLE)))); } TEST(CeilAs, RoundsUpAsExpectedForDifferentUnits) { @@ -853,10 +970,25 @@ TEST(CeilAs, RoundsUpAsExpectedForDifferentUnits) { EXPECT_THAT(ceil_as(kilo(meters), meters(999.9f)), SameTypeAndValue(kilo(meters)(1.0f))); EXPECT_THAT(ceil_as(kilo(meters), meters(999.9L)), SameTypeAndValue(kilo(meters)(1.0L))); + EXPECT_THAT(ceil_as(kilo(meters_pt), meters_pt(999)), SameTypeAndValue(kilo(meters_pt)(1.0))); + EXPECT_THAT(ceil_as(kilo(meters_pt), meters_pt(999.9)), SameTypeAndValue(kilo(meters_pt)(1.0))); + EXPECT_THAT(ceil_as(kilo(meters_pt), meters_pt(999.9f)), + SameTypeAndValue(kilo(meters_pt)(1.0f))); + EXPECT_THAT(ceil_as(kilo(meters_pt), meters_pt(999.9L)), + SameTypeAndValue(kilo(meters_pt)(1.0L))); + EXPECT_THAT(ceil_as(kilo(meters), meters(1001)), SameTypeAndValue(kilo(meters)(2.0))); EXPECT_THAT(ceil_as(kilo(meters), meters(1000.1)), SameTypeAndValue(kilo(meters)(2.0))); EXPECT_THAT(ceil_as(kilo(meters), meters(1000.1f)), SameTypeAndValue(kilo(meters)(2.0f))); EXPECT_THAT(ceil_as(kilo(meters), meters(1000.1L)), SameTypeAndValue(kilo(meters)(2.0L))); + + EXPECT_THAT(ceil_as(kilo(meters_pt), meters_pt(1001)), SameTypeAndValue(kilo(meters_pt)(2.0))); + EXPECT_THAT(ceil_as(kilo(meters_pt), meters_pt(1000.1)), + SameTypeAndValue(kilo(meters_pt)(2.0))); + EXPECT_THAT(ceil_as(kilo(meters_pt), meters_pt(1000.1f)), + SameTypeAndValue(kilo(meters_pt)(2.0f))); + EXPECT_THAT(ceil_as(kilo(meters_pt), meters_pt(1000.1L)), + SameTypeAndValue(kilo(meters_pt)(2.0L))); } TEST(CeilAs, SupportsDifferentOutputTypes) { @@ -865,8 +997,32 @@ TEST(CeilAs, SupportsDifferentOutputTypes) { EXPECT_THAT(ceil_as(meters, meters(3.9)), SameTypeAndValue(meters(static_cast(std::ceil(3.9))))); + EXPECT_THAT(ceil_as(meters_pt, meters_pt(3)), + SameTypeAndValue(meters_pt(static_cast(std::ceil(3))))); + EXPECT_THAT(ceil_as(meters_pt, meters_pt(3.9)), + SameTypeAndValue(meters_pt(static_cast(std::ceil(3.9))))); + EXPECT_THAT(ceil_as(kilo(meters), meters(1000.1f)), SameTypeAndValue(kilo(meters)(2.0))); + + EXPECT_THAT(ceil_as(kilo(meters_pt), meters_pt(1000.1f)), + SameTypeAndValue(kilo(meters_pt)(2.0))); +} + +TEST(CeilAs, SupportsQuantityPointWithNontrivialOffset) { + EXPECT_THAT(ceil_as(kelvins_pt, celsius_pt(20.0f)), SameTypeAndValue(kelvins_pt(294.0f))); + EXPECT_THAT(ceil_as(kelvins_pt, celsius_pt(20.8f)), SameTypeAndValue(kelvins_pt(294.0f))); + EXPECT_THAT(ceil_as(kelvins_pt, celsius_pt(20.9f)), SameTypeAndValue(kelvins_pt(295.0f))); + + EXPECT_THAT(ceil_as(celsius_pt, fahrenheit_pt(30.0)), SameTypeAndValue(celsius_pt(-1))); + EXPECT_THAT(ceil_as(celsius_pt, fahrenheit_pt(30.5)), SameTypeAndValue(celsius_pt(0))); + EXPECT_THAT(ceil_as(celsius_pt, fahrenheit_pt(31.0)), SameTypeAndValue(celsius_pt(0))); + EXPECT_THAT(ceil_as(celsius_pt, fahrenheit_pt(31.5)), SameTypeAndValue(celsius_pt(0))); + + EXPECT_THAT(ceil_as(celsius_pt, fahrenheit_pt(32.0)), SameTypeAndValue(celsius_pt(0))); + + EXPECT_THAT(ceil_as(celsius_pt, fahrenheit_pt(32.5)), SameTypeAndValue(celsius_pt(1))); + EXPECT_THAT(ceil_as(celsius_pt, fahrenheit_pt(33.0)), SameTypeAndValue(celsius_pt(1))); } TEST(CeilIn, SameAsCeilAs) { @@ -874,10 +1030,16 @@ TEST(CeilIn, SameAsCeilAs) { EXPECT_THAT(ceil_in(kilo(meters), meters(354.28)), SameTypeAndValue(1.0)); EXPECT_THAT(ceil_in(kilo(meters), meters(354.28f)), SameTypeAndValue(1.0f)); EXPECT_THAT(ceil_in(kilo(meters), meters(354.28L)), SameTypeAndValue(1.0L)); + + EXPECT_THAT(ceil_in(kilo(meters_pt), meters_pt(354)), SameTypeAndValue(1.0)); + EXPECT_THAT(ceil_in(kilo(meters_pt), meters_pt(354.28)), SameTypeAndValue(1.0)); + EXPECT_THAT(ceil_in(kilo(meters_pt), meters_pt(354.28f)), SameTypeAndValue(1.0f)); + EXPECT_THAT(ceil_in(kilo(meters_pt), meters_pt(354.28L)), SameTypeAndValue(1.0L)); } TEST(CeilIn, SupportsDifferentOutputTypes) { EXPECT_THAT(ceil_in(kilo(meters), meters(354.28f)), SameTypeAndValue(1.0L)); + EXPECT_THAT(ceil_in(kilo(meters_pt), meters_pt(354.28f)), SameTypeAndValue(1.0L)); } TEST(InverseAs, HandlesIntegerRepCorrectly) { diff --git a/docs/reference/math.md b/docs/reference/math.md index b8f0b187..359d54df 100644 --- a/docs/reference/math.md +++ b/docs/reference/math.md @@ -400,19 +400,31 @@ As with everything else in the library, `"as"` is a word that means "return a `Q ```cpp // -// round_as(): return a Quantity +// round_as(): return a Quantity or QuantityPoint (depending on the input type) // // 1. Unit-only version (including safety checks). Typical callsites look like: // `round_as(units, quantity)` + +// a) For `Quantity` inputs template auto round_as(RoundingUnits rounding_units, Quantity q); +// b) For `QuantityPoint` inputs +template +auto round_as(RoundingUnits rounding_units, QuantityPoint q); + // 2. Explicit-rep version (overriding; ignores safety checks). Typical callsites look like: // `round_as(units, quantity)` + +// a) For `Quantity` inputs template auto round_as(RoundingUnits rounding_units, Quantity q); +// b) For `QuantityPoint` inputs +template +auto round_as(RoundingUnits rounding_units, QuantityPoint q); + // // round_in(): return a raw number @@ -420,17 +432,30 @@ auto round_as(RoundingUnits rounding_units, Quantity q); // 1. Unit-only version (including safety checks). Typical callsites look like: // `round_in(units, quantity)` + +// a) For `Quantity` inputs template auto round_in(RoundingUnits rounding_units, Quantity q); +// b) For `QuantityPoint` inputs +template +auto round_in(RoundingUnits rounding_units, QuantityPoint q); + // 2. Explicit-rep version (overriding; ignores safety checks). Typical callsites look like: // `round_in(units, quantity)` + +// a) For `Quantity` inputs template auto round_in(RoundingUnits rounding_units, Quantity q); + +// b) For `QuantityPoint` inputs +template +auto round_in(RoundingUnits rounding_units, QuantityPoint q); ``` -**Returns:** A `Quantity`, expressed in the requested units, which has an integer value in those -units. We return the nearest such quantity to the original input quantity. +**Returns:** A `Quantity` or `QuantityPoint` (depending on the input type), expressed in the +requested units, which has an integer value in those units. We return the nearest such quantity to +the original input quantity. The policy for the rep is consistent with [`std::round`](https://en.cppreference.com/w/cpp/numeric/math/round). The output rep is the same as @@ -454,19 +479,31 @@ As with everything else in the library, `"as"` is a word that means "return a `Q ```cpp // -// ceil_as(): return a Quantity +// ceil_as(): return a Quantity or QuantityPoint (depending on the input type) // // 1. Unit-only version (including safety checks). Typical callsites look like: // `ceil_as(units, quantity)` + +// a) For `Quantity` inputs template auto ceil_as(RoundingUnits rounding_units, Quantity q); +// b) For `QuantityPoint` inputs +template +auto ceil_as(RoundingUnits rounding_units, QuantityPoint q); + // 2. Explicit-rep version (overriding; ignores safety checks). Typical callsites look like: // `ceil_as(units, quantity)` + +// a) For `Quantity` inputs template auto ceil_as(RoundingUnits rounding_units, Quantity q); +// b) For `QuantityPoint` inputs +template +auto ceil_as(RoundingUnits rounding_units, QuantityPoint q); + // // ceil_in(): return a raw number @@ -474,13 +511,25 @@ auto ceil_as(RoundingUnits rounding_units, Quantity q); // 1. Unit-only version (including safety checks). Typical callsites look like: // `ceil_in(units, quantity)` + +// a) For `Quantity` inputs template auto ceil_in(RoundingUnits rounding_units, Quantity q); +// b) For `QuantityPoint` inputs +template +auto ceil_in(RoundingUnits rounding_units, QuantityPoint q); + // 2. Explicit-rep version (overriding; ignores safety checks). Typical callsites look like: // `ceil_in(units, quantity)` + +// a) For `Quantity` inputs template auto ceil_in(RoundingUnits rounding_units, Quantity q); + +// b) For `QuantityPoint` inputs +template +auto ceil_in(RoundingUnits rounding_units, QuantityPoint q); ``` **Returns:** A `Quantity`, expressed in the requested units, which has an integer value in those @@ -508,19 +557,31 @@ As with everything else in the library, `"as"` is a word that means "return a `Q ```cpp // -// floor_as(): return a Quantity +// floor_as(): return a Quantity or QuantityPoint (depending on the input type) // // 1. Unit-only version (including safety checks). Typical callsites look like: // `floor_as(units, quantity)` + +// a) For `Quantity` inputs template auto floor_as(RoundingUnits rounding_units, Quantity q); +// b) For `QuantityPoint` inputs +template +auto floor_as(RoundingUnits rounding_units, QuantityPoint q); + // 2. Explicit-rep version (overriding; ignores safety checks). Typical callsites look like: // `floor_as(units, quantity)` + +// a) For `Quantity` inputs template auto floor_as(RoundingUnits rounding_units, Quantity q); +// b) For `QuantityPoint` inputs +template +auto floor_as(RoundingUnits rounding_units, QuantityPoint q); + // // floor_in(): return a raw number @@ -528,13 +589,25 @@ auto floor_as(RoundingUnits rounding_units, Quantity q); // 1. Unit-only version (including safety checks). Typical callsites look like: // `floor_in(units, quantity)` + +// a) For `Quantity` inputs template auto floor_in(RoundingUnits rounding_units, Quantity q); +// b) For `QuantityPoint` inputs +template +auto floor_in(RoundingUnits rounding_units, QuantityPoint q); + // 2. Explicit-rep version (overriding; ignores safety checks). Typical callsites look like: // `floor_in(units, quantity)` + +// a) For `Quantity` inputs template auto floor_in(RoundingUnits rounding_units, Quantity q); + +// b) For `QuantityPoint` inputs +template +auto floor_in(RoundingUnits rounding_units, QuantityPoint q); ``` **Returns:** A `Quantity`, expressed in the requested units, which has an integer value in those @@ -640,8 +713,13 @@ Indicates whether the underlying value of a `Quantity` is a NaN ("not-a-number") **Signature:** ```cpp +// 1. `Quantity` inputs template constexpr bool isnan(Quantity q); + +// 2. `QuantityPoint` inputs +template +constexpr bool isnan(QuantityPoint q); ``` **Returns:** `true` if `q` is NaN; `false` otherwise. @@ -715,4 +793,3 @@ auto remainder(Quantity q1, Quantity q2); **Returns:** The remainder of `q1 / q2`, in the type `Quantity`, where `U` is the common unit of `U1` and `U2`, and `R` is the common type of `R1` and `R2`. -