如何最好地实施BCD作为练习?

时间:2009-01-16 14:34:31

标签: c++ bitsets

我是一名学习C ++的初学者(自学)程序员,最近我决定将二进制编码的十进制(BCD)类作为练习,因此我可以在Project Euler上处理非常大的数字。我想尽可能基本地从头开始做。

我开始使用一组int,其中输入数字的每个数字都保存为一个单独的int。我知道每个BCD数字只能用4位编码,所以我认为使用整个int这有点矫枉过正。我现在正在使用bitset< 4>的数组。

  1. 使用像这样的库类太过分了吗?
  2. 你认为这是作弊吗?
  3. 有更好的方法吗?
  4. 编辑:这个的主要原因是作为练习 - 我不想使用像GMP这样的库,因为重点是我自己制作课程。有没有办法确保每个十进制数字只使用4位?

6 个答案:

答案 0 :(得分:4)

只需一个注释,使用bitset<4>数组将需要与long数组相同的空间量。 bitset通常通过使用字大小的整数数组作为位的后备存储来实现,因此按位操作可以使用按位字操作,而不是字节操作,因此一次完成更多操作。

另外,我质疑你的动机。在系统之间发送BCD时,BCD通常用作一串数字的打包表示。通常与算术没有任何关系。你真正想要的是一个任意大小的整数算术库,如GMP

答案 1 :(得分:1)

  

使用像这样的库类太过分了吗?

我会根据一组int来对它进行基准测试,看看哪一个表现更好。如果bitset&lt; 4&gt;的阵列是是更快,然后不是它不是矫枉过正。每一点都有助于解决一些PE问题

  

你认为这是作弊吗?

不,完全没有。

  

有更好的方法吗?

像格雷格罗杰斯建议的那样,任意精度库可能是更好的选择,除非你只想学习自己的滚动。从这两种方法中学到一些东西(使用库和编写库)。我很懒,所以我经常使用Python。

答案 2 :(得分:1)

就像Greg Rogers所说的那样,使用bitset可能无法节省任何空间,并且实际上并没有提供任何其他好处。我可能会使用矢量代替。它的大小是它需要的两倍,但每个数字的索引都会更简单,更快速。

如果要使用压缩BCD,可以编写自定义索引函数并在每个字节中存储两位数。

答案 3 :(得分:1)

  
      
  1. 使用像这样的库类太过分了吗?
  2.   
  3. 你认为这是作弊吗?
  4.   
  5. 有更好的方法吗?
  6.   

1&amp; 2:不是真的

3:每个字节有8位,你可以在每个unsigned char中存储2个BCD。

答案 4 :(得分:0)

通常,位操作应用于整数的上下文中,因此从性能方面来看,没有真正的理由去位。

如果你想获得经验,那么这可能会有所帮助

#include <stdio.h>
int main
(
    void
)
{
    typedef struct
    {
        unsigned int value:4;

    } Nibble;

    Nibble nibble;

    for (nibble.value = 0; nibble.value < 20; nibble.value++)
    {
        printf("nibble.value is %d\n", nibble.value);
    }

    return 0;
}

问题的关键是在 struct 中,你要创建一个4位宽的短整数。在引擎盖下,它仍然是一个整数,但对于您的预期用途,它看起来像4位整数。

for 循环清楚地显示了这一点,实际上是无限循环。当半字节值达到16时,该值实际上为零,因为只有4位可以使用。 结果 nibble.value&lt; 20 永远不会成真。

如果您查看K&amp; R White白皮书,其中一个注意事项是,像这样的位操作不可移植,所以如果您想将程序移植到另一个平台,它可能会也可能不会起作用。

玩得开心。

答案 5 :(得分:0)

您正在尝试获得基数10表示(即数组的每个单元格中的十进制数字)。这样就浪费了空间(每个数字一个int)或时间(每个数字4位,但是有打包/解包的开销)。

为什么不尝试使用base-256,例如,使用字节数组?或者甚至是带有整数数组的base-2 ^ 32?这些操作的实现方式与base-10相同。唯一不同的是将数字转换为人类可读的字符串。

它可能像这样工作: 假设base-256,每个“digit”有256个可能的值,因此数字0-255都是单个数字值。比256写的是1:0(我会用冒号来分隔“数字”,我们不能使用像base-16那样的字母),base-10中的analoge是如何在9之后,有10个。 同样地,1030(基数-10)= 4 * 256 + 6 = 4:6(基数为256)。 此外,1020(基数-10)= 3 * 256 + 252 = 3:252(基数为256)是基数为256的两位数。

现在让我们假设我们将数字放在首字母数组中,其中最低有效数字首先出现:

unsigned short digits1[] = { 212, 121 }; // 121 * 256 + 212 = 31188
int len1 = 2;
unsigned short digits2[] = { 202, 20  }; // 20 * 256 + 202 = 5322
int len2 = 2;

然后添加会像这样(警告:提前记事本代码,可能会被破坏):

unsigned short resultdigits[enough length] = { 0 };
int len = len1 > len2 ? len1 : len2; // max of the lengths
int carry = 0;
int i;
for (i = 0; i < len; i++) {
    int leftdigit = i < len1 ? digits1[i] : 0;
    int rightdigit = i < len2 ? digits2[i] : 0;
    int sum = leftdigit + rightdigit + carry;
    if (sum > 255) {
        carry = 1;
        sum -= 256;
    } else {
        carry = 0;
    }
    resultdigits[i] = sum;
}
if (carry > 0) {
    resultdigits[i] = carry;
}

在第一次迭代中它应该是这样的:

  1. sum = 212 + 202 + 0 = 414
  2. 414&gt; 256,所以carry = 1和sum = 414 - 256 = 158
  3. resultdigits [0] = 158
  4. 在第二次迭代中:

    1. sum = 121 + 20 + 1 = 142
    2. 142&lt; 256,所以carry = 0
    3. resultdigits [1] = 142
    4. 所以最后resultdigits [] = {158,142},即142:158(base-256)= 142 * 256 + 158 = 36510(base-10),正好是31188 + 5322

      请注意,将此数字转换为人类可读形式的数字绝不是一项简单的任务 - 它需要乘法和除法10或256,如果没有适当的研究,我不能将代码作为样本。优点是操作'add','subtract'和'multiply'可以非常有效,并且基本10的重转换只在开始时进行一次,在计算结束后进行一次。

      说了这么多,就个人而言,我会在字节数组中使用base 10而不关心内存丢失。这将需要将上面的常数255和256分别调整为9和10。