Конвертер десятичных чисел в восьмеричные, проблема с последней цифрой

Я попытался создать программу C для преобразования введенного пользователем десятичного числа в восьмеричное. Я написал код C со своей собственной логикой, не исследуя, как другие пользователи пытаются это сделать. Он отлично работает для числа 601 и некоторых других чисел, но для большинства чисел он возвращает восьмеричный эквивалент с последней цифрой, которая на 1 меньше, чем должна быть. Для 75 возвращается 112 вместо 113. Я понимаю, что использование printf с %o выполняет свою работу, но это как бы сводит на нет цель обучения программированию. Вот мой код:

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

/* converting decimal to octal */

int main() 
{
    int n,x,y,p,s;
    printf("Enter a decimal number ");
    scanf("%d",&x);
    s=0;p=0;

    while (x!=0)
    {
         y=x%8;
         s=s+y*pow(10,p);
         x=(x-y)/8;
         p=p+1;
    }  

  printf("the octal equivalent is: %d\n",s);
  getch();
  return 0;
}

person Srishan Supertramp    schedule 02.11.2012    source источник
comment
Просто подозрение, может быть проблема в том, что двойное pow(10, p) приводится к int? Как насчет того, чтобы инициализировать p значением 1 и умножать его на 10 за итерацию, а не увеличивать его?   -  person susmits    schedule 02.11.2012
comment
У меня работает, я получаю 113 за 75, на линуксе с gcc.   -  person Rohan    schedule 02.11.2012
comment
У меня тоже работает! У меня получается 113 за 75, на Win7 с VS2010.   -  person Rajendra Uppal    schedule 02.11.2012


Ответы (6)


Это интересный способ сделать это. Я проголосую за вопрос только за оригинальность.

Исправьте проблему, добавив фактор выдумки:

s = s + y*pow(10,p) + 0.1;

Проблема заключается в точности с плавающей запятой. Один из этих вычислений pow возвращает что-то вроде 99,999999999999992 вместо 100. При преобразовании обратно в int теряется дробь 0,999... и получается 99 вместо 100. Или 999 вместо 1000. Это не на месте, потому что нулевой показатель степени особый случай, который получает 1.0 точно.

Кстати, поскольку вы можете получить только 9 цифр в 32-битном int, предложение Susmit умножить степень 10 в форме int вполне разумно. За исключением особых случаев, pow будет вычислять pow(x,y) как exp(y*log(x)). exp() и log(), как правило, являются дорогостоящими вычислениями.

Более того, я не могу воспроизвести эту проблему, даже заставив exp и log вычислять. Я получаю значения (10 + 1,8e-15), (100 + 4,3e-14) и (1000 + 6,8e-13), все округляя правильно, потому что ошибка как у всех положительная. Это на x86, и я думаю, что скомпилировал этот калькулятор с помощью VC++ 2008. Какой компилятор вы используете?

person Mike Housky    schedule 02.11.2012
comment
Я использую компилятор Dev C++ от Bloodshed. Спасибо Майку за объяснение и за идею умножения на 10 вместо использования pow. Сейчас исправил, работает нормально. Спасибо - person Srishan Supertramp; 12.11.2012

Альтернатива первому ответу вместо

s=s+y*pow(10,p);

иметь коэффициент сдвига, инициализированный единицей, и делать

s += y*shiftfactor;
shiftfactor *= 10;

Кроме того, вместо x=(x-y)/8; достаточно сделать просто x/=8; из-за целочисленного округления.

person hyde    schedule 02.11.2012

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

/* converting decimal to octal */

int main()
{
   int n, x, y, p;
   double s = 0;
   printf("Enter a decimal number ");
   scanf("%d", &x);

   p = 0;

   while (x != 0)
   {
        y = x % 8;
        s = s + (double)y * pow(10, p);
        x = ( x - y ) / 8;
        p = p + 1;
}

printf("восьмеричный эквивалент: %lf\n",s);

вернуть 0; }

Думаю, это решит вашу проблему. На самом деле Pow() возвращает двойное значение. В вашем коде вы использовали int, поэтому дробная часть будет усечена и помещена в переменную 's'.

person Sankar Mani    schedule 02.11.2012

#include <stdio.h>
int main()
{
    int d_num = 0;
    int o_num = 0;
    int factor = 1;

    printf("Enter a decimal number: ");
    scanf("%d", &d_num);

    while (d_num > 0) {
            o_num += d_num % 8 * factor;
            d_num /= 8;
            factor *= 10;
    }

    printf("Octal equivalent: %d\n", o_num);
    return 0;
}

Для меня этот код отлично работает только с целочисленными операциями.

person Sakthi Kumar    schedule 02.11.2012

Почему вам нужно использовать x=(x-y)/8. Почему ты не можешь использовать x=x/8. Это тот же эффект, что и тип переменных, используемых в левых и правых, все целые числа. Кстати код у меня работает. Я думаю, что проблема связана с целочисленным округлением. Просто замените x=(x-y)/8 на x=x/8

person m4n1c    schedule 02.11.2012

OP решил эту проблему некоторое время назад, но подумал, что я представлю простое решение.

/* converting decimal to octal */
unsigned decimal_to_octal(unsigned x) {
  if (x < 8) {
    return x;
  }
  return decimal_to_octal(x/8)*10 + x%8;
}

Тестовый код

unsigned test10to8(void) {
  for (;;) {
    unsigned x;
    scanf("%d", &x);
    printf("x:%u y:%u\n", x, decimal_to_octal(x));
  }
}
person chux - Reinstate Monica    schedule 27.08.2015