@@ -275,6 +275,19 @@ using CommonMagnitude = typename CommonMagnitudeImpl<Ms...>::type;
275275template <typename ... Ms>
276276using CommonMagnitudeT = CommonMagnitude<Ms...>;
277277
278+ // The sum of arbitrarily many `Magnitude` and/or `Zero` types.
279+ //
280+ // We only support this when it is "easy" to compute, where "easy" is defined as:
281+ // 1) all inputs being expressible as integer multiples of some common factor;
282+ // 2) each such integer's absolute value fitting in a `uint64_t`;
283+ // 3) *and*, the absolute value of the sum also fitting in a `uint64_t`.
284+ //
285+ // For all other cases, we currently produce a compile time error.
286+ template <typename ... Ms>
287+ struct MagSumImpl ;
288+ template <typename ... Ms>
289+ using MagSum = typename MagSumImpl<Ms...>::type;
290+
278291// //////////////////////////////////////////////////////////////////////////////////////////////////
279292// Value based interface for Magnitude (and Zero).
280293
@@ -513,45 +526,8 @@ constexpr Zero mag_round(Zero) { return {}; }
513526
514527// Addition:
515528template <typename ... BP1s, typename ... BP2s>
516- constexpr auto operator +(Magnitude<BP1s...> m1, Magnitude<BP2s...> m2) {
517- constexpr auto sgn1 = sign (m1);
518- constexpr auto sgn2 = sign (m2);
519-
520- constexpr auto abs_common = Abs<CommonMagnitude<Magnitude<BP1s...>, Magnitude<BP2s...>>>{};
521- constexpr auto abs_num1 = abs (m1) / abs_common;
522- constexpr auto abs_num2 = abs (m2) / abs_common;
523-
524- // These `get_value` calls automatically check that individual _inputs_ fit in `uint64_t`.
525- constexpr auto abs_num1_u64 = get_value<std::uint64_t >(abs_num1);
526- constexpr auto abs_num2_u64 = get_value<std::uint64_t >(abs_num2);
527-
528- // Biggest absolute input determines overall sign.
529- //
530- // Note that when the magnitudes are equal, either the choice doesn't matter (when the inputs
531- // have the same sign), or the outcome should just be `Zero`. In the latter case, we rely on
532- // the explicit `Negative` overloads below being a better match.
533- constexpr auto sgn =
534- std::conditional_t <(abs_num1_u64 > abs_num2_u64), decltype (sgn1), decltype (sgn2)>{};
535-
536- // Here, we are taking advantage of modular arithmetic on unsigned integers. This actually does
537- // handle all the signs correctly, although it may not be obvious at first glance.
538- constexpr auto num1_u64 = is_positive (sgn1) ? abs_num1_u64 : -abs_num1_u64;
539- constexpr auto num2_u64 = is_positive (sgn2) ? abs_num2_u64 : -abs_num2_u64;
540- constexpr auto abs_sum_u64 = is_positive (sgn) ? (num1_u64 + num2_u64) : -(num1_u64 + num2_u64);
541-
542- // Here is where we guard against overflow in the _output_.
543- static_assert ((sgn1 != sgn2) || abs_sum_u64 >= abs_num1_u64,
544- " Magnitude addition overflowed uint64_t" );
545-
546- return sgn * mag<abs_sum_u64>() * abs_common;
547- }
548- template <typename ... BPs>
549- constexpr Zero operator +(Magnitude<Negative, BPs...>, Magnitude<BPs...>) {
550- return {};
551- }
552- template <typename ... BPs>
553- constexpr Zero operator +(Magnitude<BPs...>, Magnitude<Negative, BPs...>) {
554- return {};
529+ constexpr auto operator +(Magnitude<BP1s...>, Magnitude<BP2s...>) {
530+ return MagSum<Magnitude<BP1s...>, Magnitude<BP2s...>>{};
555531}
556532template <typename ... BPs>
557533constexpr auto operator +(Zero, Magnitude<BPs...> m) {
@@ -1276,4 +1252,80 @@ struct CommonMagnitudeImpl<Zero, M> : stdx::type_identity<M> {};
12761252template <>
12771253struct CommonMagnitudeImpl <Zero, Zero> : stdx::type_identity<Zero> {};
12781254
1255+ // //////////////////////////////////////////////////////////////////////////////////////////////////
1256+ // `MagSum` implementation.
1257+
1258+ namespace detail {
1259+
1260+ // `U64MagSum<Ms...>` is the `Magnitude` (or `Zero`) equal to the sum of the Magnitudes `Ms...`, as
1261+ // long as these preconditions are met:
1262+ //
1263+ // 1. The absolute value of each member of `Ms...` fits in a `std::uint64_t`.
1264+ // 2. The absolute value of the sum of all members of `Ms...` fits in a `std::uint64_t`.
1265+ template <typename ... Ms>
1266+ struct U64MagSumImpl {
1267+ struct U64SumResult {
1268+ std::uint64_t sum = 0u ;
1269+ int overflow = 0 ;
1270+ };
1271+
1272+ static constexpr U64SumResult compute () {
1273+ const std::uint64_t abs_values[] = {get_value<std::uint64_t >(Abs<Ms>{})...};
1274+ const int overflows[] = {(IsPositive<Ms>::value ? 0 : -1 )...};
1275+
1276+ U64SumResult result = {0u , 0 };
1277+ for (std::size_t i = 0u ; i < sizeof ...(Ms); ++i) {
1278+ std::uint64_t old_sum = result.sum ;
1279+ result.sum += (overflows[i] >= 0 ) ? abs_values[i] : -abs_values[i];
1280+ result.overflow += overflows[i] + (result.sum < old_sum);
1281+ }
1282+
1283+ return result;
1284+ }
1285+ static constexpr std::uint64_t sum = compute().sum;
1286+ static constexpr int overflow = compute().overflow;
1287+
1288+ static_assert ((overflow == 0 ) || (overflow == -1 && sum > 0u ),
1289+ " Magnitude sum overflowed uint64_t" );
1290+
1291+ using Sign = std::conditional_t <(overflow == -1 ), Magnitude<Negative>, Magnitude<>>;
1292+
1293+ using AbsMag = std::conditional_t <(overflow == 0 ) && (sum == 0u ),
1294+ Zero,
1295+ // The surprising `sum == 0u` avoids asking for `mag<0>()`.
1296+ // It's fine, because it can never actually be used.
1297+ decltype (mag<(overflow == -1 ? -sum : sum) + (sum == 0u )>())>;
1298+
1299+ using type = decltype (Sign{} * AbsMag{});
1300+ };
1301+ template <typename ... Ms>
1302+ using U64MagSum = typename U64MagSumImpl<Ms...>::type;
1303+
1304+ template <typename ... Ms>
1305+ constexpr std::uint64_t U64MagSumImpl<Ms...>::sum;
1306+
1307+ template <typename ... Ms>
1308+ constexpr int U64MagSumImpl<Ms...>::overflow;
1309+
1310+ template <typename ... Ms>
1311+ struct MagSumImplHelper {
1312+ using Common = CommonMagnitude<Ms...>;
1313+ using type = decltype (Common{} * U64MagSum<decltype (Ms{} / Common{})...>{});
1314+ };
1315+
1316+ // The sum of no things is nothing.
1317+ template <>
1318+ struct MagSumImplHelper <> : stdx::type_identity<Zero> {};
1319+
1320+ // Keep stripping off zeros until we find at least one nonzero element, so that the common magnitude
1321+ // machinery can find something meaningful and avoid dividing by zero. (Zeros in the middle will be
1322+ // automatically handled correctly as long as there are nonzero elements.)
1323+ template <typename ... Ms>
1324+ struct MagSumImplHelper <Zero, Ms...> : MagSumImplHelper<Ms...> {};
1325+
1326+ } // namespace detail
1327+
1328+ template <typename ... Ms>
1329+ struct MagSumImpl : detail::MagSumImplHelper<Ms...> {};
1330+
12791331} // namespace au
0 commit comments