?
Solved

C++ Read Two Numbers in Base, Compute sum, print out

Posted on 2003-02-19
5
Medium Priority
?
275 Views
Last Modified: 2010-04-01
My starting code is in binary.  I just wanted to make it octal and add the numbers.

*/

#include <stdio.h>

short decimal2binary(unsigned long decimal_value, char

binary_value[32])
{
short index,significant_digits=0;
unsigned long temp_value;
for(index=31;index>=0;index--)
{
 // temp_value=decimal_value/pow(2,index)
 temp_value=decimal_value/(1<<index);
 if(temp_value>0)
 {
  binary_value[index]=(char)('0'+temp_value);
  // decimal_value=decimal_value%pow(2,index)
  decimal_value=decimal_value%(1<<index);
  if(!significant_digits)
   significant_digits=index;
 }
 else
 {
  binary_value[index]='0';
 }
}
return significant_digits;
}

short decimal2hex(unsigned long decimal_value, char hex_value[8])
{
short index,significant_digits=0;
unsigned long temp_value;
for(index=7;index>=0;index--)
{
 // temp_value=decimal_value/pow(16,index)
 temp_value=decimal_value/(1<<(index<<2));
 if(temp_value>9)
 {
  hex_value[index]=(char)('A'-10+temp_value);
  // decimal_value=decimal_value%pow(16,index)
  decimal_value=decimal_value%(1<<(index<<2));
  if(!significant_digits)
   significant_digits=index;
 }
 else if(temp_value>0)
 {
  hex_value[index]=(char)('0'+temp_value);
  // decimal_value=decimal_value%pow(16,index)
  decimal_value=decimal_value%(1<<(index<<2));
  if(!significant_digits)
   significant_digits=index;
 }
 else
 {
  hex_value[index]='0';
 }
}
return significant_digits;
}

void main()
{
short significant_digits,index;
char hex_value[8];
char binary_value[32];
/*
 * Hex conversion
 */
significant_digits=decimal2hex(0x0123FEDC,hex_value);
printf("0x0123FEDC = 0x");
/*
 * With leading zeros ...
 * for(index=8;index>=0;index--)
 */
for(index=significant_digits;index>=0;index--)
 printf("%c",hex_value[index]);
/*
 * Binary conversion next
 */
significant_digits=decimal2binary(0x0123FEDC,binary_value);
printf(" = 0b");
/*
 * Leading zeros...
 * for(index=31;index>=0;index--)
 */
for(index=significant_digits;index>=0;index--)
 printf("%c",binary_value[index]);
printf("\n");
}
0
Comment
Question by:adinkins
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 3
  • 2
5 Comments
 
LVL 12

Accepted Solution

by:
Salte earned 100 total points
ID: 7983028
First off,

Not quite sure what is your question but here are a few comments...

a unsigned long seldom hold decimal values, it usually hold binary values.

Secondly dividing by (1 << index) is the same as doing a >> shift by index, so:

num / (1 << index) is more or less the same as
num >> index.

Secondly all you want is that bit, so why do it so complicated?

for (int index = 32, bits = intval; index >=0; bits >>= 1)
   binary_value[--index] = '0' + (bits & 1);

Probably should make that string have length 33 so you have room for terminating null byte also:

binary_value[32] = 0;



The hex value the easiest is probably to provide a small table of digits:

const char digits[] = "0123456789ABCDEF";

for (int index = 8, bits = intval; index >= 0; bits >>= 4)
    hex_value[--index] = digits[bits & 0x0f];


And here is one important thing, that "decimal_value" isn't really very decimal, it is very much binary. So you probably should also have a to_decimal() function similar to the ones above. One problem is to determine number of digits but then again in decimal you probably don't want leading zeroes, here is how to convert to decimal:

void to_decimal(unsigned int val, char * string, int len)
{
   // we can't write the number to string yet because
   // we start from the least significant digit.
   // we therefore first build the digits to a temporary
   // buffer locally:
   char digits[12]; // should suffice for 32 bit int.

   char * e = digits + sizeof(digits); // point to end.
   char * p = e; // running pointer.
   *--p = 0; // add terminating 0 first.
   do {
      *--p = '0' + (val % 10);
   } while ((val /= 10) > 0);
   // how many digits is the number + null byte.
   int actual_numwidth = e - p;
   if (len < actual_numwidth) {
      // too narrow
      memset(string,'*',len-1);
   } else {
      // xlen is extra length.
      int xlen = len - actual_numwidth;
      // first blanks (right adjusted number).
      memset(string,' ', xlen);
      memcpy(string + xlen,p,actual_numwidth);
   }
}
The actual number conversion took less statements than the fixup afterwards :-)

If you want no padding and just want to return a shorter string if the value is smaller:

if (len < actual_numwidth) { // still error...
   memset(string,'*',len);
} else {
   memcpy(string,p,actual_numwidth);
}

Also, if you use C style strings with a terminating null byte at end it is easier to print out afterwards.

printf( "%s", value);

instead of

for (int i = 0; i < len; ++i)
   printf( "%c", value[i]);

If you want other formatting that is also possible by properly amending the various output functions.

You can also write a generic number output function:

char * num2str(char * buf, size_t sz,
               unsigned int val,
               int radix)
{
   ...
}

Then the code to use would be similar to the hex and the decimal output and would be a mix of the two:

char * num2str(......)
{
   const char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
// assuming lowercase letters for digits above 10.

   char buf[34]; // maximum length of a buffer.
   // even binary doesn't use longer than 34 chars.

   char * e = buf + sizeof(buf); // end of buf.
   char * p = e;
   *--p = '\0'; // terminating null byte.
   do {
      *--p = digits[val % radix];
   } while ((val /= radix) > 0);
   // format and display as you please (copy to parameter).
}

This function can handle output in any radix such that 2 <= radix <= 36.

If you want uppercase letters for digits above 10 the change obvious change is the right change. A bit trickier if you want a parameter to select lower or uppercase. Essentially you can use two strings for lowdigits and uppdigits and then based on that selection you have a const char * variable pointing to one or the other of those two and then use that char pointer instead of digits[] above.

If you want other formatting such as 0x for hex etc or perhaps you want a character to be inserted between every n digits then that is also fairly easy to do but keep in mind that the temporary buffer probably have to be made larger then, it must be made so that a completely formatted binary number with maximum prefixes and suffixes and characters in between will fit in the buffer. Also, if you want prefix such as 0x for hex you probably want leading zeroes between 0x and the number and so you first add leading zeroes before adding the prefix (since you add in stuff in front of the string and in reverse order of how it is read).

Hope this is of help for you.

Alf
0
 

Author Comment

by:adinkins
ID: 7984139
That is still beyond me.  I'm a newbie.

A working code I came struggled with is below.  Now how would I do addition, example input two numbers in base 8 and then print the results?  I'm not sure whether I have to convert the numbers first.  Any suggestions..


 #include<stdio.h>

int main()
{
   int octal, number, decimal = 0;
   int highBit = 512, factor = 10000;

   printf( "Enter two numbers ( no more than 4 digits ) in base 8: " );
   scanf( "%o", &octal );

   number = octal;

   while ( highBit >= 7 )
   {
      decimal += octal / factor * highBit;
      highBit /= 8;
      octal %= factor;
      factor /= 10;
   }

   printf( "The decimal equivalent of %d is %d\n", number, decimal );

   return 0;
}

0
 

Author Comment

by:adinkins
ID: 7984152
That is still beyond me.  I'm a newbie.

A working code I came struggled with is below.  Now how would I do addition, example input two numbers in base 8 and then print the results?  I'm not sure whether I have to convert the numbers first.  Any suggestions..


 #include<stdio.h>

int main()
{
   int octal, number, decimal = 0;
   int highBit = 512, factor = 10000;

   printf( "Enter two numbers ( no more than 4 digits ) in base 8: " );
   scanf( "%o", &octal );

   number = octal;

   while ( highBit >= 7 )
   {
      decimal += octal / factor * highBit;
      highBit /= 8;
      octal %= factor;
      factor /= 10;
   }

   printf( "The decimal equivalent of %d is %d\n", number, decimal );

   return 0;
}

0
 

Author Comment

by:adinkins
ID: 7984232
oops!! i need to go back and review my math
0
 
LVL 12

Expert Comment

by:Salte
ID: 7992488
The question of if you should convert to int before you add depends on how large the numbers are.

If the numbers will fit inside an int both before and after the add, then the easiest is to convert to int, do the math, convert back to string and output result.

If the numbers are big numbers then you can convert to a "bignum" which is an array of int values each holding a digit of the number in a radix R. The radix R is largely of your own choosing and you COULD let it be 10 and then essentially do the arithmetic on the strings directly.

Be aware that ASCII math is kinda complicated and inefficient though.

1. You do the operation for each digit, you therefore normally want to have as few digits as possible. A byte can hold values as high as 100 in a decimal based radix (1000 would be too much for a char type) but if you don't mind using non decimal based radix you can use radix 256 for a char type or even better use radix 65536 for a unsigned short type or 2^32 (2 raised to the power of 32) for an unsigned int (assuming sizeof(int) == 4).

2. ASCII holds the value with a bias of '0'. I.e. '1' isn't the same as 1 but is equal to '0' + 1. '9' == '0' + 9.

This means that '1' + '2' is not the same as '3' but is equal to '3' + '0'. so to add two values holding ascii digits you need to remember that '0' everywhere:

c = a + b - '0';

will almost correctly compute the sum of a and b if a and b are ascii digits.

The almost part is due to overflow, if a and b are '3' and '5' respectively, the above will correctly compute '8' but if a and b are '7' and '8' the result will be:

c = '7' + '8' - '0' == '?'

and obviously non-sense, so you need to check if the result c > '9' then you subtract 10 and add a carry of 1. This also means you should add the carry in in the first place:

c = a + b - '0' + carry;
carry = 0;

if (c > '9') {
   c -= 10;
   carry = 1;
}
and then you can compute the next digits.

This is for adding, for subtraction it is very much the same. When you do subtraction by hand you first compare if the value a is less than b and you then borrow if a is less than b and do not borrow if a >= b. This is not so easy to do by computer, it is much easier to always borrow and then get a carry if it is too much. That carry will cancel the borrow so you get correct result. For this reason it is good to start the subtraction by setting borrow to 1 before you start the loop.

c = a + 10 - 1 - b + carry;

The +10 is for the borrow from next higher digit, the -1 is for the reason that we did a borrow of +10 in previous lower digit and now we need to subtract a 1 for that. If we did get a carry from previous round it meant that we shouldn't have done that borrow and so we add the carry to cancel the borrow of -1. If the A operand (all digits) is >= B then you should get a final carry that would cancel the fact that we did a borrow of +10 on the highest digit which we did by borrowing 1 from the non-existent digit above the highest. Since that digit doesn't exist we should get a carry to cancel that borrow.

However, if A < B then we won't get a carry but in that case we subtract a large number from a smaller and so we should really switch it around and do (B - A) and mark the result as negative. Luckily the result is almost what we have. The only problem is that our result is R-complement of the true result. Let's use R == 10 and use the algorithm on 27 - 34 to illustrate:

carry = 1;
c0 = a0 + 10 - 1 + carry - b0 == 7 + 10 - 1 + 1 - 4 == 13
c0 = c0 - 10 == 3, carry = 1
c1 = a1 + 10 - 1 + carry - b1 == 2 + 10 - 1 + 1 - 3 == 9

the result is '93' and a carry of 0.

The carry of 0 indicate that A < B and the true result should have been: -(B-A) == -(34-27) == -7

If you add 7 and 93 you get exactly 100 and that is no coincidence. 93 is the '10-complement of 7' + 1 in 2 digits. In three digits it would have been 993 etc..

The 10 complement of 7 is 92 and +1 gets 93.

So this gives us a way to compute the right result in this case:

if (carry == 0) {
   replace each digit c with 9 - c.
   compute C = C + 1.
   correct result is -C.
}
The replace each digit c with 9-c and then add 1 can be done in one combined operation:

Scan from least significant digit and skip all '0'.

Since we got a negative result with carry == 0 at least one digit must differ from '0'.

The least digit c that is not '0' is then changed to 10-c:

c = 10 - (c - '0') + '0' == 10 + '0' + '0' - c;

Don't worry about the constants added together, a compiler will typically add them together at compile time so you get a decent one constant value in the final code. (This is usually referred to as constant folding).

For any digits higher than this least non '0' digit we compute:
c = 9 - (c - '0') + '0' == 9 + '0' + '0' - c;

and then mark the result as 'negative'.

if the value is stored in a string of length n then:

result_is_negative = (carry == 0);

if (result_is_negative) {
   int k = n;
   while (val[--k] == '0');
   // val[k] != '0'
   val[k] = 10 + '0' + '0' - val[k];
   while (--k >= 0)
      val[k] = 9 + '0' + '0' - val[k];
}

That code turns the '93' in our example to -7.

Aside from the fact that those '0' can be tricky to keep track of is the fact that when you have a raw number as input from user or file, the number some times contain formatting. For example extra decoration in front or after the number for units etc etc, so you have to strip all that stuff off before you can do the math anyway, so why not use a non-ascii format that is more math friendly? Since you have to change the value anyway.

Alf
0

Featured Post

On Demand Webinar - Networking for the Cloud Era

This webinar discusses:
-Common barriers companies experience when moving to the cloud
-How SD-WAN changes the way we look at networks
-Best practices customers should employ moving forward with cloud migration
-What happens behind the scenes of SteelConnect’s one-click button

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Written by John Humphreys C++ Threading and the POSIX Library This article will cover the basic information that you need to know in order to make use of the POSIX threading library available for C and C++ on UNIX and most Linux systems.   [s…
IntroductionThis article is the second in a three part article series on the Visual Studio 2008 Debugger.  It provides tips in setting and using breakpoints. If not familiar with this debugger, you can find a basic introduction in the EE article loc…
The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.
The viewer will learn how to clear a vector as well as how to detect empty vectors in C++.

801 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question