Please comment on my integer to string code.

M

manoj1978

Hi All,
I wrote the following to print an integer in its string
representation for base -36 to 36.
Please comment on this code.

#include <iostream>
#include <string>

using std::abs;
using std::cout;
using std::endl;
using std::reverse;
using std::string;

char charTable[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

void printBase(int i, int base)
{
if ((abs(base) <= 1) || (abs(base) > 36) || (i == 0))
{
cout << "0" << endl;
return;
}

string str = "";
if ((base > 0) && (i < 0))
{
str += "-";
i = -i;
}

do {
if (i % base >= 0)
{
str += charTable[i % base];
i = i / base;
} else {
str += charTable[i % base - base];
i = i /base + 1;
}
} while(i);
reverse(str.begin(), str.end());
cout << str << endl;
}

int main()
{
for(int i = -55; i <= 55; i++)
{
cout << i << " = ";
printBase(i,-10);
}
}

Thanks and Regards,
manoj.
 
R

red floyd

Hi All,
I wrote the following to print an integer in its string
representation for base -36 to 36.

> [code redacted]

How do you display numbers in a *negative* base?????
 
M

manoj1978

red said:
Hi All,
I wrote the following to print an integer in its string
representation for base -36 to 36.

[code redacted]

How do you display numbers in a *negative* base?????

Consider negadecimal.

27 will be 187

since 187 = 1 * -10 * -10 + 8 * -10 + 7 = 27.
 
P

Phlip

int main()
{
for(int i = -55; i <= 55; i++)
{
cout << i << " = ";
printBase(i,-10);
}
}

Unit tests are the most important aspect of programming. Your printBase
should return a string, so you can test it, and so it won't "couple" with
its environment. Here you have some cout outside printBase, and some
inside. Move all outside, then write unit tests like these:

assert("whatever" == printBase(-55);
assert("whatever" == printBase(-55);
assert("whatever" == printBase(-55);
assert("whatever" == printBase(-55);

Now each time you change the program, run those tests. If you make certain
kinds of mistakes (but not any mistake) the test will catch you, and you
can use Undo to get rid of the mistake. Undo is much more efficient than
debugging.
 
M

manoj1978

Phlip said:
Unit tests are the most important aspect of programming. Your printBase
should return a string, so you can test it, and so it won't "couple" with
its environment. Here you have some cout outside printBase, and some
inside. Move all outside, then write unit tests like these:

assert("whatever" == printBase(-55);
assert("whatever" == printBase(-55);
assert("whatever" == printBase(-55);
assert("whatever" == printBase(-55);

Now each time you change the program, run those tests. If you make certain
kinds of mistakes (but not any mistake) the test will catch you, and you
can use Undo to get rid of the mistake. Undo is much more efficient than
debugging.
Hi Philip,
Thanks.I will surely keep this in mind.
This time I had the expected results in a file.
I was redirecting the output and doing file compare.
Regards,
manoj.
 
V

Victor Bazarov

Phlip said:
Unit tests are the most important aspect of programming. Your printBase
should return a string, so you can test it, and so it won't "couple" with
its environment. Here you have some cout outside printBase, and some
inside. Move all outside, then write unit tests like these:

assert("whatever" == printBase(-55);
assert("whatever" == printBase(-55);
assert("whatever" == printBase(-55);
assert("whatever" == printBase(-55);

Why four of them in a row? Are there side effects expected that affect
the behaviour of the function?
Now each time you change the program, run those tests. If you make certain
kinds of mistakes (but not any mistake) the test will catch you, and you
can use Undo to get rid of the mistake. Undo is much more efficient than
debugging.

V
 
P

Phlip

Victor said:
Why four of them in a row?  Are there side effects expected that affect
the behaviour of the function?

I forgot to edit:

assert("whatever" == printBase(-55));
assert("whatever" == printBase(-54));
assert("whatever" == printBase( -1));
assert("whatever" == printBase( 0));
...

The next, more complex topic is: How to make sure the tests use enough
inputs to exercise all the branches in the code. Don't go there. ;-)
 
B

Ben Pope

Phlip said:
The next, more complex topic is: How to make sure the tests use enough
inputs to exercise all the branches in the code. Don't go there. ;-)

Classification trees and code coverage tests. Don't know of any free
software to do that.

Ben Pope
 
P

Pete Becker

Phlip said:
The next, more complex topic is: How to make sure the tests use enough
inputs to exercise all the branches in the code. Don't go there. ;-)

Especially since branch testing is known to be inadequate. <g>
 
P

Pete Becker

Phlip said:
Pete Becker wrote:




Inadequate for converting an integer to a string???

Certainly inadequate as a testing discipline in general, and almost
certainly inadequate for testing a function that converts an integer to
a string. Whether it gives you adequate coverage in any particular case
depends on the case. Applied to the original code it is inadequate.

For example, branch analysis could be satisfied with test cases that
only exercise the do loop exactly twice: once to branch back to the head
of the loop and once to branch out of it. That doesn't test what happens
when you need exactly one digit or three or more digits to represent the
value. Of course, doing that one thoroughly requires path testing, which
is known to be prohibitively expensive <g>, although perhaps workable
for this example.

For another example, if the charTable had some incorrect values, a test
suite based only on branch testing wouldn't necessarily find them.
 
D

Dietmar Kuehl

Phlip said:
Inadequate for converting an integer to a string???

At least insufficient. For example, branch testing would not detect
the error which is in the original code when passing
'std::numeric_limits<int>::min()': the value range for integers is
[normally] asymmetric and "i = -i" is the identity operation for
the two values '0' and 'std::numeric_limits<int>::min()' (well, for
the latter value the behavior may actually be undefined or
implementation defined). That is, in addition to branch testing you
need to perform at least some testing for corner cases.
 
P

Phlip

Dietmar said:
That is, in addition to branch testing you
need to perform at least some testing for corner cases.

Tiny little voice: Then add, uh, branches inside the code to handle or throw
the corner cases...

But thanks guys - I put both yours and Pete's answers on a Wiki page
BranchTesting. For some strange reason no page by that name was there
yet. ;-)
 
B

BobR

(e-mail address removed) wrote in message
Hi All,
I wrote the following to print an integer in its string
representation for base -36 to 36.
Please comment on this code.

#include <iostream>
#include <string>
// >using std::abs;
using std::cout;
using std::endl; // >using std::reverse;
using std::string;

Style-wise, **IMHO**, (at this point) it stinks!! I'd put the 'using *'
inside the function/main if I couldn't type "std::".
My suggestions inline below.
// >char charTable[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

std::string charTable( "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" );
// >void printBase(int i, int base){
std::string CalcBase(int i, int base){
using std::abs; // etc.
if ((abs(base) <= 1) || (abs(base) > 36) || (i == 0)){
// > cout << "0" << endl;
// > return;
return "0";
}

string str = "";
if ((base > 0) && (i < 0)){
str += "-"; // prints "-16 = 10-" for base==16
i = -i;
}

do {
if (i % base >= 0){
// > str += charTable[i % base];
str += charTable.at( i % base );
i = i / base;
} else {
// > str += charTable[i % base - base];
str += charTable.at( i % base - base );
i = i /base + 1;
}
} while(i);
// > reverse(str.begin(), str.end());
std::reverse(str.begin(), str.end());

// prints "-16 = 10-" for base==16

// > cout << str << endl;
return str;
} // CalcBase(int,int)

int main(){

try{
for(int i(-55); i <= 55; ++i){
// > cout << i << " = ";
// > printBase( i, -10 );
std::cout << i <<" = "<<CalcBase( i, -10 )<<std::endl;
} // for(i)
} // try
catch(std::eek:ut_of_range &Oor){
std::cerr<<"caught "<<Oor.what()<<std::endl;
return EXIT_FAILURE;
}
catch(...){
std::cerr<<"caught ... something"<<std::endl;
return EXIT_FAILURE;
}
return 0;
} // main()end

Thanks and Regards,
manoj.

// - output - (base==-10)
-55 = 65
-54 = 66
[snip]
-11 = 29
-10 = 10
-9 = 11
-8 = 12
[snip]
-2 = 18
-1 = 19
0 = 0
1 = 1
[snip]
8 = 8
9 = 9
10 = 190
11 = 191
[snip]
54 = 154
55 = 155


And your C++ problem is?
 
P

Phlip

BobR said:
catch(...){
std::cerr<<"caught ... something"<<std::endl;
return EXIT_FAILURE;
}

I know a certain lead programmer driven to distraction by code that relied
on catch(...). The advice is let the program halt; use a debugger to fix
all root causes, and stress-test applications to force out these risks.

And catch(...) only catches hardware crashes on Win32 as a compiler-specific
extension. Other platforms just dump core, meaning they cannot use that
trick to maintain their work loops and keep running.
 
P

Pete Becker

BobR said:
// > str += charTable[i % base];
str += charTable.at( i % base );

The data has already been validated. This check adds nothing. The
original version, with the char array and direct access, had far less
overhead and was just as robust.
 
P

Pete Becker

Phlip said:
I know a certain lead programmer driven to distraction by code that relied
on catch(...). The advice is let the program halt; use a debugger to fix
all root causes, and stress-test applications to force out these risks.

But it's okay here, since the code will never throw an exception. The
checking in calls to at() is redundant.
 
B

BobR

Phlip wrote in message ...
BobR wrote:

I know a certain lead programmer driven to distraction by code that relied
on catch(...). The advice is let the program halt; use a debugger to fix
all root causes, and stress-test applications to force out these risks.

And catch(...) only catches hardware crashes on Win32 as a compiler-specific
extension. Other platforms just dump core, meaning they cannot use that
trick to maintain their work loops and keep running.

Good advice.
But for 'toy' code, it's nice to see *something* output when testing a chunk
of new code.
In my experiments with try-catch, I've caught things other than 'hardware'
(MinGW, win98).

// ------------------------------------
class MyError : public std::runtime_error { public:
MyError(std::string const &msg = "") : std::runtime_error(msg){}
};
// ------------------------------------
void f() throw(MyError){
throw MyError( "blah-blah" );
return;
}
// ------------------------------------
// ------------ in main()
try{ f();}
// catch(MyError const &x){
// std::cout<<"MyError test: "<<x.what()<<std::endl;
// }
// catch(std::runtime_error const &error){
// std::cout<<"std::runtime_error "<<error.what()<<std::endl;
// }
catch(...){
std::cout<<"something TERRIBLE happened,"
" but, I don't know what!!"<<std::endl;
}
// ------------
// - output -
something TERRIBLE happened, but, I don't know what!!


Are you saying I can't do that in GNU/Linux, winXP?
 
P

Phlip

BobR said:
Are you saying I can't do that in GNU/Linux, winXP?

Windows has an extra feature that turns general protection faults (you know;
crashies. Bad things.) into exceptions that you can catch with catch(int).
So catch(...) will also catch them.

Other systems may have similar features. Programmers should develop slowly
and incrementally enough to discover and write code to catch all "real"
exceptions, and should treat catch(...) like a failing assert() statement.
Time to dissect the code looking for a programmer error.

So, either way, never rely on 'catch(...)' within delivered code.
 
R

red floyd

Phlip said:
Windows has an extra feature that turns general protection faults (you know;
crashies. Bad things.) into exceptions that you can catch with catch(int).
So catch(...) will also catch them.

We're way OT here, but it turns them into catch(int)? I could never
figure out C0000005 errors (Win32 equivalent of SegV) turned into.
That's the main reason I had catch(...) in my code.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,756
Messages
2,569,535
Members
45,007
Latest member
OrderFitnessKetoCapsules

Latest Threads

Top