
Hello, boost-request.
Date: Mon, 07 May 2007 17:19:36 +0100 From: "Phil Endecott" <spam_from_boost_dev@chezphil.org> Subject: Re: [boost] Boost::Bigint header draft - suggestions are welcome!
Looks good. Some quick thoughts:
- Have you considered conversions to/from floating-point? Hmm, no. I'm putting it in the todo list, though this is not going to be in the base part I think.
- Have you considered using Boost.Operators? Yes. Quoting discussion with my mentor:
See Boost.operators for a concept breakdown for Addable, Subtractable, etc.
I started working on the header draft, and looked at boost.operators. Am I correct if I say that they are not like traits (to be able to determine if a type can be added to another one, etc.), but instead provide implementation for all related operators if some basic ones are provided (+ if += exists, all comparison operators if < and == exist, etc.)?
Because if that's true, the only thing I could use from it is the group of operators that relate to comparison.
The reason is simple - the suggested implementation for + in terms of += (T nrv( lhs ); nrv += rhs; return nrv;)
is not as efficient as it could be even if compiler implements NRVO - this function still always has extra copy (T nrv(lhs)). I referred to it in my application btw. If I have a generic 3-register implementation of an operation (add(dest, src1, src2)), I can do the most efficient implementation.
My implementation for + could look like (or well, it already does :))
bigint<I> result; result.impl.add(lhs.impl, rhs.impl); return result;
So it utilizes NRVO, but does not have extra copy if the implementation itself allows for it. So the call will have exactly the same overhead as the user might expect if he knows what implementation is used - there's no hidden overhead here, if the compiler implements NRVO of course. And this does not require a lot of extra work, I was planning to do 3-register implementations of every binary operation anyway.
As for <, <=, etc. - I don't see any benefit from boost operators for me either.
= is implemented like this, for example:
return lhs.impl.compare(rhs.impl) >= 0;
And this "compare" function is more or less the same as operator< in terms of implementation - not much harder.
Though perhaps here the situation is different - in theory, providing separate < and == allows the operations == and != be implemented faster (not that I'm planning to do it), than via compare() == 0 way.
So I guess the answer is - yes, I considered using Boost.Operators, but the current decision is not to use them.
- Are logical operations well defined for negative operands with different numbers of bits? (e.g. -1 | 12345) Also, what does ~ do? Are different signed and unsigned types needed? The semantics of logical operations for negative numbers is to be defined, they will most likely work with negative numbers as the 2-complement of their magnitude (regardless of the underlying implementation), either expanded at some amount of bits (max for bits in both operands, perhaps more than that) or negative numbers will just be treated as 2-complemented positive numbers with infinite number of '1' as a sign extension (so about -1 | 5: MSB first, LSB last
-1 is treated as 1...(infinite number)..1111 5 is treated as 0...(infinite number)...0101 Oring them produces 1..(infinite number)..1111 so that's -1 Xoring them produces 1..(infinite number)..1010 that's the complement for 101, so that's -5). This will be outlined in the docs. I don't think providing both signed and unsigned types is reasonable, because without sign, special handling for underflows has to be decided on, and generally I don't see 'unsigned bigint' as something that should ever be used instead of 'signed' one.
- I take it that this class dynamically allocated enough memory for the value. Can you quantify the performance penalty that this imposes compared to declaring the number of bits in advance (e.g. bigint<256>) and statically allocating that much memory? 'default' (pure C++ w/out third-party libraries) implementation will be useful with any storage strategy (I will include one based on std::vector, so it'll be possible to provide a custom allocator, and a stack-based one with the specified maximum number of bytes). 'gmp' one (based on GMP) is not customizable in this way now, though this is a feature in a todo list for the second part of the project (read: perhaps it will be implemented after all base features are).
-- Best regards, Arseny mailto:arseny.kapoulkine@gmail.com