【问题标题】:Octal conversion using loops in C++在 C++ 中使用循环进行八进制转换
【发布时间】:2011-09-30 06:24:47
【问题描述】:

我目前正在开发一个将二进制数转换为八进制数的基本程序。它的任务是打印一个包含 0-256 之间所有数字的表格,以及它们的二进制、八进制和十六进制等价物。该任务只要求我使用自己的代码(即使用循环等而不是内置函数)。我制作的代码(目前非常混乱)如下(这只是一个片段):

        int counter = ceil(log10(fabs(binaryValue)+1));
        int iter;
        if (counter%3 == 0)
        {
            iter = counter/3;
        }
        else if (counter%3 != 0)
        {
            iter = ceil((counter/3)); 
        }
        c = binaryValue;
        for (int h = 0; h < iter; h++)
        {
            tempOctal = c%1000;
            c /= 1000;
            int count = ceil(log10(fabs(tempOctal)+1));
            for (int counter = 0; counter < count; counter++)
            {
                if (tempOctal%10 != 0)
                {
                   e = pow(2.0, counter);
                   tempDecimal += e;
                }
                tempOctal /= 10;
            }
            octalValue += (tempDecimal * pow(10.0, h));
        }

输出完全错误。例如,当二进制代码为 1111(十进制值 15)时,它输出 7。我可以理解为什么会发生这种情况(二进制数 111 中的最后三位数字是十进制格式的 7),但不能找出代码中的问题。有什么想法吗?

编辑:经过一些调试和测试,我找到了答案。

#include <iostream>
#include <cmath>
using namespace std;

int main()
{
    while (true)
{
    int binaryValue, c, tempOctal, tempDecimal, octalValue = 0, e;
    cout << "Enter a binary number to convert to octal: ";
    cin >> binaryValue;
    int counter = ceil(log10(binaryValue+1));
    cout << "Counter " << counter << endl;
    int iter;
    if (counter%3 == 0)
    {
       iter = counter/3;
    }
    else if (counter%3 != 0)
    {
       iter = (counter/3)+1; 
    }
    cout << "Iterations " << iter << endl;
    c = binaryValue;
    cout << "C " << c << endl;
    for (int h = 0; h < iter; h++)
    {
        tempOctal = c%1000;
        cout << "3 digit binary part " << tempOctal << endl;
        int count = ceil(log10(tempOctal+1));
        cout << "Digits " << count << endl;
        tempDecimal = 0;
        for (int counterr = 0; counterr < count; counterr++)
        {
            if (tempOctal%10 != 0)
            {
                 e = pow(2.0, counterr);
                 tempDecimal += e;
                 cout << "Temp Decimal value 0-7 " << tempDecimal << endl;
            }
            tempOctal /= 10;
        }
        octalValue += (tempDecimal * pow(10.0, h));
        cout << "Octal Value " << octalValue << endl;
        c /= 1000;
    }
cout << "Final Octal Value: " << octalValue << endl;
}
system("pause");
return 0;

}

【问题讨论】:

    标签: c++ binary octal


    【解决方案1】:

    这看起来过于复杂。不需要涉及浮点数学,它很可能会引入问题。

    当然,显而易见的解决方案是使用预先存在的函数来执行此操作(例如 { char buf[32]; snprintf(buf, sizeof buf, "%o", binaryValue); } 并完成,但如果您真的想“手动”完成,您应该考虑使用位操作:

    • 使用binaryValue &amp; 3 屏蔽掉三个最低位。这些将是您的下一个八进制数字(三位是 0..7,即一个八进制数字)。
    • 使用binaryValue &gt;&gt;= 3 移动数字以将三个新位移到最低位置
    • 之后反转数字,或者(如果可能)从字符串缓冲区的末尾开始并向后发出数字

    【讨论】:

    • 我之所以“手工”做是因为它是我正在阅读的书中的一个练习。如果我真的需要在程序中使用它,我只会使用预先存在的功能。:)
    • 不要使用&amp; 3&gt;&gt;= 3。那是混淆,它只处理基数的特殊情况,即二的幂。使用% base/= base,它会更清晰,并且适用于所有基础(如果base 是常量,编译器将生成相同或更好的代码)。
    【解决方案2】:

    它不理解你的代码;这似乎太复杂了。但是一个 可以肯定的是,如果您要将内部表示转换为 八进制,你将不得不在某个地方除以 8,然后做一个% 8 某处。我没有看到他们。另一方面,我看到两者 10 和 1000 的操作,两者都不应该存在。

    对于初学者,您可能想编写一个简单的函数来转换 一个值(最好是某种类型的unsigned——获取unsigned 就在担心符号之前)使用任何基础的字符串,例如:

    //! \pre
    //!     base >= 2 && base < 36
    //!
    //! Digits are 0-9, then A-Z.
    std::string convert(unsigned value, unsigned base);
    

    这不应该超过大约 5 或 6 行代码。但注意, 正常算法以相反的顺序生成数字:如果你是 使用std::string,最简单的解决方案是push_back每个数字, 然后在最后调用std::reverse,然后返回。否则: C 风格 char[] 效果很好,只要你把它做得足够大。 (sizeof(unsigned) * CHAR_BITS + 2 绰绰有余,即使对于 签名,即使最后有'\0',如果你不需要 返回一个字符串。)只需将指针初始化为buffer + sizeof(buffer),并在每次插入数字时预递减。到 构造您返回的字符串: std::string( pointer, buffer + sizeof(buffer) ) 应该可以解决问题。

    对于循环,结束条件可以简单地是value == 0。 (你每次都将value除以base,所以你 保证达到这个条件。)如果你使用do ... while, 而不仅仅是while,您还可以保证至少有一位数字 输出。

    (如果只是发布代码对我来说会容易得多,但是因为 这显然是作业,我认为最好只是给出指示 关于需要做什么。)

    编辑:我已经添加了我的实现,以及你新的一些 cmets 代码:

    首先对于 cmets:有一个非常误导性的提示:“输入一个 二进制数”听起来用户应该输入二进制;如果你是 读入int,输入的值应该是十进制的。并且有 仍然是 % 1000/ 1000% 10/ 10 我不知道 理解。无论你在做什么,如果没有% 8/ 8,它就不可能是正确的。试试看:例如,输入"128",看看你会得到什么。

    如果你想输入二进制,那么你真的必须输入一个 字符串,然后自己解析。

    我的转换代码是:

    //! \pre
    //!     base >= 2 && base <= 36
    //!
    //! Digits are 0-9, then A-Z.
    std::string toString( unsigned value, unsigned base )
    {
        assert( base >= 2 && base <= 36 );
        static char const digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        char buffer[sizeof(unsigned) * CHAR_BIT];
        char* dst = buffer + sizeof(buffer);
        do
        {
            *--dst = digits[value % base];
            value /= base;
        } while (value != 0);
        return std::string(dst, buffer + sizeof(buffer));
    }
    

    如果你想解析输入(例如二进制),那么像 以下应该可以解决问题:

    unsigned fromString( std::string const& value, unsigned base )
    {
        assert( base >= 2 && base <= 36 );
        static char const digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        unsigned results = 0;
        for (std::string::const_iterator iter = value.begin();
                iter != value.end();
                ++ iter)
        {
            unsigned digit = std::find
                ( digits, digits + sizeof(digits) - 1,
                  toupper(static_cast<unsigned char>( *iter ) ) ) - digits;
            if ( digit >= base )
                throw std::runtime_error( "Illegal character" );
            if ( results >= UINT_MAX / base
                 && (results > UINT_MAX / base || digit > UINT_MAX % base) )
                throw std::runtime_error( "Overflow" );
            results = base * results + digit;
        }
        return results;
    }
    

    它比toString 更复杂,因为它必须处理各种问题 可能的错误条件。它也可能比你更简单 需要;您可能还想修剪空白等(甚至忽略 他们:输入010000000100 0000 更容易出错。

    (另外,find 的结束迭代器有一个 - 1,因为尾随 '\0' 编译器插入到digits。)

    【讨论】:

    • 谢谢:D。这不是真正的家庭作业。我还没到上大学的年龄,所以我买了一本大学教科书,现在正在阅读和做练习。
    • @Okada 得出同样的结论。你正在努力学习,而只是给你答案的人不一定是最好的解决方案。 (如果您想要完整的代码,我很乐意发布它;它很容易放入评论中。但我建议您先尝试自己弄清楚。)
    • @Kanze 真的,谢谢。我想我解决了这个问题并将其发布在上面。我也希望将完整的代码作为参考点(因为我们使用了不同的方法)。
    • @Emile 好的。我将编辑我的答案以在您的新解决方案中添加完整的代码和 cmets。
    【解决方案3】:

    其实我不明白你为什么需要这么复杂的代码来完成你所需要的。

    首先,不存在从二进制到八进制的转换这样的事情(转换到/从十进制等也是如此)。机器始终以二进制运行,对此您无能为力(或不应该)。

    这其实是格式化的问题。也就是说,如何将数字打印为八进制数,以及如何解析八进制数的文本表示。

    编辑:

    您可以使用以下代码打印任意基数的数字:

    const int PRINT_NUM_TXT_MAX = 33; // worst-case for binary
    
    void PrintNumberInBase(unsigned int val, int base, PSTR szBuf)
    {
        // calculate the number of digits
        int digits = 0;
        for (unsigned int x = val; x; digits++)
            x /= base;
    
        if (digits < 1)
            digits = 1; // will emit zero
    
        // Print the value from right to left
    
        szBuf[digits] = 0; // zero-term
    
        while (digits--)
        {
            int dig = val % base;
            val /= base;
    
            char ch = (dig <= 9) ?
                ('0' + dig) :
                ('a' + dig - 0xa);
    
            szBuf[digits] = ch;
        }
    }
    

    例子:

    char sz[PRINT_NUM_TXT_MAX];
    PrintNumberInBase(19, 8, sz);
    

    【讨论】:

    • 对不起,我来澄清一下问题
    • 为什么用复杂的表达式来确定数字? (我只是索引到一个数组中。)为什么还要事先计算计数呢?循环直到val == 0。 (另外:你不能声明一个维度不是常量的 C 样式数组。)
    • @James Kanze:(1) 计算特定数字字符的表达式适用于十进制和十六进制数字。索引到数组 - 也可以,但是这需要额外的初始化。 (2) 您忘记了上面的循环将按 从右到左 的顺序计算数字。您必须知道数字计数才能将每个数字放在正确的位置。 (3) 我没有声明变长数组。
    • @valdo (1) “计算字符的表达式”不能保证适用于十六进制。它也不必要地复杂。对数组的索引更清晰。 (2) 不行,你要么把转换后生成的字符串倒过来,要么干脆从后面填写。至于(3),我看你没有。您更喜欢填充未知长度的数组,可能会导致缓冲区溢出。
    【解决方案4】:

    当您想要一个不同基数的数字时,OP 要求生成的代码是您的科学计算器会执行的操作。

    我认为你的算法是错误的。只是看着它,我看到了一个接近末端的函数。为什么?有一种简单的数学方法可以完成您所说的事情。获得数学部分后,您可以将其转换为代码。

    如果你有铅笔和纸,没有计算器(类似于不使用预建函数),方法是取你所在的基数,将其更改为基数 10,然后更改为你需要的基数。在您的情况下,以 8 为底,以 10 为底,以 2 为底。

    这应该可以帮助您入门。您真正需要的是带有模数的 if/else 语句来获得余数。 http://www.purplemath.com/modules/numbbase3.htm

    然后你必须弄清楚如何获得你想要的输出。也许将余数存储在数组中或输出到 txt 文件。

    (对于这样的问题,我想双修应用数学专业)

    由于您希望从十进制 0 到 256 进行转换,因此最简单的方法是创建函数,例如将它们称为 int binary()、char hex() 和 int octal()。先做二进制和八进制,因为这将是最简单的,因为它们只能用整数表示。

    【讨论】:

      【解决方案5】:
      #include <cmath>
      #include <iostream>
      #include <string>
      #include <cstring>
      #include <cctype>
      #include <cstdlib>
      
      using namespace std;
      
      char*  toBinary(char* doubleDigit)
      {
        int digit = atoi(doubleDigit);
        char* binary = new char();
      
        int x = 0 ;
        binary[x]='(';
        //int tempDigit = digit;
       int k=1;
        for(int i = 9 ; digit != 0; i--)
        {
          k=1;//cout << digit << endl;
          //cout << "i"<< i<<endl;
          if(digit-k *pow(8,i)>=0)
          {
      
      
            k =1;
            cout << "i" << i << endl;
            cout << k*pow(8,i)<< endl;
      
            while((k*pow(8,i)<=digit))
            {
          //cout << k <<endl;
          k++;
            }
            k= k-1;
      
      
      
             digit = digit -k*pow(8,i);
      
            binary[x+1]= k+'0';
            binary[x+2]= '*';
            binary[x+3]= '8';
            binary[x+4]='^';
            binary[x+5]=i+'0';
            binary[x+6]='+';
      
          x+=6;
      
          }
      
        }
        binary[x]=')';
        return binary;
      }
      
      int main()
      {
       char value[6]={'4','0','9','8','7','9'};
      
      
      
       cout<< toBinary(value); 
      
      
      
        return 0 ;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-01-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-11-06
        • 1970-01-01
        相关资源
        最近更新 更多