I tend to think that the "library-only" approach would work fine, if only we could get the proper primitives into the standard library. For example, if we had something like _addcarry_u64, but without the pointer parameter. These primitives will be generally useful; and then things like std::uintN_t<256> can be built on top of them. Right now it feels like P0539 (and John McFarlane's P0554/P0828) are trying to build uintN_t without any access to the appropriate primitives.
Nit on the WG14 proposal: Surely the proper name for the new header is <stdwide.h>, not <stdwint.h>? Or if we must have <stdwint.h>, can we get <stdkidd.h> to go with it?
Major issue on the WG14 proposal: The proposed "**" widening multiplication operator comes out of nowhere. Not only would this be inconsistent with every other programming language (where "**" generally means "exponentiation"), but it would be a lexer-breaking change for C and C++ because two consecutive stars can already appear in valid code. Why not just propose a library function `z = widening_multiply(x, y)` that does the same thing but without breaking any existing lexers? C already has _Generic functions (as of C11) which means you don't need to create a new operator to form an overload set in C. You can just do the same thing that e.g. `sin(x)` does.
Question/issue on the WG14 proposal: Does there exist any space-efficient algorithm for printing a `_Wide(1 << 20) int` in base 10? Or are you proposing to allow printing them only in bases 8 and 16? The wording in section 3.11 is too vague to be sure.
I tend to think that the "library-only" approach would work fine, if only we could get the proper primitives into the standard library. For example, if we had something like _addcarry_u64, but without the pointer parameter. These primitives will be generally useful; and then things like std::uintN_t<256> can be built on top of them. Right now it feels like P0539 (and John McFarlane's P0554/P0828) are trying to build uintN_t without any access to the appropriate primitives.I think C++ will never standardise support for carry and overload arithmetic. Too many CPUs don't support it.And besides, it solves the wrong problem. Don't tell the compiler how to implement bigint. Tell it you want bigint. Let it sort out an implementation.
Nit on the WG14 proposal: Surely the proper name for the new header is <stdwide.h>, not <stdwint.h>? Or if we must have <stdwint.h>, can we get <stdkidd.h> to go with it?Well, I wouldn't want to consume the name for something standard and wide in the future. stdwint is at least consistent with stdint. Bond villans aside, of course.Major issue on the WG14 proposal: The proposed "**" widening multiplication operator comes out of nowhere. Not only would this be inconsistent with every other programming language (where "**" generally means "exponentiation"), but it would be a lexer-breaking change for C and C++ because two consecutive stars can already appear in valid code. Why not just propose a library function `z = widening_multiply(x, y)` that does the same thing but without breaking any existing lexers? C already has _Generic functions (as of C11) which means you don't need to create a new operator to form an overload set in C. You can just do the same thing that e.g. `sin(x)` does.It may be the case that ordinary multiply can reliably do widening multiplication with compiler unknown values, and so no extra operator is needed.I specifically included the widening multiplication operator because these wide integers are a bit different from normal integers, specifically the compiler is not required to understand them (they are just a bunch of bits as far as the compiler knows). That means no constant folding, no common subexpression elimination, and so on (though implementations can optionally choose to implement those).I had been assuming that this would require the programmer to tell the compiler what to do. But a compiler writer on WG14 seems to think it'll be okay, just cast the inputs to the larger output type and multiply. The compiler, even without knowing anything about multiply, should correctly not call the twice-too-large multiply routine.
And besides, it solves the wrong problem. Don't tell the compiler how to implement bigint. Tell it you want bigint. Let it sort out an implementation.I agree that carry flags can (hopefully) remain an implementation detail. I'm not sure how a library solution won't suffice -- other than that it's of no use to WG14.
And besides, it solves the wrong problem. Don't tell the compiler how to implement bigint. Tell it you want bigint. Let it sort out an implementation.I agree that carry flags can (hopefully) remain an implementation detail. I'm not sure how a library solution won't suffice -- other than that it's of no use to WG14.Can you show me a library solution which causes the compiler to generate optimum codegen for extended precision integers, and which does not use non-standard C++?
(Funnily enough, a "stupid" fixed implementation library implementation of my proposal based on _Generic I think is very close to possible, but I am unaware of any implementation in existence. You have a lot more experience in this domain than I do though)
--Niall
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/a4b9c3d3-1b3c-4c90-bc98-124106e6d980%40isocpp.org.
On Tue, 6 Nov 2018 at 19:01 Niall Douglas <nialldo...@gmail.com> wrote:I tend to think that the "library-only" approach would work fine, if only we could get the proper primitives into the standard library. For example, if we had something like _addcarry_u64, but without the pointer parameter. These primitives will be generally useful; and then things like std::uintN_t<256> can be built on top of them. Right now it feels like P0539 (and John McFarlane's P0554/P0828) are trying to build uintN_t without any access to the appropriate primitives.I think C++ will never standardise support for carry and overload arithmetic. Too many CPUs don't support it.And besides, it solves the wrong problem. Don't tell the compiler how to implement bigint. Tell it you want bigint. Let it sort out an implementation.I agree that carry flags can (hopefully) remain an implementation detail. I'm not sure how a library solution won't suffice -- other than that it's of no use to WG14.(If GCC/Clang's equivalent intrinsics are anything to go by, I don't see how pointers pose a problem. Yes, it's an ugly interface but should be constexpr-friendly in latest tools and language revision.)Regarding the efficiency of P0539, don't judge it by the implementation which, AFAIK, only implements bytewise arithmetic. I still think it's the wrong API but for more reasons than just performance.
On Thu, Nov 8, 2018 at 3:18 AM John McFarlane <jo...@mcfarlane.name> wrote:On Tue, 6 Nov 2018 at 19:01 Niall Douglas <nialldo...@gmail.com> wrote:I tend to think that the "library-only" approach would work fine, if only we could get the proper primitives into the standard library. For example, if we had something like _addcarry_u64, but without the pointer parameter. These primitives will be generally useful; and then things like std::uintN_t<256> can be built on top of them. Right now it feels like P0539 (and John McFarlane's P0554/P0828) are trying to build uintN_t without any access to the appropriate primitives.I think C++ will never standardise support for carry and overload arithmetic. Too many CPUs don't support it.And besides, it solves the wrong problem. Don't tell the compiler how to implement bigint. Tell it you want bigint. Let it sort out an implementation.I agree that carry flags can (hopefully) remain an implementation detail. I'm not sure how a library solution won't suffice -- other than that it's of no use to WG14.(If GCC/Clang's equivalent intrinsics are anything to go by, I don't see how pointers pose a problem. Yes, it's an ugly interface but should be constexpr-friendly in latest tools and language revision.)Regarding the efficiency of P0539, don't judge it by the implementation which, AFAIK, only implements bytewise arithmetic. I still think it's the wrong API but for more reasons than just performance."Don't judge this numerics library by its performance" doesn't sound acceptable to me. I mean, performance seems like just about the only thing we should be judging. Would you (or anyone) say you're confident that the performance problems can be overcome? Do we have a proof-of-concept somewhere that shows we can get perfect codegen for any one test case?
On Thu, Nov 8, 2018 at 3:18 AM John McFarlane <jo...@mcfarlane.name> wrote:On Tue, 6 Nov 2018 at 19:01 Niall Douglas <nialldo...@gmail.com> wrote:I tend to think that the "library-only" approach would work fine, if only we could get the proper primitives into the standard library. For example, if we had something like _addcarry_u64, but without the pointer parameter. These primitives will be generally useful; and then things like std::uintN_t<256> can be built on top of them. Right now it feels like P0539 (and John McFarlane's P0554/P0828) are trying to build uintN_t without any access to the appropriate primitives.I think C++ will never standardise support for carry and overload arithmetic. Too many CPUs don't support it.And besides, it solves the wrong problem. Don't tell the compiler how to implement bigint. Tell it you want bigint. Let it sort out an implementation.I agree that carry flags can (hopefully) remain an implementation detail. I'm not sure how a library solution won't suffice -- other than that it's of no use to WG14.(If GCC/Clang's equivalent intrinsics are anything to go by, I don't see how pointers pose a problem. Yes, it's an ugly interface but should be constexpr-friendly in latest tools and language revision.)Regarding the efficiency of P0539, don't judge it by the implementation which, AFAIK, only implements bytewise arithmetic. I still think it's the wrong API but for more reasons than just performance."Don't judge this numerics library by its performance" doesn't sound acceptable to me. I mean, performance seems like just about the only thing we should be judging. Would you (or anyone) say you're confident that the performance problems can be overcome? Do we have a proof-of-concept somewhere that shows we can get perfect codegen for any one test case?I trust the inliner on all modern compilers (except maybe MSVC). If someone can provide C++ code (standard or non-standard, I don't care) that implements 256-bit and 512-bit arithmetic with ugly C++ but perfect codegen on at least one compiler, then I'll gladly donate a day of my time to refactoring the C++ code into something more ergonomic.Here's a perfect-codegen proof of concept for 128-bit operator+.Here's an intrinsic-less proof of concept for 128-bit operator+, but it falls down pretty badly on 256-bit operator+.
On Thu, Nov 8, 2018 at 3:18 AM John McFarlane <jo...@mcfarlane.name> wrote:On Tue, 6 Nov 2018 at 19:01 Niall Douglas <nialldo...@gmail.com> wrote:I tend to think that the "library-only" approach would work fine, if only we could get the proper primitives into the standard library. For example, if we had something like _addcarry_u64, but without the pointer parameter. These primitives will be generally useful; and then things like std::uintN_t<256> can be built on top of them. Right now it feels like P0539 (and John McFarlane's P0554/P0828) are trying to build uintN_t without any access to the appropriate primitives.I think C++ will never standardise support for carry and overload arithmetic. Too many CPUs don't support it.And besides, it solves the wrong problem. Don't tell the compiler how to implement bigint. Tell it you want bigint. Let it sort out an implementation.I agree that carry flags can (hopefully) remain an implementation detail. I'm not sure how a library solution won't suffice -- other than that it's of no use to WG14.(If GCC/Clang's equivalent intrinsics are anything to go by, I don't see how pointers pose a problem. Yes, it's an ugly interface but should be constexpr-friendly in latest tools and language revision.)Regarding the efficiency of P0539, don't judge it by the implementation which, AFAIK, only implements bytewise arithmetic. I still think it's the wrong API but for more reasons than just performance."Don't judge this numerics library by its performance" doesn't sound acceptable to me.
I mean, performance seems like just about the only thing we should be judging.
Would you (or anyone) say you're confident that the performance problems can be overcome?
Do we have a proof-of-concept somewhere that shows we can get perfect codegen for any one test case?
I trust the inliner on all modern compilers (except maybe MSVC). If someone can provide C++ code (standard or non-standard, I don't care) that implements 256-bit and 512-bit arithmetic with ugly C++ but perfect codegen on at least one compiler, then I'll gladly donate a day of my time to refactoring the C++ code into something more ergonomic.Here's a perfect-codegen proof of concept for 128-bit operator+.Here's an intrinsic-less proof of concept for 128-bit operator+, but it falls down pretty badly on 256-bit operator+.
Since I can't even get to 256-bit operator+ on my own, I'm by default skeptical that anyone can get to N-bit all-the-operators. That's why I want to see a proof of concept, and why I think it's so important to look at the codegen of the existing libraries and demonstrate (as Niall has demonstrated) that they're all horrible. We shouldn't standardize something that's horrible.
Why are current libraries horrible? If it's because their authors are "digging with spoons instead of shovels," then we should ask them what shovels they'd need in order to do a good job. (For example, would it help to have "widening add" and "widening multiply" algorithms in the Standard Library?)
I tend to think that the "library-only" approach would work fine, if only we could get the proper primitives into the standard library. For example, if we had something like _addcarry_u64, but without the pointer parameter. These primitives will be generally useful; and then things like std::uintN_t<256> can be built on top of them. Right now it feels like P0539 (and John McFarlane's P0554/P0828) are trying to build uintN_t without any access to the appropriate primitives.I think C++ will never standardise support for carry and overload arithmetic. Too many CPUs don't support it.And besides, it solves the wrong problem. Don't tell the compiler how to implement bigint. Tell it you want bigint. Let it sort out an implementation.
I don't think anyone is saying that it's acceptable for the performance to be terrible. But having bignums standardized as a library component as in P0539 doesn't prevent the implementation from treating std::wide_integer types as magic types implemented using efficient intrinsics.