【问题标题】:How can I multiply a 64-bit unsigned int with a 32-bit unsigned int on C without using long long?如何在不使用 long long 的情况下将 64 位 unsigned int 与 C 上的 32 位 unsigned int 相乘?
【发布时间】:2018-04-04 00:47:13
【问题描述】:

如何在不使用long long 的情况下将 64 位无符号整数与 32 位无符号整数相乘。 64 位数字存储为两个 32 位数字的数组。

换句话说,我怎样才能从abbc 获得ef 给定以下条件:

( a * 2^32 + b ) * c = e * 2^32 + f

abcef 都是 32 位无符号整数,只能进行 32 位数学运算。

【问题讨论】:

  • 您想将 64 位整数与 64 位相乘还是 64 位与 32 位相乘?
  • 64 位与 32 位
  • 您需要将两个 32 位数字拆分为四个 16 位数字。这样,您可以将两个 16 位 "digits" 相乘,而不会溢出 32 位值。剩下的只是小学数学:乘数字,加结果,处理进位。
  • ( a * 2^32 + b ) * c = ( a * c ) * 2^32 + ( b * c ),让您需要两个执行两个 32 位乘法并处理它们的进位。 This 展示了如何将两个 64 位数字乘以两个 64 位数字。您将使用相同的方法将两个 32 位数字乘以两个 32 位数字。这是 user3386109 描述的方法。

标签: c arrays 64-bit


【解决方案1】:

首先要实现的是,我们将获得一个 96 位的数字,如下所示:

(264 - 1) × (232 - 1)
= 264 × 232 - 264 - 232 + 1
= 296 - 264 - 232 + 1
96

接下来要意识到的是,我们不能使用 32 位乘法。即使没有进位,结果也太大了。

(232 - 1) × (232 - 1)
= 232 × 232 - 232 - 232 + 1
= 264 - 233 + 1
≥232

我们需要改为执行 16 位乘法。 16 位乘法可以使用 32 位数字完成,而不会溢出,即使使用 16 位进位也是如此。

FFFF16 × FFFF16 + FFFF16
= FFFF_000016
32

如果我们将数字视为 16 位值,则乘法如下:

                  +-----+-----+-----+-----+
                  | ahi | alo | bhi | blo |
                  +-----+-----+-----+-----+

                              +-----+-----+
                           ×  | chi | clo |
                              +-----+-----+

===========================================

      +-----+-----+-----+-----+-----+-----+
      | dhi | dlo | ehi | elo | fhi | flo |
      +-----+-----+-----+-----+-----+-----+

上面的乘法从小学就知道了,对吧?

      +-----+-----+-----+-----+     +-----+
      | ahi | alo | bhi | blo |  ×  | clo |
      +-----+-----+-----+-----+     +-----+

      +-----+-----+-----+-----+-----+     +-----+
   +  | ahi | alo | bhi | blo |  0  |  ×  | chi |
      +-----+-----+-----+-----+-----+     +-----+

表示以下是我们需要的所有工具:

void mul16c(
   uint16_t i, uint16_t j, uint16_t cin,
   uint16_t *prod_ptr, uint16_t *cout_ptr
) {
   uint32_t prod = i * j + cin;
   *prod_ptr = prod & 0xFFFF:
   *cout_ptr = prod >> 16;
}

void add16c(
   uint16_t i, uint16_t j, uint16_t cin,
   uint16_t *sum_ptr, uint16_t* cout_ptr
) {
   uint32_t sum = i + j + cin;
   *sum_ptr = sum & 0xFFFF:
   *cout_ptr = sum >> 16;
}

我们可以按如下方式使用它们:

void mul64x32(
   uint32_t i[2], uint32_t j,
   uint32_t (*prod_ptr)[2], uint32_t *cout_ptr
) {
   uint16_t i16[4];
   i16[3] = i[1] >> 16;
   i16[2] = i[1] & 0xFFFF;
   i16[1] = i[0] >> 16;
   i16[0] = i[0] & 0xFFFF;

   uint16_t j16[2];
   j16[1] = j[0] >> 16;
   j16[0] = j[0] & 0xFFFF;

   uint16_t p[6];
   {
      uint16_t carry = 0;
      mul16c(i16[0], j16[0], carry, &(p[0]), &carry);
      mul16c(i16[1], j16[0], carry, &(p[1]), &carry);
      mul16c(i16[2], j16[0], carry, &(p[2]), &carry);
      mul16c(i16[3], j16[0], carry, &(p[3]), &carry);
      p[4] = carry;
      p[5] = 0;
   }

   uint16_t q[6];
   {
      uint16_t carry = 0;
      q[0] = 0;
      mul16c(i16[0], j16[1], carry, &(q[1]), &carry);
      mul16c(i16[1], j16[1], carry, &(q[2]), &carry);
      mul16c(i16[2], j16[1], carry, &(q[3]), &carry);
      mul16c(i16[3], j16[1], carry, &(q[4]), &carry);
      q[5] = carry;
   }

   uint16_t product[6];
   {
      uint16_t carry = 0;
      add16c(p[0], q[0], carry, &(product[0]), &carry);
      add16c(p[1], q[1], carry, &(product[1]), &carry);
      add16c(p[2], q[2], carry, &(product[2]), &carry);
      add16c(p[3], q[3], carry, &(product[3]), &carry);
      add16c(p[4], q[4], carry, &(product[4]), &carry);
      add16c(p[5], q[5], carry, &(product[5]), &carry);
   }

   (*prod_ptr)[0] = ( product[1] << 16 ) | product[0];
   (*prod_ptr)[1] = ( product[3] << 16 ) | product[2];
   *cout_ptr      = ( product[5] << 16 ) | product[4];
}

每个数组的元素零应该是数字的最低有效部分。

【讨论】:

    猜你喜欢
    • 2013-08-01
    • 2016-07-29
    • 1970-01-01
    • 1970-01-01
    • 2013-09-22
    • 2012-01-27
    • 2017-09-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多