tags:

views:

543

answers:

9

Hi - As a homework problem, I'm working on reading a decimal int from stdin, converting it to a different base (also provided from stdin) and printing it to the screen.

Here's what I've got so far:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int num, base, remainder, quotient;
    printf("please enter a positive number to convert: ");
    scanf("%d", &num);
    printf("please enter the base to convert to: ");
    scanf("%d", &base);

    remainder = quotient = 1;

    // validate input
    if (num < 0 || base < 0) {
        printf("Error - all numbers must be positive integers!\n");
        return 1;
    }

    // keep dividing to find remainders
    while (quotient > 0) {
        remainder = num % base;
        quotient = num / base;
        num = quotient;
        if (remainder >= 10) {
            printf("%c", remainder + 55);
        } else {
            printf("%d", remainder);
        }
    }   
    printf("\n");
    return 0;
}

This works great, only that the algorithm this uses calculates the converted numbers from least significant to most significant digit, thus printing it in reverse. So, for example, converting 1020 to hexadecimal ( 0x3FC ) will print CF3.

Is there a trick I could use to reverse these numbers to print in the correct order. I can only use if-else, while, simple math operators and printf()/getchar()/scanf() - no functions, arrays or pointers. thanks.

+3  A: 

(removed original part of the post here, since it is not the solution)

THen the only solution I can see is to perform the loop that you have now the number of times that you have digits.

So first you calculate all digits till you get to the last, and then print it.

Then you take the original value + base and start dividing again till you come to the second "highest value" digit. Print it.

It is a double loop and you calculate everything twice, but you don't use extra storage.

Marco van de Voort
He can't use functions...
Arno
The loop body is the example how to tackle it. He can just inline it. But I realized that is not what the teacher is after. So I provide an extra solution below the code.
Marco van de Voort
I like the second solution - given the restrictions (no arrays, pointers, etc.), it seems a decent way to go about it, even if it's a bit slow and redundant. +1
Tim
A: 

You could rewrite the piece of code calculating each number to behave as a state machine. It will start in the initial state and compute the number of digits, then change the state to "print the Nth digit" to print the most significant digit, then change the state to proceed to the less significant digits, etc until it eneters the final state. Running this inside a loop you will output all digits in proper order.

sharptooth
+1  A: 

In one loop you can calculate the number of digits and the big_base.
In a second loop you can output the digits starting from the most significant, like this:

n = 1020, 3 hex digits, big_base = 16*16

1st step
1020 / (16*16) = 3

2nd step
n = 1020- 3*(16*16) = 252
252 / (16) = 15, F

3rd step
n = 252 - 15*16 = 12, C

Nick D
+1. You'd need to figure out N, where `base^N <= num < base^(N+1)`.
dirkgently
that's be easy with float math : ceil( log(num) / log(base) )But it's not allowed there I guess, that's be too easy.
Zeograd
A: 

You could use two loops. The first keeps generating powers of the base until it finds a power greater than the input number. The second starts from here (or rather, one power before) and works back to base^0 (i.e. 1) to compute the output digits most significant first.

Untested pseudo-code:

// Determine highest power, don't actually need "power" it's just there for illustration
power = 0;
baseraisedtopower = 1;
while (baseraisedtopower <= input)
{
    baseraisedtopower *= base;
    power++;
}
// Go back one step, could have saved previous result
baseraisedtopower /= base;
power--;
// Output
while (input > 0)
{
    // Integer division, truncate
    quotient = input / baseraisedtopower;
    printf("%c", quotient + 55);
    input -= quotient * baseraisedtopower;
    baseraisedtopower /= base;
    power--;
}
Tom
A: 

You can give a try at this approach. It's more a proof of concept, you'll still need to handle some special case, but, hey, that's your homework :)

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int num, base, remainder, quotient;
    int divider;

    printf("please enter a positive number to convert: ");
    scanf("%d", &num);
    printf("please enter the base to convert to: ");
    scanf("%d", &base);

    remainder = quotient = 1;

    // validate input
    if (num < 0 || base < 0) {
        printf("Error - all numbers must be positive integers!\n");
        return 1;
    }

    // First get the highest divider
    divider = base;

    while ( num / divider > base ) {
      divider *= base;
    }

    do {

      // Get the highest digit
      remainder = num / divider;

      // And update num accordingly
      num -= remainder * divider;
      divider /= base;

      if (remainder >= 10) {
        printf("%c", remainder + 55);
      } else {
        printf("%d", remainder);
      }

    } while ( divider );

    printf("\n");
    return 0;
}
Zeograd
+3  A: 

It's a good try, and well phrased question. If only we had more people asking questions in such a clear manner!

The restrictions seem artificial. I guess you haven't learned about functions, arrays, pointers etc., in your class yet, but I think this problem is not meant to be solved elegantly without functions and/or arrays.

Anyway, you can do something like this:

curr := base
pow := 1
while num / curr >= 1 do:
    curr := curr * base
    pow := pow + 1

while pow >= 1:
    pow := pow - 1
    print floor(num / base ** pow)
    num := mod(num, base ** pow)

Basically, you are calculating how many digits you will need in the first loop, and then printing the digits in the correct order later.

Some specific issues with your code. I understand it's the beginning of a C class, but still, it's better to know of such issues now than to never realize them:

printf("please enter a positive number to convert: ");

You should add an fflush(stdout) after this to make sure the output appears before scanf() is called. By default, stdout is line buffered on many systems, so the prompt may not appear before your program waits for input.

printf("please enter the base to convert to: ");

Same as above.

    if (remainder >= 10) {
        printf("%c", remainder + 55);
    } else {
        printf("%d", remainder);
    }

You're assuming ASCII character set. This need not be true. But without arrays or pointers, there's no easy way to print the alphabets corresponding to 10.... Also, your code may print weird characters for base > 36.

You should also be aware that it's very hard to use scanf() safely. Hopefully you will learn better ways of getting input later.

Alok
+1  A: 

Hey ! I recognize a famous homework I had in first year of my school too (@Epitech students : don't copy/paste the following code, try to come up with your own solution, it's for your own good ^^)

The solution to your problem is to perform the problem in a recursive way :

void    my_putnbr_base(int num, int base)
{
  int   start;
  int   remainder;

  remainder = num % base;
  start = (num - remainder) / base;
  if (start != 0)
    my_putnbr_base(start, base);
  if (remainder >= 10)
    printf("%c", remainder + 55);
  else
    printf("%d", remainder);
}

Does your homework specifies that it should only work with positives numbers ? If not, it's easy to include the negative numbers handling :

void    my_putnbr_base(int num, int base)
{
  int   start;
  int   remainder;

  if (num < 0)
    {
      putchar('-');
      my_putnbr_base(-num, base);
    }
  else
    {
      remainder = num % base;
      start = (num - remainder) / base;
      if (start != 0)
     my_putnbr_base(start, base);
      if (remainder >= 10)
        printf("%c", remainder + 55);
      else
     printf("%d", remainder);
    }
}

@arno : that's true, because the exemple code is using ASCII table. If we want something trully flexible we need the base in parameter. For example :

>> my_putnbr_base(4242, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")
39U
>> my_putnbr_base(42, "0123456789ABCDEF")
2A

this implements the example :

void    my_putnbr_base(int num, char *base)
{
  int   start;
  int   remainder;
  int   len;

  len = strlen(base);
  if (num < 0)
    {
      putchar('-');
      my_putnbr_base(-num, base);
    }
  else
    {
      remainder = num % len;
      start = (num - remainder) / len;
      if (start != 0)
     my_putnbr_base(start, base);
      printf("%c", base[remainder]);
    }
}

I hope it solves your problem !

edit: I didn't read correctly ^^ You are not allowed to use functions, so recursion is out of the question... Here is an interative way, you can put this in a main(). You can improve this code by adding the negative numbers handling and flexible bases, as I showed you :)

int  my_putnbr_base_it(int num, int base)
{
  unsigned int  quotient = 1;
  unsigned int  remainder;

  while ((num / quotient) >= base)
    quotient *= base;
  while (quotient)
    {
      if ((remainder = (num / quotient) % base) < 10)
        printf("%d", remainder);
      else
        printf("%c", 55 + remainder);
      quotient /= base;
    }
  return (0);
}

Hope it solves everything now !

Thomas Joulin
A: 

Interesting task, you've got as a homework. I am a beginner programmer to, and I've tried to resolve this task.

The following code is working (I haven't tested a lot, apparently is working). I am sure it's not the optimal&best solution, but was the only thing I've could come up with. It should work with any base. Unfortunately it won't convert 10->A, 11->B, etc.:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int main(){
        int nr,base,res,tp,tpb,tpbt,r,rnr,lp,lpt,i;
        float baset,rt;

        /** Read number */
        printf("nr=");
        scanf("%d",&nr);

        /** Read base */
        printf("base=");
        scanf("%d",&base);

        /** Returning result */
        res=0;

        /** Test if number is positive
        and base is bigger than 2 */
        if(nr<0||base<2){
                /** Error */
                res=1;
        }
        else{
                /** Determine how many
                digits are necessary */
                lp=0;
                baset=base;
                while(baset>1){
                        lp++;
                        baset/=10;
                }

                /** Determine full power
                of 10 when r has length of lp */
                tpb=1;
                while((lp--)>0){
                        tpb*=10;
                }

                /** Power of ten that will be
                incremented */
                tp=0;

                /** Converted number (will be printed
                as the result) */
                rnr=0;

                /** Algorithm */
                while(nr>0){
                        r=nr%base;
                        nr/=base;
                        rt=r;

                        /** Temporary lp for
                        r */
                        lpt=0;
                        while(rt>1){
                                lpt++;
                                rt/=10;
                        }

                        /** Temporary tpb for
                        lpt */
                        tpbt=tpb;
                        for(i=0;i<lpt;i++){
                                tpbt/=10;
                        }

                        /** Build number */
                        rnr+=r*pow((double)(tpbt),(double)(tp++));
                }
        }

        /** Show number */
        printf("number is: %d \n",rnr);

        return (res);
}
Andrei Ciobanu
thanks, but I can't use pow..
sa125
Implement your own simple pow function in the code. It's not that hard: use a for/while loop.
Andrei Ciobanu
A: 

Based on what was suggested, the way to tackle this was to keep print the last number and repeat the loop for every digit. I kept track of the print condition by saving the previous quotient and printing when I got to it every time (then reseting the number and starting over), then reset it to the one before. Sounds complicated, but the change to the code was simple. My stop condition for the loop was when I had 2 consecutive prints, since most of the time it would just calculate quotient/remainder and print nothing, and when 2 digits print in a row, it's the last two. Anyway, here's the code:

#include <stdio.h>
#include <stdlib.h>
int main()
{
    int num, saved, base, remainder;
    int quotient, prev_q, stop_q, just_printed;

    printf("please enter a positive number to convert: ");
    scanf("%d", &num);
    printf("please enter the base to convert to: ");
    scanf("%d", &base);

    saved = num;
    remainder = quotient = prev_q = just_printed = 1;
    stop_q = 0;

    // validate input
    if (num <= 0 || base <= 0) {
        printf("Error - all numbers must be positive integers!\n");
        return 1;
    }

    // divide
    while (1) {
        remainder = num % base;
        quotient = num / base;
        num = quotient;

        // print if it's the last number and reset num to the next 
        if (quotient == stop_q) {
            if (remainder >= 10) { printf("%c", remainder + 55); } 
            else { printf("%d", remainder); }

            // if 2 consecutive printing occur, this means it's time to end this
            if (just_printed) { break; }

            // next time print when hitting the previous quotient
            stop_q = prev_q;

            // reset the number to the original value
            num = saved;


            just_printed = 1;
        } else {
            just_printed = 0;
        }
        prev_q = quotient;
    }   
    printf("\n");
    return 0;
}

Thanks to everyone who pitched in!

sa125