473,382 Members | 1,329 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,382 software developers and data experts.

Better way to program this?

Hi.

(posted to both newsgroups since I was not sure of which would be
appropriate for this question or how specific to the given language it
is. If one of them is inappropriate, just don't send replies to it.)

I'm making a bignum package for use in a program I've got (this is
something different from the pi program you may have heard about). The
package is going to support manipulating long floating point numbers.

The way I have this set up right now is that there is a "raw" unsigned
integer package, from which the floating point package is built on top
of. The questions I have are about coding that lower-level package.

See, I have a routine here that adds up two unsigned integers, but it
also must be able to add so many digits of one starting at one point
in it's data, to so many digits of the other starting at a different
point in it's data, and finally store so many digits of the result at
yet another point in the data buffer holding the result.

This is what I've got (it's in C++, not C, by the way). The problem is
it just doesn't seem like a "neat", or "nice" piece of code. Anyone
know of a better way to approach this?:

/* Add two RawInts.
* Parameters:
* a: First RawInt
* b: Second RawInt
* rOrigin: offset of digit in this to store result at
* rLength: length of region in this to store result in
* aOrigin: offset of digit in a to start addition at
* aLength: length of region in a to add
* bOrigin: offset of digit in b to start addition at
* bLength: length of region in b to add
*
* Returns: carry.
*
* Operation: *this = a + b.
*
* Use ?Origin = 0 and ?Length = -1 for a simple integer addition
* operation.
*/
DIGIT RawInt::rawAdd(const RawInt &a,
const RawInt &b,
int rOrigin, int rLength,
int aOrigin, int aLength,
int bOrigin, int bLength)
{
int i;
DIGIT tmp, carry = 0;
int rlen2, alen2, blen2;
std::vector<DIGIT>::iterator di, de;
std::vector<DIGIT>::const_iterator ai, ae;
std::vector<DIGIT>::const_iterator bi, be;

/* Make sure we don't exceed the boundaries
* of the digit arrays.
*/
if(rLength != -1)
{
rlen2 = rLength;
if(rlen2 (length - rOrigin))
rlen2 = (length - rOrigin);
} else {
rlen2 = (length - rOrigin);
}

if(aLength != -1)
{
alen2 = aLength;
if(alen2 (a.length - aOrigin))
alen2 = (a.length - aOrigin);
} else {
alen2 = (a.length - aOrigin);
}

if(bLength != -1)
{
blen2 = bLength;
if(blen2 (b.length - bOrigin))
blen2 = (b.length - bOrigin);
} else {
blen2 = (b.length - bOrigin);
}

if(rOrigin+alen2 >= length)
alen2 = length-rOrigin;
if(rOrigin+blen2 >= length)
blen2 = length-rOrigin;

if(alen2 rlen2) alen2 = rlen2;
if(blen2 rlen2) blen2 = rlen2;

if(alen2 < 0) alen2 = 0;
if(blen2 < 0) blen2 = 0;

/* Set up the iterators */
di = digits.begin()+rOrigin; de = digits.end();
ai = a.digits.begin()+aOrigin; ae = a.digits.end();
bi = b.digits.begin()+bOrigin; be = b.digits.end();

/* Now do the addition */
if(alen2 >= blen2)
{
/* Case 1: a is at least as long as b */

/* Add up a's and b's digits */
for(i=0;i<blen2;i++,++di,++ai,++bi)
{
tmp = *ai + *bi + carry;
if(carry) carry = (tmp <= *bi) ? 1 : 0;
else carry = (tmp < *bi) ? 1 : 0;
*di = tmp;
}

/* Now tackle the part of a that is longer than b */
for(i=blen2;i<alen2;i++,++di,++ai)
{
tmp = *ai + carry;
if(carry)
carry = (tmp == 0) ? 1 : 0;
*di = tmp;
}

/* Zeroize the rest of this. */
for(i=alen2;i<rlen2;i++,++di)
{
*di = carry;
carry = 0;
}
} else {
/* Case 2: b is longer than a */

/* Add up a's and b's digits */
for(i=0;i<alen2;i++,++di,++ai,++bi)
{
tmp = *ai + *bi + carry;
if(carry) carry = (tmp <= *bi) ? 1 : 0;
else carry = (tmp < *bi) ? 1 : 0;
*di = tmp;
}

/* Now tackle the part of b that is longer than a */
for(i=alen2;i<blen2;i++,++di,++bi)
{
tmp = *bi + carry;
if(carry) carry = (tmp <= *bi) ? 1 : 0;
else carry = (tmp < *bi) ? 1 : 0;
*di = tmp;
}

/* Zeroize the rest of this. */
for(i=blen2;i<rlen2;i++,++di)
{
*di = carry;
carry = 0;
}
}

/* Done! Return any leftover carry. */
return(carry);
}

Nov 3 '07 #1
23 2323
mike3 wrote:
>
.... snip ...
>
I'm making a bignum package for use in a program I've got (this is
something different from the pi program you may have heard about).
The package is going to support manipulating long floating point
numbers.
Why? Do you want more significant digits? Do you want a larger
range? What are you trying to do.

--
Chuck F (cbfalconer at maineline dot net)
<http://cbfalconer.home.att.net>
Try the download section.
--
Posted via a free Usenet account from http://www.teranews.com

Nov 3 '07 #2
On Nov 3, 3:40 pm, CBFalconer <cbfalco...@yahoo.comwrote:
mike3 wrote:

... snip ...
I'm making a bignum package for use in a program I've got (this is
something different from the pi program you may have heard about).
The package is going to support manipulating long floating point
numbers.

Why? Do you want more significant digits? Do you want a larger
range? What are you trying to do.
That's right -- more precision. More significant digits. It's for a
fractal generator, so you can zoom real deep down.
--
Chuck F (cbfalconer at maineline dot net)
<http://cbfalconer.home.att.net>
Try the download section.

--
Posted via a free Usenet account fromhttp://www.teranews.com

Nov 3 '07 #3

mike3 wrote in message...
>
DIGIT RawInt::rawAdd(const RawInt &a,
const RawInt &b, int rOrigin, int rLength,
int aOrigin, int aLength, int bOrigin, int bLength){
// int i;
DIGIT tmp, carry = 0;
int rlen2, alen2, blen2;
// std::vector<DIGIT>::iterator di, de;
// std::vector<DIGIT>::const_iterator ai, ae;
// std::vector<DIGIT>::const_iterator bi, be;

[ note 'commented' above ]
You seem to have a 'C' background <G>. Declare and initialize those where
you use them. See 'di', 'de' below.
>
/* Make sure we don't exceed the boundaries
* of the digit arrays. */
if(rLength != -1){
rlen2 = rLength;
if(rlen2 (length - rOrigin))
rlen2 = (length - rOrigin);
} else {
rlen2 = (length - rOrigin);
}

if(aLength != -1){
alen2 = aLength;
if(alen2 (a.length - aOrigin))
alen2 = (a.length - aOrigin);
} else {
alen2 = (a.length - aOrigin);
}

if(bLength != -1){
blen2 = bLength;
if(blen2 (b.length - bOrigin))
blen2 = (b.length - bOrigin);
} else {
blen2 = (b.length - bOrigin);
}
Those three if-else blocks are identical. I'd move them into their own
little function. Then you'd just call them something like:

// make 'MyFunc()' return an type int.
int rlen2( MyFunc( rLength, length, rOrigin ) );
int alen2( MyFunc( aLength, a.length, aOrigin ) );
int blen2( MyFunc( bLength, b.length, bOrigin ) );
>
if(rOrigin+alen2 >= length)
alen2 = length-rOrigin;
if(rOrigin+blen2 >= length)
blen2 = length-rOrigin;

if(alen2 rlen2) alen2 = rlen2;
if(blen2 rlen2) blen2 = rlen2;
if(alen2 < 0) alen2 = 0;
if(blen2 < 0) blen2 = 0;

/* Set up the iterators */
di = digits.begin()+rOrigin; de = digits.end();
This is the first place you use 'di', 'de'. I'd do it this way.

std::vector<DIGIT>::iterator di( digits.begin() + rOrigin ),
de( digits.end() );
ai = a.digits.begin()+aOrigin; ae = a.digits.end();
std::vector<DIGIT>::const_iterator ai( a.digits.begin() + aOrigin ),
ae( a.digits.end() );
bi = b.digits.begin()+bOrigin; be = b.digits.end();
std::vector<DIGIT>::const_iterator bi( b.digits.begin() + bOrigin ),
be( b.digits.end() );
>
/* Now do the addition */
if(alen2 >= blen2){
/* Case 1: a is at least as long as b */

/* Add up a's and b's digits */
// for(i=0;i<blen2;i++,++di,++ai,++bi){

You do not use 'int i' outside the for() loops, so, declare them *in* the
for():

for( int i(0); i < blen2; ++i, ++di, ++ai, ++bi ){
tmp = *ai + *bi + carry;
if(carry) carry = (tmp <= *bi) ? 1 : 0;
else carry = (tmp < *bi) ? 1 : 0;
*di = tmp;
}

/* Now tackle the part of a that is longer than b */
// for(i=blen2;i<alen2;i++,++di,++ai){

for( int i( blen2 ); i < alen2; ++i, ++di, ++ai ){
tmp = *ai + carry;
if(carry)
carry = (tmp == 0) ? 1 : 0;
*di = tmp;
}

/* Zeroize the rest of this. */
// for(i=alen2;i<rlen2;i++,++di){

for( int i( alen2 ); i < rlen2; ++i, ++di ){
..... etc.
That's just my suggestion(s). Maybe with less clutter you'll see other
places to 'clean up'.
--
Bob R
POVrookie
Nov 3 '07 #4
On Nov 3, 4:07 pm, LR <lr...@superlink.netwrote:
<snip>

Interesting ideas, and it got me thinking about this approach:
What if instead of making this odd "wrapper" thingy, one were
to just integrate the parameters "length", "origin" right into the
RawInt? So you'd have the maximum length (allocated space),
the used length, the origin, and finally the vector containing the
digits/limbs of the number.

Then when one wants to change them, all one needs is a simple
member function or two, like this:

--- begin snippet ---
/* Reset the length of used part and origin
* (position of initial digit) of a RawInt.
*/
void RawInt::SetLengthOrigin(int newlength, int neworigin)
{
if(neworigin >= length_max)
neworigin = length_max-1; /* safety */

if(newlength length_max-neworigin)
newlength = length_max-neworigin; /* another safety */

length_used = newlength;
origin = neworigin;
}

/* Reset the used-part length/origin to defaults. */
void RawInt::DefaultLengthOrigin()
{
length_used = length_max;
origin = 0;
}
--- end snippet ---

Then one does not need to fuss around with those funny
"origin" checks in the routines -- all that is handled by those
two member functions. All one needs to do is just set up
use the ready-made parameters "origin" and "length_max",
and go! I'll give this a try, and post the results.
Nov 3 '07 #5
On Nov 3, 4:04 pm, "BobR" <removeBadB...@worldnet.att.netwrote:
mike3 wrote in message...
DIGIT RawInt::rawAdd(const RawInt &a,
const RawInt &b, int rOrigin, int rLength,
int aOrigin, int aLength, int bOrigin, int bLength){

// int i; DIGIT tmp, carry = 0;
int rlen2, alen2, blen2;

// std::vector<DIGIT>::iterator di, de;
// std::vector<DIGIT>::const_iterator ai, ae;
// std::vector<DIGIT>::const_iterator bi, be;

[ note 'commented' above ]
You seem to have a 'C' background <G>. Declare and initialize those where
you use them. See 'di', 'de' below.


/* Make sure we don't exceed the boundaries
* of the digit arrays. */
if(rLength != -1){
rlen2 = rLength;
if(rlen2 (length - rOrigin))
rlen2 = (length - rOrigin);
} else {
rlen2 = (length - rOrigin);
}
if(aLength != -1){
alen2 = aLength;
if(alen2 (a.length - aOrigin))
alen2 = (a.length - aOrigin);
} else {
alen2 = (a.length - aOrigin);
}
if(bLength != -1){
blen2 = bLength;
if(blen2 (b.length - bOrigin))
blen2 = (b.length - bOrigin);
} else {
blen2 = (b.length - bOrigin);
}

Those three if-else blocks are identical. I'd move them into their own
little function. Then you'd just call them something like:

// make 'MyFunc()' return an type int.
int rlen2( MyFunc( rLength, length, rOrigin ) );
int alen2( MyFunc( aLength, a.length, aOrigin ) );
int blen2( MyFunc( bLength, b.length, bOrigin ) );
if(rOrigin+alen2 >= length)
alen2 = length-rOrigin;
if(rOrigin+blen2 >= length)
blen2 = length-rOrigin;
if(alen2 rlen2) alen2 = rlen2;
if(blen2 rlen2) blen2 = rlen2;
if(alen2 < 0) alen2 = 0;
if(blen2 < 0) blen2 = 0;
/* Set up the iterators */
di = digits.begin()+rOrigin; de = digits.end();

This is the first place you use 'di', 'de'. I'd do it this way.

std::vector<DIGIT>::iterator di( digits.begin() + rOrigin ),
de( digits.end() );
ai = a.digits.begin()+aOrigin; ae = a.digits.end();

std::vector<DIGIT>::const_iterator ai( a.digits.begin() + aOrigin ),
ae( a.digits.end() );
bi = b.digits.begin()+bOrigin; be = b.digits.end();

std::vector<DIGIT>::const_iterator bi( b.digits.begin() + bOrigin ),
be( b.digits.end() );
/* Now do the addition */
if(alen2 >= blen2){
/* Case 1: a is at least as long as b */
/* Add up a's and b's digits */

// for(i=0;i<blen2;i++,++di,++ai,++bi){

You do not use 'int i' outside the for() loops, so, declare them *in* the
for():

for( int i(0); i < blen2; ++i, ++di, ++ai, ++bi ){
tmp = *ai + *bi + carry;
if(carry) carry = (tmp <= *bi) ? 1 : 0;
else carry = (tmp < *bi) ? 1 : 0;
*di = tmp;
}
/* Now tackle the part of a that is longer than b */

// for(i=blen2;i<alen2;i++,++di,++ai){

for( int i( blen2 ); i < alen2; ++i, ++di, ++ai ){
tmp = *ai + carry;
if(carry)
carry = (tmp == 0) ? 1 : 0;
*di = tmp;
}
/* Zeroize the rest of this. */

// for(i=alen2;i<rlen2;i++,++di){

for( int i( alen2 ); i < rlen2; ++i, ++di ){
.... etc.

That's just my suggestion(s). Maybe with less clutter you'll see other
places to 'clean up'.
--
Bob R
POVrookie
Thanks for the answers. I'll see how it goes.
Nov 3 '07 #6
Well, here's my newest attempt, using the ideas you presented.
What do you think? Is it any better (nicer, cleaner, clearer, less
messy) than the original one?

---
/* Add two RawInts.
* Parameters:
* a: First RawInt
* b: Second RawInt
*
* Returns: carry.
*
* Operation: *this = a + b.
*/
DIGIT RawInt::rawAdd(const RawInt &a, const RawInt &b)
{
DIGIT tmp, carry(0);

/* Set up the iterators */
std::vector<DIGIT>::iterator ri(digits.begin()+origin);
std::vector<DIGIT>::const_iterator ai(a.digits.begin()+a.origin);
std::vector<DIGIT>::const_iterator bi(b.digits.begin()+b.origin);

/* Make sure we don't overrun this's digit buffer */
int alen2(MinInt(a.length_used, length_used));
int blen2(MinInt(b.length_used, length_used));

/* Now do the addition */
if(alen2 >= blen2)
{
/* Add up a's and b's digits */
for(int i(0);i<blen2;i++,++ri,++ai,++bi)
{
tmp = *ai + *bi + carry;
if(carry) carry = (tmp <= *bi) ? 1 : 0;
else carry = (tmp < *bi) ? 1 : 0;
*ri = tmp;
}

/* Now tackle the part of a that is longer than b */
for(int i(blen2);i<alen2;i++,++ri,++ai)
{
tmp = *ai + carry;
if(carry)
carry = (tmp == 0) ? 1 : 0;
*ri = tmp;
}

/* Zeroize the rest of this. */
for(int i(alen2);i<length_used;i++,++ri)
{
*ri = carry;
carry = 0;
}
} else {
/* Swap so that the first operand is larger than the second. */
return(this->rawAdd(b, a));
}

/* Done! Return any leftover carry. */
return(carry);
}
---

(Here, "MinInt" is a little macro that returns the smaller of
two integers.)

I have one extra question. There's also a subtraction routine,
which will be similar to the above. However, it is not so easy
to do the swap for when b is longer than a, as if one simply
were to call "rawSub(b, a)" that would give a 2's-complement
negative of the difference (because subtraction is not
commutative like addition, a - b != b - a in general), which
would then need to be negated, which takes longer. So then
wouldn't it be better to repeat the code in the first half of the
if?

Nov 4 '07 #7
On Nov 4, 1:35 am, mike3 <mike4...@yahoo.comwrote:
Well, here's my newest attempt, using the ideas you presented.
What do you think? Is it any better (nicer, cleaner, clearer, less
messy) than the original one?
I may have this one totally wrong, but this looks
like a possible candidate for transform, where
what you do with the carry could be encapsulated
in your function object. I still can't figure
out exactly what the rules are concerning the
carry, despite reading your comments, but perhaps
I'm just I bad observer :-). If I were you
I would draw a little diagram explaining what
the algo should do e.g:

Sequence (or digit a):
5 2 [9 7] 1 4
Sequence (or digit b):
8 [1 4] 2 9 0
Should give result:
....
[] indicative of the ranges concerned.

With regards to the code presented, see below:

DIGIT RawInt::rawAdd(const RawInt &a, const RawInt &b)
{
DIGIT tmp, carry(0);
Why is DIGIT in caps? I presume its a macro. If it
is a macro, why use a macro, if not, then you should
not use caps.
/* Set up the iterators */
std::vector<DIGIT>::iterator ri(digits.begin()+origin);
std::vector<DIGIT>::const_iterator ai(a.digits.begin()+a.origin);
std::vector<DIGIT>::const_iterator bi(b.digits.begin()+b.origin);
Perhaps use a member function that returns the origin (2
versions, one returning const_iterator for constant RawInt
and the other returning non const for non constant ...:
/* Make sure we don't overrun this's digit buffer */
int alen2(MinInt(a.length_used, length_used));
int blen2(MinInt(b.length_used, length_used));
Why not use std::min over here - I'll have to presume
MinInt does something special that differs from std::min.
/* Now do the addition */
if(alen2 >= blen2)
{
I would declare tmp and carry here, not at the top.
/* Add up a's and b's digits */
for(int i(0);i<blen2;i++,++ri,++ai,++bi)
Horizontal space makes things more readable, but opinions
may differ.
if(carry) carry = (tmp <= *bi) ? 1 : 0;
else carry = (tmp < *bi) ? 1 : 0;
Above could look like this:

if( carry ){ carry = (tmp <= *bi); }
else { carry = (tmp < *bi); }

or even:
carry = carry ? (tmp <= *bi) : (tmp < *bi);

Rationale:
the boolean expression is already sufficient for the assignment
/* Now tackle the part of a that is longer than b */
for(int i(blen2);i<alen2;i++,++ri,++ai)
{
tmp = *ai + carry;
if(carry)
carry = (tmp == 0) ? 1 : 0;
Ditto -simply: carry = (tmp == 0) would do.
/* Zeroize the rest of this. */
for(int i(alen2);i<length_used;i++,++ri)
{
*ri = carry;
carry = 0;
}
How about using std::fill here? Its perfect for zeroing.
In that case you would have to first carry carry over, then
fill the remainder. Regardless, the above is simple enough
to not require fill.

The idea that I've had (out of the box) was something in
the line of:

std::transform(
s1_digit_pos, s1_end,
s2_digit_pos,
result_digit_pos,
Op ); //Where Op is a binary operator.

<Opcould store the carry and implement it on the next item
in the sequence. <Opcould also contain state to indicate
whether an item should be added to the destination (or
result) sequence. transform makes a copy of <Op>, but
this does not matter as you could use something like
boost::ref to make the algo only use a reference to
Op. Eventually you have something like this (off
the top of my head). I'll use normal vectors for
simplicity.

#include <vector>
#include <functional>
#include <algorithm>
#include <boost/cref.hpp>

struct Op : std::binary_function<int, int, int>
{
Op( int digMax )
: digMax_( digMax ), carry_( 0 ){ }
int operator()( int s1v, int s2v )
{
int result( s1v + s2v + carry_ );
//I'm still not sure about when
// carry has effect, but for example
// lets assume when s1v + s2v n
carry_ = (s1v + s2v) digMax_;
return result;
}
const int digMax_;
int carry_;
};

struct RawDigit
{
void add(
const std::vector<int>& s1,
const std::vector<int&s2 );
std::vector<intmySeq_;
};

void RawDigit::add(
const std::vector<int>& s1, const std::vector<int&s2 )
{
Op op( 10 );
//For simplicity, lets assume s1 and s2 has
// same amount of items and this has more.
std::transform( s1.begin(), s1.end(), s2.begin(),
mySeq_.begin(), boost::ref( op ) );
//Add the carry...
mySeq.push_back( op.carry_ );
//or perhaps... addCarry( op.carry_ );
}

All said, one needs to evaluate whether the algorithmic
approach is necessarily simpler (more clear). I feel
it does isolate the transformation better. Nevertheless,
as someone has often pointed out, there is nothing wrong
with non-algorithmic approach, as long as the intent is
clear (you should not even need comments IMO, but that's
another argument).

Regards,

Werner
Nov 4 '07 #8

mike3 wrote in message...
Well, here's my newest attempt, using the ideas you presented.
What do you think? Is it any better (nicer, cleaner, clearer, less
messy) than the original one?
In my opinion it is better. More important, do *you* see improvement?
>
---
/* Add two RawInts.
* Parameters:
* a: First RawInt
* b: Second RawInt
* Returns: carry.
* Operation: *this = a + b.
*/
DIGIT RawInt::rawAdd(const RawInt &a, const RawInt &b){
DIGIT tmp, carry(0);

/* Set up the iterators */
std::vector<DIGIT>::iterator ri(digits.begin()+origin);
Review this and the use of 'ri' in the rest of this code. Is 'digits' a
member of RawInt', or <chokeglobal?
std::vector<DIGIT>::const_iterator ai(a.digits.begin()+a.origin);
std::vector<DIGIT>::const_iterator bi(b.digits.begin()+b.origin);

/* Make sure we don't overrun this's digit buffer */
int alen2(MinInt(a.length_used, length_used));
int blen2(MinInt(b.length_used, length_used));

/* Now do the addition */
if(alen2 >= blen2){
/* Add up a's and b's digits */
for(int i(0);i<blen2;i++,++ri,++ai,++bi){
I notice you post-inc the 'i'. Why?
[ if you test it, you'll see that you get the same result with pre-inc for
that situation. Pre-increment is faster, but, you would not even be able to
measure the diff on such a short loop (as above).]
tmp = *ai + *bi + carry;
if(carry) carry = (tmp <= *bi) ? 1 : 0;
else carry = (tmp < *bi) ? 1 : 0;
*ri = tmp;
}

/* Now tackle the part of a that is longer than b */
for(int i(blen2);i<alen2;i++,++ri,++ai){
tmp = *ai + carry;
if(carry)
carry = (tmp == 0) ? 1 : 0;
*ri = tmp;
}

/* Zeroize the rest of this. */
for(int i(alen2);i<length_used;i++,++ri){
*ri = carry;
carry = 0;
}
You might look into using std::fill() in place of that last loop. Or, if as
I suspect, you are not using 'ri', eliminate it from your code. [?? replace
'tmp' with '*ri'. ??]
[ example, taken from earlier...
Maybe?:
for( int i( blen2 ); i < alen2; ++i,++ri,++ai ){
*ri = *ai + carry;
if( carry )
carry = ( *ri == 0) ? 1 : 0;
// *ri = tmp;
}
]

For std::fill() it might look something like:
std::fill( ri, ri + ( length_used - alen2 - 1 ), 0 );
or:
std::fill( ri, digits.end(), 0 );
} else {
/* Swap so that the first operand is larger than the second. */
return(this->rawAdd(b, a));
}

/* Done! Return any leftover carry. */
return(carry);
}
---

(Here, "MinInt" is a little macro that returns the smaller of
two integers.)

I have one extra question. There's also a subtraction routine,
which will be similar to the above. However, it is not so easy
to do the swap for when b is longer than a, as if one simply
were to call "rawSub(b, a)" that would give a 2's-complement
negative of the difference (because subtraction is not
commutative like addition, a - b != b - a in general), which
would then need to be negated, which takes longer. So then
wouldn't it be better to repeat the code in the first half of the
if?
I'd get the code above working well, *and tested*, then concentrate on the
'subtraction routine', using what you've learned.

[ corrections welcome ]
--
Bob R
POVrookie
Nov 4 '07 #9
On Nov 4, 10:12 am, werasm <wer...@gmail.comwrote:
On Nov 4, 1:35 am, mike3 <mike4...@yahoo.comwrote:
Well, here's my newest attempt, using the ideas you presented.
What do you think? Is it any better (nicer, cleaner, clearer, less
messy) than the original one?

I may have this one totally wrong, but this looks
like a possible candidate for transform, where
what you do with the carry could be encapsulated
in your function object. I still can't figure
out exactly what the rules are concerning the
carry, despite reading your comments, but perhaps
I'm just I bad observer :-). If I were you
I would draw a little diagram explaining what
the algo should do e.g:

Sequence (or digit a):
5 2 [9 7] 1 4
Sequence (or digit b):
8 [1 4] 2 9 0
Should give result:
...
[] indicative of the ranges concerned.
What the thing does is take a.length_used worth of
digits starting at a.origin from a's buffer, adds that to
b.length_used worth of digits starting at b.origin from
b's buffer, and finally stores the result at "origin" (which
is in "this"), padding if necessary to make it as long as
"length_used" (also in "this"). So if you have
a = 12345 (NOT in base 10 in the real program, of course,
that is just for purposes of illustration), and b = 2241,
and "this" = 1111111, and you have a.length_used = 2,
a.origin = 1, b.length_used = 3, b.origin = 1,
this->length_used = 3, this->origin = 2, you get:

a + b = 12[34]5 + [224]1 = [34] + [224] = [34 + 224] = [258],

"this" = 11[111]11 -11[258]11 -1125811 =
"this" after the operation.

The parameters "origin" and "length_used" are never set
directly but instead by a special member function that
checks to ensure they don't exceed the bounds of the
buffer.

Why is the "carry" determination so odd? Because the
"base" is actually the maximum size of the type "DIGIT"
plus one, which should be a power of two, as this is
binary. So to find the carry, we have to find the wraparound
overflow.
With regards to the code presented, see below:
DIGIT RawInt::rawAdd(const RawInt &a, const RawInt &b)
{
DIGIT tmp, carry(0);

Why is DIGIT in caps? I presume its a macro. If it
is a macro, why use a macro, if not, then you should
not use caps.
"DIGIT" is just a data type (in this case unsigned 32-bit).
I use caps for emphasis.
/* Set up the iterators */
std::vector<DIGIT>::iterator ri(digits.begin()+origin);
std::vector<DIGIT>::const_iterator ai(a.digits.begin()+a.origin);
std::vector<DIGIT>::const_iterator bi(b.digits.begin()+b.origin);

Perhaps use a member function that returns the origin (2
versions, one returning const_iterator for constant RawInt
and the other returning non const for non constant ...:
That returns the origin? What's the point of that? The addition
routine is a member of RawInt, so why bother when you can
just access "origin" directly?
/* Make sure we don't overrun this's digit buffer */
int alen2(MinInt(a.length_used, length_used));
int blen2(MinInt(b.length_used, length_used));

Why not use std::min over here - I'll have to presume
MinInt does something special that differs from std::min.
No, it doesn't do anything different, just takes the minimum
of the two integers.
/* Now do the addition */
if(alen2 >= blen2)
{

I would declare tmp and carry here, not at the top.
OK.
/* Add up a's and b's digits */
for(int i(0);i<blen2;i++,++ri,++ai,++bi)

Horizontal space makes things more readable, but opinions
may differ.
Well, I guess I have a habit of making compact, short
lines.
if(carry) carry = (tmp <= *bi) ? 1 : 0;
else carry = (tmp < *bi) ? 1 : 0;

Above could look like this:

if( carry ){ carry = (tmp <= *bi); }
else { carry = (tmp < *bi); }

or even:
carry = carry ? (tmp <= *bi) : (tmp < *bi);
That's a lot neater.
Rationale:
the boolean expression is already sufficient for the assignment
/* Now tackle the part of a that is longer than b */
for(int i(blen2);i<alen2;i++,++ri,++ai)
{
tmp = *ai + carry;
if(carry)
carry = (tmp == 0) ? 1 : 0;

Ditto -simply: carry = (tmp == 0) would do.
Oh, since "true" = 1, and "false" = 0, so comparison
operators already return the required 1/0 we need,
right?
/* Zeroize the rest of this. */
for(int i(alen2);i<length_used;i++,++ri)
{
*ri = carry;
carry = 0;
}

How about using std::fill here? Its perfect for zeroing.
In that case you would have to first carry carry over, then
fill the remainder. Regardless, the above is simple enough
to not require fill.

The idea that I've had (out of the box) was something in
the line of:

std::transform(
s1_digit_pos, s1_end,
s2_digit_pos,
result_digit_pos,
Op ); //Where Op is a binary operator.

<Opcould store the carry and implement it on the next item
in the sequence. <Opcould also contain state to indicate
whether an item should be added to the destination (or
result) sequence. transform makes a copy of <Op>, but
this does not matter as you could use something like
boost::ref to make the algo only use a reference to
Op. Eventually you have something like this (off
the top of my head). I'll use normal vectors for
simplicity.
<snip snippet for brevity>
All said, one needs to evaluate whether the algorithmic
approach is necessarily simpler (more clear). I feel
it does isolate the transformation better. Nevertheless,
as someone has often pointed out, there is nothing wrong
with non-algorithmic approach, as long as the intent is
clear (you should not even need comments IMO, but that's
another argument).
Also, looking at your code, what happened to the "origin"
thing, anyway? I need that if I want to add bits and pieces
of the digit buffers, as is needed in my implementation
of huge floating point arithmetic using this.
Regards,

Werner

Nov 4 '07 #10
On Nov 4, 11:39 am, "BobR" <removeBadB...@worldnet.att.netwrote:
mike3 wrote in message...
Well, here's my newest attempt, using the ideas you presented.
What do you think? Is it any better (nicer, cleaner, clearer, less
messy) than the original one?

In my opinion it is better. More important, do *you* see improvement?
To me it looks better. I was just wondering what you thought.
>
---
/* Add two RawInts.
* Parameters:
* a: First RawInt
* b: Second RawInt
* Returns: carry.
* Operation: *this = a + b.
*/
DIGIT RawInt::rawAdd(const RawInt &a, const RawInt &b){
DIGIT tmp, carry(0);
/* Set up the iterators */
std::vector<DIGIT>::iterator ri(digits.begin()+origin);

Review this and the use of 'ri' in the rest of this code. Is 'digits' a
member of RawInt', or <chokeglobal?
It's a member, of course. A global? No way.
std::vector<DIGIT>::const_iterator ai(a.digits.begin()+a.origin);
std::vector<DIGIT>::const_iterator bi(b.digits.begin()+b.origin);
/* Make sure we don't overrun this's digit buffer */
int alen2(MinInt(a.length_used, length_used));
int blen2(MinInt(b.length_used, length_used));
/* Now do the addition */
if(alen2 >= blen2){
/* Add up a's and b's digits */
for(int i(0);i<blen2;i++,++ri,++ai,++bi){

I notice you post-inc the 'i'. Why?
[ if you test it, you'll see that you get the same result with pre-inc for
that situation. Pre-increment is faster, but, you would not even be able to
measure the diff on such a short loop (as above).]
'Cause I've gotten into the habit of post-incing in loops.
Like you said, the diff can't really be measured, since
the loop is going to be so short.
>
tmp = *ai + *bi + carry;
if(carry) carry = (tmp <= *bi) ? 1 : 0;
else carry = (tmp < *bi) ? 1 : 0;
*ri = tmp;
}
/* Now tackle the part of a that is longer than b */
for(int i(blen2);i<alen2;i++,++ri,++ai){
tmp = *ai + carry;
if(carry)
carry = (tmp == 0) ? 1 : 0;
*ri = tmp;
}
/* Zeroize the rest of this. */
for(int i(alen2);i<length_used;i++,++ri){
*ri = carry;
carry = 0;
}

You might look into using std::fill() in place of that last loop. Or, if as
I suspect, you are not using 'ri', eliminate it from your code. [?? replace
'tmp' with '*ri'. ??]
I suppose I could.
[ example, taken from earlier...
Maybe?:
for( int i( blen2 ); i < alen2; ++i,++ri,++ai ){
*ri = *ai + carry;
if( carry )
carry = ( *ri == 0) ? 1 : 0;
// *ri = tmp;
}
]

For std::fill() it might look something like:
std::fill( ri, ri + ( length_used - alen2 - 1 ), 0 );
or:
std::fill( ri, digits.end(), 0 );
} else {
/* Swap so that the first operand is larger than the second. */
return(this->rawAdd(b, a));
}
/* Done! Return any leftover carry. */
return(carry);
}
---
(Here, "MinInt" is a little macro that returns the smaller of
two integers.)
I have one extra question. There's also a subtraction routine,
which will be similar to the above. However, it is not so easy
to do the swap for when b is longer than a, as if one simply
were to call "rawSub(b, a)" that would give a 2's-complement
negative of the difference (because subtraction is not
commutative like addition, a - b != b - a in general), which
would then need to be negated, which takes longer. So then
wouldn't it be better to repeat the code in the first half of the
if?

I'd get the code above working well, *and tested*, then concentrate on the
'subtraction routine', using what you've learned.
It does work already now, by the way.
[ corrections welcome ]
--
Bob R
POVrookie

Nov 4 '07 #11
LR
mike3 wrote:
Well, here's my newest attempt, using the ideas you presented.
What do you think? Is it any better (nicer, cleaner, clearer, less
messy) than the original one?
A little bit, yes.

/* Now do the addition */
if(alen2 >= blen2)
{
/* Add up a's and b's digits */
for(int i(0);i<blen2;i++,++ri,++ai,++bi)
{
tmp = *ai + *bi + carry;
if(carry) carry = (tmp <= *bi) ? 1 : 0;
else carry = (tmp < *bi) ? 1 : 0;
*ri = tmp;
}

/* Now tackle the part of a that is longer than b */
for(int i(blen2);i<alen2;i++,++ri,++ai)
{
tmp = *ai + carry;
if(carry)
carry = (tmp == 0) ? 1 : 0;
*ri = tmp;
}

/* Zeroize the rest of this. */
for(int i(alen2);i<length_used;i++,++ri)
{
*ri = carry;
carry = 0;
}
} else {
/* Swap so that the first operand is larger than the second. */
return(this->rawAdd(b, a));
}

/* Done! Return any leftover carry. */
return(carry);
}
I have one extra question. There's also a subtraction routine,
which will be similar to the above. However, it is not so easy
to do the swap for when b is longer than a, as if one simply
were to call "rawSub(b, a)" that would give a 2's-complement
negative of the difference (because subtraction is not
commutative like addition, a - b != b - a in general), which
would then need to be negated, which takes longer. So then
wouldn't it be better to repeat the code in the first half of the
if?
No. I don't think so.

// do that first loop
const int start0 = 0;
const int end0 = alen < blen ? alen : blen;
for(int i=start0; i<end0; i++, ..... ) ....
// second loop
const int start1 = alen < blen ? alen : blen;
const int end1 = alen < blen ? blen : alen;
std::vector<DIGIT>::const_iterator &xi = alen < blen ? bi : ai;
for(int i=start1; i<end1; i++,xi++) ....
or
for(int i=start1; i<end1; ++i,++xi) ....
// third loop
... etc...

I'm pretty certain that I don't have all those right. You'll have to
figure out the correct values.

And I agree with whomever wrote that you ought to change all those
if(carry) things to something like..
carry = (carry == 1 && tmp == 0) ? 0 : 1

Another approach to the above, but perhaps less efficient would be to
normalize the numbers first and then add or subtract them.

But of course, YMWV.

LR

Nov 4 '07 #12
On Nov 4, 11:39 am, "BobR" <removeBadB...@worldnet.att.netwrote:
mike3 wrote in message...
<snip>

Alright, now all the suggestions have been put into effect, and
this is the final result:

---
/* Add two RawInts.
* Parameters:
* a: First RawInt
* b: Second RawInt
*
* Returns: carry.
*
* Operation: *this = a + b.
*/
DIGIT RawInt::rawAdd(const RawInt &a, const RawInt &b)
{
/* Set up the iterators */
std::vector<DIGIT>::iterator ri(digits.begin()+origin);
std::vector<DIGIT>::const_iterator ai(a.digits.begin()+a.origin);
std::vector<DIGIT>::const_iterator bi(b.digits.begin()+b.origin);

/* Make sure we don't overrun this's digit buffer */
int alen2(min(a.length_used, length_used));
int blen2(min(b.length_used, length_used));

/* Now do the addition */
if(alen2 >= blen2)
{
DIGIT tmp, carry(0);

/* Add up a's and b's digits */
for(int i(0);i<blen2;++i,++ri,++ai,++bi)
{
tmp = *ai + *bi + carry;
carry = carry ? (tmp <= *bi) : (tmp < *bi);
*ri = tmp;
}

/* Now tackle the part of a that is longer than b */
for(int i(blen2);i<alen2;++i,++ri,++ai)
{
tmp = *ai + carry;
carry = carry ? (tmp == 0) : 0;
*ri = tmp;
}

/* Zeroize the rest of this and carry over the remaining
* carry.
*/
if(alen2 < length_used)
{
std::fill(ri, ri + (length_used - alen2 - 1), 0);
*ri = carry; carry = 0;
}

/* Return carry */
return(carry);
} else {
/* Swap and use the above code */
return(this->rawAdd(b, a));
}
}
---

That's pretty good, now ain't it? I think so. It's been
tested, and it seems to work OK.

Nov 5 '07 #13
On Nov 4, 2:20 pm, LR <lr...@superlink.netwrote:
mike3 wrote:
<snip>

Well, here's the subtraction routine:

---

/* Subtract two RawInts.
* Parameters:
* a: First RawInt
* b: Second RawInt
* dss_r: Pointer to a DSS that specifies the digit substring of
* this to store diff in. If NULL, this's full digit
string
* is used.
* dss_a: Pointer to a DSS that specifies the digit substring of
* a to have b subtracted from. If NULL, a's full digit
string
* is used.
* dss_b: Pointer to a DSS that specifies the digit substring of
* b to be subtracted from a. If NULL, b's full digit
string is
* used.
*
* Returns: borrow.
*
* Operation: *this = a - b.
*/
DIGIT RawInt::rawSub(const RawInt &a, const RawInt &b,
DSS *dss_r, DSS *dss_a, DSS *dss_b)
{
/* Set up the iterators */
std::vector<DIGIT>::iterator ri(digits.begin());
std::vector<DIGIT>::const_iterator ai(a.digits.begin());
std::vector<DIGIT>::const_iterator bi(b.digits.begin());

/* Use information in the DSSes, if applicable,
* to restrict the operation to the specified substrings
* of this's, a's, and b's digits.
*/
int rlen(length), alen(a.length), blen(b.length);
if(dss_r) dss_r->Apply(&ri, &rlen);
if(dss_a) dss_a->Apply(&ai, &alen);
if(dss_b) dss_b->Apply(&bi, &blen);

/* Make sure we don't overrun this's digit buffer */
int alen2(min(alen, rlen));
int blen2(min(blen, rlen));

/* Figure out which of a or b is longer. */
int smallerlen(min(alen2, blen2));
int largerlen(max(alen2, blen2));
std::vector<DIGIT>::const_iterator xi = alen < blen ? bi : ai;
int larger = (alen < blen) ? 1 : 0;

/* Now do the subtraction */
DIGIT tmp, borrow(0);

/* Subtract a's and b's digits */
for(int i(0);i<smallerlen;++i,++ri,++ai,++bi,++xi)
{
tmp = *ai - *bi - borrow;
borrow = borrow ? (*ai <= *bi) : (*ai < *bi);
*ri = tmp;
}

/* Now tackle the part of a/b that is longer than b/a */
for(int i(smallerlen);i<largerlen;++i,++ri,++xi)
{
if(larger == 0) /* a is larger */
{
tmp = *xi - borrow;
borrow = borrow ? (*xi == 0) : 0;
} else { /* b is larger */
tmp = 0 - *xi - borrow;
borrow = borrow ? (0 <= *xi) : (0 < *xi);
}

*ri = tmp;
}

/* Ripple borrow */
if(rlen largerlen)
std::fill(ri, ri + (rlen - largerlen - 1), 0 - borrow);

/* Return borrow */
return(borrow);
}

---

I've broke the habit of using that awful "if carry" block, as you
can see.

As you might notice, I've changed the location of the "origin"/
"length_used" parameters from being inside the RawInts to
being in a little wrapper class of their own called "DSS" (for "digit
substring specifier"). That allows one to keep the input BigFloats
as "const" in the floating point implementation, since one
does not need to tinker with internal pointers. This helps make
it more foolproof. It allows the routines to be able to handle
in-place arithmetic without buffering, which saves a little time
and makes the code a little nicer. And it avoids constantly having
to call a special routine to reset the pointers after we get done.
Finally, it guards against forgetting to reset internal pointers when
one needs to use a different part of the numbers, or even
the numbers "as is" without using partial strings of their
digits, further foolproofing the raw integer package. One can just
specify which parts one needs to use on every operation. Overall,
I just think it's better that way and easier to use. Was this a good
decision?

Nov 5 '07 #14
On Nov 4, 8:25 pm, mike3 <mike4...@yahoo.comwrote:
a + b = 12[34]5 + [224]1 = [34] + [224] = [34 + 224] = [258],

"this" = 11[111]11 -11[258]11 -1125811 =
"this" after the operation.
OK, thanks.
With regards to the code presented, see below:
DIGIT RawInt::rawAdd(const RawInt &a, const RawInt &b)
{
DIGIT tmp, carry(0);
Why is DIGIT in caps? I presume its a macro. If it
is a macro, why use a macro, if not, then you should
not use caps.

"DIGIT" is just a data type (in this case unsigned 32-bit).
I use caps for emphasis.
Then I accept you made it caps for the example, as even to
emphasize using CAPS is a no-no in C++ as it is reserved for
macros.
/* Set up the iterators */
std::vector<DIGIT>::iterator ri(digits.begin()+origin);
std::vector<DIGIT>::const_iterator ai(a.digits.begin()+a.origin);
std::vector<DIGIT>::const_iterator bi(b.digits.begin()+b.origin);
Perhaps use a member function that returns the origin (2
versions, one returning const_iterator for constant RawInt
and the other returning non const for non constant ...:

That returns the origin? What's the point of that? The addition
routine is a member of RawInt, so why bother when you can
just access "origin" directly?
By calling an inline member you can make a small improvement that
prevents you from duplicating the exact code three times. Therefore
instead of using...

std::vector<DIGIT>::iterator ri(digits.begin()+origin);
std::vector<DIGIT>::const_iterator ai(a.digits.begin()+a.origin);
std::vector<DIGIT>::const_iterator bi(b.digits.begin()+b.origin);

....use:

std::vector<DIGIT>::iterator ri( originIter() );
std::vector<DIGIT>::const_iterator ai( a.originIter() );
std::vector<DIGIT>::const_iterator bi( b.originIter() );

where originIter is simply defined like this:

std::vector<DIGIT>::iterator originIter() const{ return digits.begin()
+origin; }

- It looks less cluttered and is clearer.
- If you decide to change something later, you change it
in one place( not that you would, but this is stylistic
answer to your question of making it better.
No, it doesn't do anything different, just takes the minimum
of the two integers.
The use std::min.
Well, I guess I have a habit of making compact, short
lines.
I'm not to phased about this, but certain bureaucrats
would be ;-).
Ditto -simply: carry = (tmp == 0) would do.

Oh, since "true" = 1, and "false" = 0, so comparison
operators already return the required 1/0 we need,
right?
Exactly.
Also, looking at your code, what happened to the "origin"
thing, anyway? I need that if I want to add bits and pieces
of the digit buffers, as is needed in my implementation
of huge floating point arithmetic using this.
I did not duplicate your code (or even requirements).
I presented another idea. It is up to you to use
it if you want to. The idea is to use transform
to do the transformation, and then simply carry
the carry over after the transformations were performed.
Obviously, the iterators in the transformation would
be obtained from getOrigin(). The idea was out of the
box and just a presentation of what you could do as
alternative. What I like about the alternative is that
it emphasizes (or localizes) the translation to the
overloaded op(), but I think this is debatable. You
would have to post the alternative implementation if
you want us to scrutinize more. All said, what you've
ended up with already looks much better than what you've
started with.

Kind regards,

Werner

Nov 5 '07 #15
On Nov 5, 1:30 am, werasm <wer...@gmail.comwrote:
On Nov 4, 8:25 pm, mike3 <mike4...@yahoo.comwrote:
a + b = 12[34]5 + [224]1 = [34] + [224] = [34 + 224] = [258],
"this" = 11[111]11 -11[258]11 -1125811 =
"this" after the operation.

OK, thanks.
With regards to the code presented, see below:
DIGIT RawInt::rawAdd(const RawInt &a, const RawInt &b)
{
DIGIT tmp, carry(0);
Why is DIGIT in caps? I presume its a macro. If it
is a macro, why use a macro, if not, then you should
not use caps.
"DIGIT" is just a data type (in this case unsigned 32-bit).
I use caps for emphasis.

Then I accept you made it caps for the example, as even to
emphasize using CAPS is a no-no in C++ as it is reserved for
macros.
Geez, I guess I didn't know of all these Conventions they've
got for naming/capitalization. What should it be? "Digit", "digit",
"digiT"? I was actually using "DIGIT" in my program, a
real program, by the way! Now that you mentioned it I guess
I'd better change it. But what should it be?
/* Set up the iterators */
std::vector<DIGIT>::iterator ri(digits.begin()+origin);
std::vector<DIGIT>::const_iterator ai(a.digits.begin()+a.origin);
std::vector<DIGIT>::const_iterator bi(b.digits.begin()+b.origin);
Perhaps use a member function that returns the origin (2
versions, one returning const_iterator for constant RawInt
and the other returning non const for non constant ...:
That returns the origin? What's the point of that? The addition
routine is a member of RawInt, so why bother when you can
just access "origin" directly?

By calling an inline member you can make a small improvement that
prevents you from duplicating the exact code three times. Therefore
instead of using...

std::vector<DIGIT>::iterator ri(digits.begin()+origin);
std::vector<DIGIT>::const_iterator ai(a.digits.begin()+a.origin);
std::vector<DIGIT>::const_iterator bi(b.digits.begin()+b.origin);

...use:

std::vector<DIGIT>::iterator ri( originIter() );
std::vector<DIGIT>::const_iterator ai( a.originIter() );
std::vector<DIGIT>::const_iterator bi( b.originIter() );

where originIter is simply defined like this:

std::vector<DIGIT>::iterator originIter() const{ return digits.begin()
+origin; }

- It looks less cluttered and is clearer.
- If you decide to change something later, you change it
in one place( not that you would, but this is stylistic
answer to your question of making it better.
Now I've done that. I also moved the "origin"/"length_used"
parameters into a separate wrapper class (considerations
of the floating point library this is for suggested this approach
was better) and one of it's functions applies the origin value
to an iterator.
No, it doesn't do anything different, just takes the minimum
of the two integers.

The use std::min.
I've done that now, too.
Well, I guess I have a habit of making compact, short
lines.

I'm not to phased about this, but certain bureaucrats
would be ;-).
Who's that, anyway? You mean whoever comes up with
all these conventions?
Ditto -simply: carry = (tmp == 0) would do.
Oh, since "true" = 1, and "false" = 0, so comparison
operators already return the required 1/0 we need,
right?

Exactly.
And that's standard, right? That's why I was a
little weary of using it -- I wasn't sure if it was
standard or not.
Also, looking at your code, what happened to the "origin"
thing, anyway? I need that if I want to add bits and pieces
of the digit buffers, as is needed in my implementation
of huge floating point arithmetic using this.

I did not duplicate your code (or even requirements).
I presented another idea. It is up to you to use
it if you want to. The idea is to use transform
to do the transformation, and then simply carry
the carry over after the transformations were performed.
Obviously, the iterators in the transformation would
be obtained from getOrigin(). The idea was out of the
box and just a presentation of what you could do as
alternative. What I like about the alternative is that
it emphasizes (or localizes) the translation to the
overloaded op(), but I think this is debatable. You
would have to post the alternative implementation if
you want us to scrutinize more. All said, what you've
ended up with already looks much better than what you've
started with.
Alright. Is the "std::transform" method preferred though
because it allows one to package the little addition/
subtraction cores into neat little functions that you can
simply reference when you need to use those operations?
But I'd like to know: Is using "std::transform" slower than
using the "hardwired" approach I've got there now? If
not, it shouldn't really matter since I'm going to have
special "hardwired" functions for the time-critical parts
of the program, but I'm still curious.
Kind regards,

Werner

Nov 5 '07 #16
On Nov 5, 10:03 pm, mike3 <mike4...@yahoo.comwrote:

Geez, I guess I didn't know of all these Conventions they've
got for naming/capitalization. What should it be? "Digit", "digit",
"digiT"? I was actually using "DIGIT" in my program, a
real program, by the way! Now that you mentioned it I guess
I'd better change it. But what should it be?
There has been long discussions in this group about naming
conventions, and especially using capitals in the past.
It boils down to CAPITALS being reserved for macros, and
that is inherited from "C", if I understood correctly.

I think the standards says you may not prefix with
_ (underscore), and the capital reservation for macros
are just known convention - I'm not sure whether the standard
mentions anything about that.

I usually use CamelCase for class names. For function
and variable names I have my first syllable lower case,
but this is not that important, as long as you are
consistent. I more or less follow the convention spelled
out by Andrei Alexandrescu and Herb Sutter, but not all
people like that (I'm sure you'll find it if you look it up).

It is partially described in Andrei's book "Modern C++ Design".
Now I've done that. I also moved the "origin"/"length_used"
parameters into a separate wrapper class (considerations
of the floating point library this is for suggested this approach
was better) and one of it's functions applies the origin value
to an iterator.
As long as you believe it is better :-).
Ditto -simply: carry = (tmp == 0) would do.
And that's standard, right? That's why I was a
little weary of using it -- I wasn't sure if it was
standard or not.
Of course.
Alright. Is the "std::transform" method preferred though
because it allows one to package the little addition/
subtraction cores into neat little functions that you can
simply reference when you need to use those operations?
Not necessarily, but that is why I prefer it.
But I'd like to know: Is using "std::transform" slower than
using the "hardwired" approach I've got there now?
I don't know, I've not tested it, but perhaps it could be
as transform returns by value. It all depends on how the
compiler generates the code, but I would imagine it to
be slightly slower. All said, speed of execution is rarely
a measure of how good code is. Maintainability, clarity and
scalability is most often more important. Speed only becomes
a factor when it becomes an issue. Other topic, though.

Also, you have to believe it's better. If you think your
approach is better, good. I just gave you an out of the
box thought - a different approach. I did not find your
initial approach attractive because it was not obvious.
OTOH, some things need not be obvious.
If
not, it shouldn't really matter since I'm going to have
special "hardwired" functions for the time-critical parts
of the program, but I'm still curious.
Yes, 80/20 rule. 20 % of the code executes 80% of the time
(perhaps even less, I'd imagine).

Regards,

Werner

Nov 6 '07 #17
On Nov 6, 9:31 am, werasm <wer...@gmail.comwrote:
On Nov 5, 10:03 pm, mike3 <mike4...@yahoo.comwrote:
Geez, I guess I didn't know of all these Conventions they've
got for naming/capitalization. What should it be? "Digit", "digit",
"digiT"? I was actually using "DIGIT" in my program, a
real program, by the way! Now that you mentioned it I guess
I'd better change it. But what should it be?
There has been long discussions in this group about naming
conventions, and especially using capitals in the past.
It boils down to CAPITALS being reserved for macros, and
that is inherited from "C", if I understood correctly.
I think the standards says you may not prefix with _
(underscore), and the capital reservation for macros are just
known convention - I'm not sure whether the standard mentions
anything about that.
First, it *is* a convention. As far as the language goes,
there's no problem. But it seems to be a more or less universal
convention, so it's better to stick with it. (Traditionally,
some conventions also used all caps for constants. But one
man's constant is another man's variable, and you really do want
a convention in which macro names are distinct, so it's probably
better to avoid that tradition.)
I usually use CamelCase for class names. For function
and variable names I have my first syllable lower case,
but this is not that important, as long as you are
consistent. I more or less follow the convention spelled
out by Andrei Alexandrescu and Herb Sutter, but not all
people like that (I'm sure you'll find it if you look it up).
Naming conventions are used to establish (meta-)namespaces. As
a general rule:

-- macros must be separate, since they don't follow any of the
rules other symbols follow, and

-- it's probably best if type names are different from other
things as well, since C++ parses differently depending on
whether a symbol is a type name or not.

I'd pretty much consider the first an absolute. And I'd add
that macro names normally require some sort of library specific
prefix, to avoid conflicts.

In an ideal world, the convention for the rest would be more or
less:

-- type names are unqualified nouns,
-- variable and constant names are qualified nouns, or in some
cases adjectives, and
-- function names are verbs or verb phrases, predicate
functions use the verb "to be".

Thus, "color" is a type, since it is an unqualified noun,
"background_color" or "red" are variables or constants, and
"set_color" or "is_red" would be functions.

In practice, it's rarely that clear: if the functions represent
an attribute of the class, for example, I'll use the qualified
noun for them (which means I need something else for the actual
member they set or get). Is automate_state a qualified noun, or
an unqualified one? Is "start" or "empty" a verb, or an
adjective?

So I tend to recommend an additional typographical convention to
distinguish types and non-types. An initial capital seems to be
quasi universal for types. (Some conventions use it for
functions as well: I don't particularly like it, but since
function names are generally verbs, and typenames nouns, it
probably doesn't cause too many problems in practice.)

The remaining point is how to separate words in a symbol: the
two conventions are camel case and using an underscore. Camel
case is ugly, and less readable, but it seems to be by far the
most used.
It is partially described in Andrei's book "Modern C++ Design".
It goes back a lot further than that. It was used in the CCITT
standards, long before 1990. (The fact that it was used in the
CCITT standards meant that it was quasi-universal in European
telecoms.) It's also the Java standard (although Java uses all
caps for constants---they don't have to worry about macros, but
of course, when the constant becomes a variable, as constants
are wont to do, you have the choice of modifying everywhere or
being inconsistent). I find it esthetically very disagreeable,
but having worked for many, many years in telecoms (in Europe),
it's more or less second nature to me.

Of course, on any given contract, I use the conventions my
client has adopted.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Nov 6 '07 #18
On Nov 6, 1:31 am, werasm <wer...@gmail.comwrote:
On Nov 5, 10:03 pm, mike3 <mike4...@yahoo.comwrote:
Geez, I guess I didn't know of all these Conventions they've
got for naming/capitalization. What should it be? "Digit", "digit",
"digiT"? I was actually using "DIGIT" in my program, a
real program, by the way! Now that you mentioned it I guess
I'd better change it. But what should it be?

There has been long discussions in this group about naming
conventions, and especially using capitals in the past.
It boils down to CAPITALS being reserved for macros, and
that is inherited from "C", if I understood correctly.

I think the standards says you may not prefix with
_ (underscore), and the capital reservation for macros
are just known convention - I'm not sure whether the standard
mentions anything about that.
But other than that, what should one use for the type?
Would any of "digit", "Digit", "DigiT", "digiT" be
acceptable, then? And should ALL macros be named
with CAPITALS, then?
I usually use CamelCase for class names. For function
and variable names I have my first syllable lower case,
but this is not that important, as long as you are
consistent. I more or less follow the convention spelled
out by Andrei Alexandrescu and Herb Sutter, but not all
people like that (I'm sure you'll find it if you look it up).

It is partially described in Andrei's book "Modern C++ Design".
$50 for that book?! I need a good source of money... Heh,
which is why I'm trying to make these programs.
Now I've done that. I also moved the "origin"/"length_used"
parameters into a separate wrapper class (considerations
of the floating point library this is for suggested this approach
was better) and one of it's functions applies the origin value
to an iterator.

As long as you believe it is better :-).
It helped, since it makes it easier to program the floating
point stuff, and it is also makes it a lot harder to forget
resetting internal pointers in the RawInts when you
want to use different parts of the numbers or even use
all of 'em just like in "ordinary" arithmetic.
Ditto -simply: carry = (tmp == 0) would do.
And that's standard, right? That's why I was a
little weary of using it -- I wasn't sure if it was
standard or not.

Of course.
Good.
Alright. Is the "std::transform" method preferred though
because it allows one to package the little addition/
subtraction cores into neat little functions that you can
simply reference when you need to use those operations?

Not necessarily, but that is why I prefer it.
I suppose so. It does sound like it would make it easier,
as then you can change how you do those often-used functions
without changing everything that uses them.
But I'd like to know: Is using "std::transform" slower than
using the "hardwired" approach I've got there now?

I don't know, I've not tested it, but perhaps it could be
as transform returns by value. It all depends on how the
compiler generates the code, but I would imagine it to
be slightly slower. All said, speed of execution is rarely
a measure of how good code is. Maintainability, clarity and
scalability is most often more important. Speed only becomes
a factor when it becomes an issue. Other topic, though.
Well, in my application, it needs to be very fast. However,
there's only one place where the speed matters -- actually
drawing the fractals. That's where the calculations have to
be really, really fast. But for that, I have a separate set of
"fast" routines with various restrictions (ex. all operands
*and* the output buffer must have the same length (precision)).
However this (what I've been discussing right now) is for
the general bignum package used for all other bignum
manipulations.
Also, you have to believe it's better. If you think your
approach is better, good. I just gave you an out of the
box thought - a different approach. I did not find your
initial approach attractive because it was not obvious.
OTOH, some things need not be obvious.
If
not, it shouldn't really matter since I'm going to have
special "hardwired" functions for the time-critical parts
of the program, but I'm still curious.

Yes, 80/20 rule. 20 % of the code executes 80% of the time
(perhaps even less, I'd imagine).

Regards,

Werner

Nov 6 '07 #19
On Nov 6, 9:24 pm, mike3 <mike4...@yahoo.comwrote:

But other than that, what should one use for the type?
Would any of "digit", "Digit", "DigiT", "digiT" be
acceptable, then? And should ALL macros be named
with CAPITALS, then?
I presume by the type you mean the class (or is
DIGIT in actual fact a template argument). It
did not seem like a template argument to me.

e.g:

Your function did not look like this:

template <class DigitT>
void SomeObject( /*...*/ )
{
std::vector<DigitTblah;
}

As I understood it DIGIT was a class. If that is the
case (it being a class), refer to James's detailed
(and sound) reply. If it was indeed a template argument
like DigitT here above, I personally would have called it
DigitT (or Digit_T). All caps are reserved for macros. A
capital here and there does no harm, as long as the identifier
does not only consist of capitals (just in case you misunderstood).
$50 for that book?! I need a good source of money... Heh,
which is why I'm trying to make these programs.
Good book - worth the money (IMHO).
I suppose so. It does sound like it would make it easier,
as then you can change how you do those often-used functions
without changing everything that uses them.
That is more or less my rationale too. If you have an
implementation where you've used transform, please post it
(under the previous topic) for interest sake (or mail me).

Regards,

Werner

Nov 6 '07 #20
werasm wrote:
>
All caps are reserved for macros. A
capital here and there does no harm, as long as the identifier
does not only consist of capitals (just in case you misunderstood).
No, there is no such restriction. An argument can be made that all caps
*SHOULD BE* reserved for macros (an argument that I agree with), but
there is no such restriction defined in the language.

Nov 6 '07 #21
On Nov 6, 11:37 pm, red floyd <no.s...@here.dudewrote:
werasm wrote:
All caps are reserved for macros. A
capital here and there does no harm, as long as the identifier
does not only consist of capitals (just in case you misunderstood).

No, there is no such restriction. An argument can be made that all caps
*SHOULD BE* reserved for macros (an argument that I agree with), but
there is no such restriction defined in the language.
The point was from the start that it is generally accepted as
convention. Because of this, not adhering to this convention
makes you run the risk. All said, lowercase macros also exists,
but it is quite rare (min being one irritating one). I recently
came across a macro named "index" in a library that gave me some
problems. The argument is therefore indeed "SHOULD BE".

Regards,

Werner

Nov 7 '07 #22
On Nov 7, 1:13 am, werasm <wer...@gmail.comwrote:
On Nov 6, 11:37 pm, red floyd <no.s...@here.dudewrote:
werasm wrote:
All caps are reserved for macros. A
capital here and there does no harm, as long as the identifier
does not only consist of capitals (just in case you misunderstood).
No, there is no such restriction. An argument can be made that all caps
*SHOULD BE* reserved for macros (an argument that I agree with), but
there is no such restriction defined in the language.

The point was from the start that it is generally accepted as
convention. Because of this, not adhering to this convention
makes you run the risk. All said, lowercase macros also exists,
but it is quite rare (min being one irritating one). I recently
came across a macro named "index" in a library that gave me some
problems. The argument is therefore indeed "SHOULD BE".

Regards,

Werner
And that's why I want to know what all these conventions are.
What therefore should be used instead of all caps?

Nov 8 '07 #23
On Nov 8, 9:12 pm, "Victor Bazarov" <v.Abaza...@comAcast.netwrote:

[...]
Then there is one with the name followed
by '_t' for all types (Standard library does not really follow it,
only for primitive types, like size_t, wchar_t, ptrdiff_t, etc.)
The C standard uses _t for typedef's. The C++ uses it for
compatibility with C (even in the case of wchar_t, which isn't a
typedef in C++).

Because the only way in C to get a user symbol which is the name
of a type is with a typedef, some people interpret the _t in C
as the convention for a user defined type.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Nov 9 '07 #24

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

5
by: Abby Lee | last post by:
My code does what I want (works unless there is a lot of volume...works for this month cause not a lot of items marked paid yet...) but the page times out for last month because there is just so...
4
by: Nobody | last post by:
Lets say I have a class that is only available if a specific DLL (a.dll) is present. I can't link to that DLL through lib files or my app will fail on any machine that doesn't have a.dll. So I...
36
by: Ben Justice | last post by:
For a program in c, I need some random numbers for a system were people are placing bets. This is not a commerical project btw. Generally, I tend to rely on things from the standard library,...
3
by: darklight | last post by:
Q: write a program so that it excepts six even numbers or until the number 99 is entered please explain why one is better than the other, if that is the case. A1: /*EX6-1.C TO COUNT AND...
85
by: masood.iqbal | last post by:
I know that this topic may inflame the "C language Taleban", but is there any prospect of some of the neat features of C++ getting incorporated in C? No I am not talking out the OO stuff. I am...
23
by: JoeC | last post by:
I am a self taught programmer and I have figured out most syntax but desigining my programs is a challenge. I realize that there are many ways to design a program but what are some good rules to...
22
by: JoeC | last post by:
I am working on another game project and it is comming along. It is an improvment over a previous version I wrote. I am trying to write better programs and often wonder how to get better at...
10
by: weidongtom | last post by:
Hi, I was working on the following problem and I managed to get a solution, but it's too slow. And I am still in search for a better algorithm. Please enlighten me. ...
5
by: Tony WONG | last post by:
i have a "for next" program to detect workstations ON/OFF status by ping. i wish the program restart after 60 seconds after completion of for next loop. at present, i use...
1
by: CloudSolutions | last post by:
Introduction: For many beginners and individual users, requiring a credit card and email registration may pose a barrier when starting to use cloud servers. However, some cloud server providers now...
0
by: Faith0G | last post by:
I am starting a new it consulting business and it's been a while since I setup a new website. Is wordpress still the best web based software for hosting a 5 page website? The webpages will be...
0
by: taylorcarr | last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.