使用有符号乘数的无符号乘法

Posted

技术标签:

【中文标题】使用有符号乘数的无符号乘法【英文标题】:Unsigned Multiplication using Signed Multiplier 【发布时间】:2020-10-15 16:28:37 【问题描述】:

我被分配了使用有符号乘数执行无符号乘法的任务。尽管多次尝试,我无法得到它。可以这样做吗?

代码:

#include <stdio.h>
int main()

    // Must be short int
    short int a=0x7fff;
    short int b=0xc000;
    unsigned int res1;
    signed int res2;
    
    //unsigned multiplier
    res1= (unsigned short int) a * (unsigned short int) b;
    
    //signed multiplier
    res2= (short int) a * (short int) b;

    printf("res1: 0x%x %d \n res2: 0x%x %d\n",res1,res1,res2,res2);
    return 0;

这是提供的代码。

电流输出:

res1: 0x5fff4000 1610563584 
res2: 0xe0004000 -536854528

预期输出:

res1: 0x5fff4000 1610563584 
res2: 0x5fff4000 1610563584 

工作代码:

#include<stdio.h>
#include<conio.h>
int main()

    short int a = 0x7fff;
    short int b = 0x3000;
    unsigned int unsignedRes;
    signed int signedRes;
    unsigned int ourRes;
    // Unsigned Multiplier
    unsignedRes = (unsigned short int) a * (unsigned short int) b;
    // Signed Multiplier
    signedRes = (short int) a * (short int) b;

    // Our Code using SIgned Multiplier
    ourRes = ((short int)a & ~(0xffffu << 16))*((short int)b & ~(0xffffu << 16));

    printf("Expected: 0x%x\nSigned: 0x%x\nResult: 0x%x",unsignedRes,signedRes,ourRes);
    getch();
    return 0;

【问题讨论】:

目前还不清楚约束是什么。你可以使用带符号的 32 位乘法吗?或者你可以使用多个更小的乘法吗?规则是什么? 唯一给出的是使用有符号乘数并进行无符号乘法。这段代码显示了有符号乘数是什么。我们可以修改乘数,但不能执行任何无符号乘法。 @TomKarzes 对不起,如果我不能更清楚的话。 但由于唯一的限制是不要使用无符号乘数,我认为它可以进行多次小计算。 一种方法是将两个操作数都转换为int。如果int 在您的机器上是 32 位,那么这适用于很多情况。但是,如果结果 >= 2**31,则结果可能会溢出。您可以使用unsigned long long,如果它是 64 位,则可以让您获得正确的结果。但即便如此,如果您尝试将其转换为int,这似乎是问题的一个约束,它可能会溢出。最坏的情况是0xffff 乘以0xffff。结果应该是0xfffe0001,它不能表示为带符号的 32 位整数。它可以表示为一个无符号的 32 位值。 所以不清楚你想如何处理这种情况。 【参考方案1】:

单独的分配short int b=0xc000; 已经实现定义(视频:ISO/IEC 9899:2018 6.3.1.3 p. 3),需要分两部分完成b = b1 * 2^15 + b0 = 0x1*2^15 + 0x4000(假设SHRT_MAX + 1 == 32768)。如果你在两个部分都这样做,并且以这样的方式理解赋值,结果是一个无符号的数据类型,并通过使用 unsigned int ret2signed int 其余的临时变量来执行 a*b == (a1 * 2^15 + a0) * (b1 * 2^15 + b0)

假设输入仅限于unsigned short int 的功能(unsigned 因为0xc000)并且所有类型都必须是有符号类型,但输出除外:

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

int main(void)

   /*
      This whole thing has ben pulled apart for clarity and could (should!) be done
      in less lines of code.
      All implicit type conversions are made explicit.
      We also assume 2s complement (and a little bit more, to be honest).
    */

   /* 
       A and B limited to 0xffff to keep this code simple.
       If you want to get rid of these limits you need to count the bits
       of A and B and make sure that the sum does not exceed sizeof(int)*CHAR_BIT-1
    */
   signed int A = 0x7fff;
   signed int B = 0xc000;

   short int a0, a1;
   short int b0, b1;

   signed int shift = SHRT_MAX+1;

   unsigned int res1, res2;
   /* Additional temporary variables for legibility */
   signed int t0, t1, t2, t3;

   /* Check input range */
   if ((A > 0xffff) || (B > 0xffff)) 
      fprintf(stderr,"Input must not exceed 0xffff! A = 0x%x, B = 0x%x\n",A,B);
      exit(EXIT_FAILURE);
   

   //unsigned multiplier
   res1 = (unsigned int)A * (unsigned int)B;

   //signed multiplier

   /* Compute  A*B == (a1 * shift + a0) * (b1 * shift + b0) */
   a0 = (short int)(A % shift);
   a1 = (short int)(A / shift);
   b0 = (short int)(B % shift);
   b1 = (short int)(B / shift);

   /*
      Multiply out for convenience:

      A*B == (a1 * 2^15 + a0) * (b1 * 2^15 + b0)
          ==  a1 * b1 *2^15 * 2^15
            + a0 * b1 * 2^15
            + a1 * b0 * 2^15
            + a0 * b0
    */
   /*
      Here a1 and b1 are either 0 (zero) or 1 (one) and (SHRT_MAX+1)^2 < INT_MAX
      so t0 cannot overflow.
      You should make use of that fact in production.
    */
   t0 = (signed int)a1 * (signed int)b1 * shift * shift; /* t0 in 0,shift^2 */
   t1 = (signed int)a0 * (signed int)b1 * shift; /* t1 in 0, a0 * shift */
   t2 = (signed int)a1 * (signed int)b0 * shift; /* t2 in 0, b0 * shift */
   t3 = (signed int)a0 * (signed int)b0; /* t3 can get larger than INT_MAX! */

   /* Cannot overflow because floor(sqrt(2^32-1)) = 0xffff and both A and B < 0xfff */
   res2 = (unsigned int)t0 + (unsigned int)t1 + (unsigned int)t2 + (unsigned int)t3;

   printf("res1: 0x%x %d\nres2: 0x%x %d\n",res1, res1, res2, res2);

   exit(EXIT_SUCCESS);

为了保持礼貌,作业的定义相当不明确,但由于我不知道是你的错、老师的错还是变焦连接不好,我试着给你几个方向。如果不是预期的那样,您现在至少应该能够提出正确的问题。

【讨论】:

非常感谢。你救了我。我也在想类似的东西,但找不到分割它的方法。 我注意到您使用了short int a0, a1,b0,b1,但在使用unsigned short int a0, a1,b0,b1 之后。它解决了我的问题。 @fatalcoder524 是的,这将解决您的问题(这就是您首先要做的事情。允许您例如:使用按位运算,这本身就是一个巨大的优势)但你的任务是使用 signed 类型? 任务是,在一个只能进行有符号乘法的系统中,如何使用有符号乘法器得到无符号乘法的答案。

以上是关于使用有符号乘数的无符号乘法的主要内容,如果未能解决你的问题,请参考以下文章

Verilog -- 有符号与无符号运算

C中的无符号整数在java中的处理

C中的无符号十六进制常量?

C中的无符号十六进制常量?

C# 中的无符号字符使用哪种数据类型?

如何从 Java 中的无符号字节中获取浮点数?