当前位置: 首页 > 知识库问答 >
问题:

C十进制到三进制的基数转换

尉迟墨竹
2023-03-14

我正在将整数值转换为三元数(即字符串)。我怀疑应该有一种更好的方法来实现这一点,只需较少的计算工作量。

我的算法直接实现了长除法,可以在十进制和任意基数之间转换数字。

char *get_ternary_str(int value) {
  char buffer[3] = { "000" };
  int dividend = value;

  int i = 0;
  do {
    int remainder = dividend % 3;
    dividend = dividend / 3;
    buffer[i] = ((char)remainder + '0');
    i++;
  } while(dividend >= 1 && i < 3);

  char temp = buffer[2];
  buffer[2] = buffer[0];
  buffer[0] = temp;

  return buffer;
}

即使在这个例子中,三元数是3位长,我的目标是使它成为n位长。也欢迎就如何对n位三元数执行此操作提出建议。

共有3个答案

林浩漫
2023-03-14

如果您关心的是计算时间,您可以使用一个简单而小的查找表来交换一些内存空间。

把这个C(你的问题的早期版本也是用这种语言提出想法的)片段作为我提出的算法的一个例子。

#include <iostream>
#include <array>
#include <vector>
#include <string>
#include <iomanip>
#include <algorithm>
#include <limits>

std::string ternary_from(int value)
{ 
    // The idea is to elaborate three figures (in base 3) at a time
    constexpr int dim = 3 * 3 * 3;

    // Note the values, are "reversed"
    static const std::array<std::string, dim> inner_parts {
        "000", "100", "200", "010", "110", "210", "020", "120", "220", 
        "001", "101", "201", "011", "111", "211", "021", "121", "221", 
        "002", "102", "202", "012", "112", "212", "022", "122", "222" 
    };

    static const std::array<std::string, dim> parts {
        "", "1", "2", "01", "11", "21", "02", "12", "22", 
        "001", "101", "201", "011", "111", "211", "021", "121", "221", 
        "002", "102", "202", "012", "112", "212", "022", "122", "222" 
    };

    if ( value == 0 )
        return std::string{"0"};

    std::string result;

    // Thanks @Chux for recalling that -INT_MIN is UB
    unsigned tmp = value;
    if ( value < 0 )
        tmp = -tmp;

    // note that 'dim' = 27, so you are performing a third of the calculations
    while (tmp >= dim)
    {
        unsigned remainder = tmp % dim;
        // concatenating a string at the end is easier...
        result += inner_parts[remainder];
        tmp = tmp / dim;
    }
    result += parts[tmp];

    if (value < 0)
        result += '-';

    // now the string needs to be reversed. e.g. 3 -> 10(3), not 01 
    std::reverse(result.begin(), result.end());
    return result;
}

int main()
{
    std::vector<int> tests {
        42, -8, 0, 81, 27, -28, std::numeric_limits<int>::max(), std::numeric_limits<int>::min()
    };

    std::cout << "   decimal           ternary\n";
    for (int i : tests)
    {
        std::cout << std::setw(12) << i << std::setw(23) << ternary_from(i) << '\n';
    }
}

It输出

   decimal           ternary
          42                   1120
          -8                    -22
           0                      0
          81                  10000
          27                   1000
         -28                  -1001
  2147483647   12112122212110202101
 -2147483648  -12112122212110202102
宗政和韵
2023-03-14

代码有问题。

返回指向局部变量的指针

返回指向本地(非静态)变量的指针是未定义行为(UB)。不要依赖该内存在函数完成后有效。

char *get_ternary_str(int value) {
  char buffer[3] = ...
  ...
  return buffer;  // UB
}

以负数失败

-1%3--

int remainder = dividend % 3;
...
buffer[i] = ((char)remainder + '0');

空间不足

缓冲区[3]对于字符串“000”来说太小,因为空字符需要空间<代码>缓冲区[3]对于一个3位字符的数组来说足够大,但肯定需要一个字符串。

// char buffer[3] = { "000" };
char buffer[3+1] = { "000" };

从未分配过空字符

当然,结果应该是需要空字符“\0”的字符串。为了确保这一点,需要对法规进行修订。

我的目标是使其具有n位数的长度,也欢迎您就如何对n位数的三元数执行此操作提出建议。

需要一个char缓冲区来处理形成的字符串。一个简单的解决方案是传入一个足够大的缓冲区指针,以应对最坏的情况。我建议也传入缓冲区的大小。

下面是处理任意基数2-36的解决方案。

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

// Maximum buffer size (worst case: INT_MIN and base 2)
#define ITOA_BASE_N (1 + sizeof(int)*CHAR_BIT + 1)

char *itoa_base(char *dest, size_t sz, int i, unsigned base) {
  char buf[ITOA_BASE_N];
  char *s = &buf[sizeof buf - 1];  // set to last character.
  *s = '\0';
  unsigned u = (unsigned) i;  // Use unsigned to cope with INT_MIN ...
  if (i < 0) {
    u = -u;                   // ... as -INT_MIN is UB
  }
  do {
    *(--s) = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[u % base];
    u /= base;
  } while (u);
  if (i < 0) {
    *(--s) = '-';
  }
  size_t size_used = &buf[sizeof buf] - s;
  if (size_used > sz) {
    return NULL;  // or some other error handling.
  }
  return memcpy(dest, s, size_used);
}

该测试使用复合文本进行临时存储,但如果需要,可以传入固定缓冲区。

#define TO_BASE3(x) itoa_base((char [ITOA_BASE_N]){0}, ITOA_BASE_N, (x), 3)

void test3(int x) {
  char *s = TO_BASE3(x);
  // do stuff with `s`, it is valid for until the end of this block
  printf("base10:%11d base3:'%s'\n", x, s);
}

int main(void) {
  test3(0);
  test3(1);
  test3(42);
  test3(-81);
  test3(INT_MAX);
  test3(INT_MIN);
}

输出

base10:          0 base3:'0'
base10:          1 base3:'1'
base10:         42 base3:'1120'
base10:        -81 base3:'-10000'
base10: 2147483647 base3:'12112122212110202101'
base10:-2147483648 base3:'-12112122212110202102'
田化
2023-03-14
  • 您应该使用malloc动态分配缓冲区。
  • 您可以使用memset使用零初始化char数组。
  • 最后,您可以数学计算char数组长度。上限为Log[value]base 3将为您提供字符串的字符大小。请记住添加一个额外的char以使用null。

因为您需要建议,所以我不提供代码。但是,如果您需要进一步的帮助,您可以在评论中指出。

 类似资料:
  • 我可以运行这个程序,但由于某些原因,它会显示/放置随机字符,而不是二进制的初始值,而且我似乎无法将程序从十进制运行回二进制。我该如何改进这些代码。要明确说明它不会将二进制转换为十进制,我将如何将其转换回十进制转换为二进制,如果有一些代码可以帮助我,将不胜感激。

  • 本文向大家介绍十进制到二进制转换,包括了十进制到二进制转换的使用技巧和注意事项,需要的朋友参考一下 十进制数字也可以转换为二进制格式。要将十进制数转换为二进制数,我们需要将数字除以2,直到达到0或1。然后,在每一步骤中,其余部分将分开存储以形成相反的二进制等效数。 在此算法中,我们将遵循递归方法。这将帮助我们在不使用堆栈数据结构的情况下解决问题。在实现中,我们知道函数的递归将遵循内部堆栈。我们将使

  • 问题内容: 我有一个家庭作业,需要在十进制,二进制和十六进制之间进行三向转换。我需要帮助的功能是将十进制转换为十六进制。我几乎不了解十六进制,但是如何将十进制转换为十六进制。我需要一个接受并返回的函数。不幸的是我没有此功能的任何草稿,我完全迷路了。我只有这个。 另外,我不能使用诸如Integer.toHexString()之类的预制函数或任何其他东西,我需要真正地制作算法,否则我什么都不会学。 问

  • Python3 实例 以下代码用于实现十进制转二进制、八进制、十六进制: # -*- coding: UTF-8 -*- # Filename : test.py # author by : www.runoob.com # 获取用户输入十进制数 dec = int(input("输入数字:")) print("十进制数为:", dec) print("转换为二进制为:", bin(dec

  • 本文向大家介绍科学知识:二进制、八进制、十进制、十六进制转换,包括了科学知识:二进制、八进制、十进制、十六进制转换的使用技巧和注意事项,需要的朋友参考一下 一、 十进制与二进制之间的转换 (1) 十进制转换为二进制,分为整数部分和小数部分 ① 整数部分 方法:除2取余,逆序排列,即每次将整数部分除以2,余数为该位权上的数,而商继续除以2,余数又为上一个位权上的数,这个步骤一直持续下去,直到商为0为

  • 问题内容: 我试图在python函数中使三进制十进制数。我的想法是继续除法,直到商和余数相等,但是我似乎无法使它起作用。这是我的代码: 问题答案: 我的想法是继续除法,直到商和余数相等,但是我似乎无法使它起作用。 是的,类似的东西。本质上,您希望保持除以3,然后收集余数。其余的则组成最终的数字。在Python中,您可以用来划分和收集余数。 例子: