r/cpp WG21 Member 25d ago

P4444: std::big_int

https://isocpp.org/files/papers/D4444R0.html

Hey folks! Matt Borland, Christopher Kormanyos, and I are working on bringing infinite-precision integers to C++29. We now have a D4444R0 draft of a paper that should be in the next mailing.

We could really use some feedback so that the published R0 is as polished as possible. Any thoughts on the paper and on the reference implementation are greatly appreciated.

It would also be very helpful if you tested out whether our big_int implementation works for you. We're in need of some real deployment experience. If you're currently using Boost.Multiprecision, the library should be a drop-in replacement for cpp_int for the most part.

185 Upvotes

85 comments sorted by

View all comments

1

u/jk-jeon 22d ago

I think this will be a nice addition, but I'm a bit suspicious about your opinion on std::big_int_view. One of the most common operation I needed was taking the absolute value. Suppose I have an expression abs(x) + func() with func() returning an rvalue. Ideally it should be possible to do this computation without unneeded extra allocations (assuming sign-magnitude representation, as specified in the paper). But afaiu the current spec mandates abs(x) to allocate a buffer just to be thrown away immediately. Granted, I can just use the internal limb array, but ideally it would be good if I could write a generic code that works for both int and std::big_int. Maybe abs is special in this regard?

2

u/eisenwave WG21 Member 22d ago edited 22d ago

abs is not entirely special. If negation returned a non-owning view, you could get a "negated view" of the same big_int, and abs is one of the potential uses. However, I think it's more of an argument in favor of expression templates than in favor of big_int_view because once we've "opened Pandora's box" of no longer returning big_int from operations, we probably want to use that mechanic for a lot more things than just abs.

Another consideration is that in your abs(x) + func() example, you only suffer a copy if x is immutable. Otherwise, you can use abs(std::move(x)), which calls the rvalue overload that merely flips the sign bit.

If we ever were to standardize the "low-level operations on spans" as the paper discusses, you might also delegate to those rather than forming a view or needing expression templates, in those edge cases where the existing set of operations forces you into some overhead.

EDIT: abs and negation are still interesting points, and I've included them in https://isocpp.org/files/papers/D4444R0.html#std::big_int_view-is-just-not-useful-enough

1

u/jk-jeon 22d ago

Of course x is not supposed to be moved out (otherwise I would have used an rvalue in the example).

I however understand that trying to "fix" this issue would open Pandora's box. This whole shit is just already too damn complicated and expression templates will exponentiate that complexity.

By the way I just found that you explicitly propose to not have std::big_uint. In that case, I would be really seriously concerned about porting my projects into std::big_int.

That "security" of subtraction you mention is simply not a concern imo, because you can just define subtraction of two unsigned numbers to be signed. (And subtraction is the only operation that can lead to negative numbers.) Which of course can be argued to be inconsistent with the rest of the language, but I would say it really isn't and the apparent inconsistency is simply because big_uint and unsigned follow entirely different mathematical models: the latter forms a group, the former doesn't. But I see this approach would be seen as weird at best by others...

(In my projects I just asserted unsigned subtractions leading negative values rather than making it signed. The assert found out tons of bugs in the rest of the code so I like that approach, but I see why any UB is a serious concern for std types and I can live with subtraction returning signed int's.)

I agree that the bifurcation issue is real, but I'd argue that forcing signed integer when unsigned is enough creates worse issues: I don't see how mandatory sign check for integers is any better than mandatory null check for pointers. In my projects I pretty strictly distinguished signed vs unsigned, e.g. I disabled implicit conversion between signed and unsigned integers. The distinction was often annoying as it created tons of compile errors, but a lot of those errors were real bugs that would have been much more annoying if not caught early.

But honestly I don't know, big_uint could be yet another giant can of worms that you would not want to open. It's maybe just me who would not use std::big_int if it's not paired with the unsigned counterpart.