Skip to content

Conversation

@chiphogg
Copy link
Member

When we start collecting like terms as part of adding Constant
instances to each other, we will naturally form N-ary sums. To prepare
the way, we introduce a MagSum<Ms...> utility to perform this
operation.

Now, we could just implement this in terms of the existing operator+
for Magnitude. However, that gets a little weird: it makes the result
depend on the order. It's possible for a subset of inputs to overflow
(thus making the binary operation return an error), when subsequent
inputs might "rescue" the result (say, by subtracting to bring it back
into range). Thus, I redid the implementation from scratch, using
modular arithmetic for the result, and keeping track of the (signed)
number of overflows. This gives us exact information about the true sum
(over a very very wide range of values), although we can't always use
this information. Specifically, we can only use the result when the
overflow is 0 (simple), or when it is -1 and the sum is not 0 (because
if sum were 0 in this case it would actually represent -2^64).

This new, more symmetrical implementation immediately takes the place of
all three Magnitude-Magnitude implementations of operator+.

Helps #607.

When we start collecting like terms as part of adding `Constant`
instances to each other, we will naturally form N-ary sums.  To prepare
the way, we introduce a `MagSum<Ms...>` utility to perform this
operation.

Now, we _could_ just implement this in terms of the existing `operator+`
for `Magnitude`.  However, that gets a little weird: it makes the result
depend on the order.  It's possible for a subset of inputs to overflow
(thus making the binary operation return an error), when subsequent
inputs might "rescue" the result (say, by subtracting to bring it back
into range).  Thus, I redid the implementation from scratch, using
modular arithmetic for the result, and keeping track of the (signed)
number of overflows.  This gives us exact information about the true sum
(over a very very wide range of values), although we can't always _use_
this information.  Specifically, we can only use the result when the
overflow is 0 (simple), or when it is -1 _and_ the sum is not 0 (because
if sum _were_ 0 in this case it would actually represent -2^64).

This new, more symmetrical implementation immediately takes the place of
all three `Magnitude`-`Magnitude` implementations of `operator+`.

Helps #607.
Comment on lines +1285 to +1286
static constexpr std::uint64_t sum = compute().sum;
static constexpr int overflow = compute().overflow;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the compiler smart enough not to do this twice?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was wondering about that myself. Ultimately I landed on "I'm curious, but it doesn't matter". Since we store the result(s) as constexpr variables, and then only ever use those, the worst that could happen is a little extra compile time. (And I do mean "little" --- I really don't see how it could be very much.)

Matter of fact... an earlier iteration just used compute() everywhere, so we wouldn't have to add definitions for static member variables down below! 😆

Comment on lines +1295 to +1296
// The surprising `sum == 0u` avoids asking for `mag<0>()`.
// It's fine, because it can never actually be used.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this just because the compiler can't intuit this invariant?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The precise problem we would have if we deleted + (sum == 0u) is that whenever we take the first branch of the conditional, it amounts to std::conditional_t<true, Zero, decltype(mag<0>())>. Obviously, this "should" always just be Zero... but the fact that we have formed mag<0>() at all, anywhere, is enough for the compiler to bail out.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough.

Co-authored-by: Michael Hordijk <hordijk@aurora.tech>
@chiphogg chiphogg merged commit 1420d2d into main Jan 30, 2026
27 checks passed
@chiphogg chiphogg deleted the chiphogg/n-ary-mag-sum#607 branch January 30, 2026 22:47
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

Successfully merging this pull request may close these issues.

3 participants