打印
[程序源码]

大整数乘法(例程)

[复制链接]
2209|11
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
zwz7219|  楼主 | 2013-11-2 16:56 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
TE, ui, fm, TEST, ST
最近做了一个功率计的项目,检测芯片用的是 CS5460A,它采样到的数据是 24 位,AD 结果(如电压和电流)还要乘以一个系数才可以得到适合显示的 BCD 码,电压和电流的系数都比较大。并且,在计算功率时,还要把两个系数相乘,然后再乘以 24 位的功率值,才得到可显示的数据,最终的结果远大于四字节整数所能表示的数值,因此,需要用到大整数乘法。

在网上搜了一下,没找到合适的,就写了一个,我把此段程序整理出来,放在这里,希望能给需要的朋友带来方便,如果有更好的方法,也请指正。

如果你写的程序,或者你在别的地方看到过类似的程序,也请指出,只能表明我见识不过,还请原谅,但我的程序决不是抄别人的,只想为需要的朋友带来一点方便。

程序实现的是两个四字节无符号数相乘,结果为 8 个字节,根据程序原理,你也可以设计出任意长度的数据相乘。

不过,请注意,项目中使用的芯片是 51 类的,编译器自然也是 KEIL C51,它使用的是大端数据结构,如果将程序移植到使用小端结构的程序中,需要修改后才能使用,但原理相同。下面是程序:

-------------------------------------------------------------
#include <intrins.h>

union
{
    Uint16 D16[2];
    Uint32 D32;
} Data0, Data0;         //乘数和被乘数

Uint8 ResultOfMul[8];   //乘法运算结果


void main(void)
{
    while (1)
    {
        Data0.D32 = 0x12345678;
        Data1.D32 = 0x87654321;
        BigNumMul();    //ResultOfMul 中的数据为 0x09A0CD0570B88D78
        
        Data0.D32 = 0xFFFFFFFF;
        Data1.D32 = 0xFFFFFFFF;
        BigNumMul();    //ResultOfMul 中的数据为 0xFFFFFFFE00000001
    }
}


void BigNumMul(void)
{
    Uint8 i;
    Uint32 Temp;
   
    for (i = 0; i < 8; i++)
    {
        ResultOfMul[i] = 0;
    }
   
    Temp = Data0.D16[1];
    Temp *= Data1.D16[1];
    *(Uint32 *)&ResultOfMul[4] = Temp;
   
    Temp = Data0.D16[0];
    Temp *= Data1.D16[0];
    *(Uint32 *)&ResultOfMul[0] = Temp;
   
    Temp = Data0.D16[0];
    Temp *= Data1.D16[1];
    *(Uint32 *)&ResultOfMul[2] += Temp;
    if (_testbit_(CY))      //if (PSW & 0x80)
    {
        (*(Uint16 *)&ResultOfMul[0])++;;
    }
   
    Temp = Data0.D16[1];
    Temp *= Data1.D16[0];
    *(Uint32 *)&ResultOfMul[2] += Temp;
    if (_testbit_(CY))      //if (PSW & 0x80)
    {
        (*(Uint16 *)&ResultOfMul[0])++;;
    }
}

-------------------------------------------------------------
在上面的程序中,有一句为:

if (_testbit_(CY))      //if (PSW & 0x80)

它表示可以用注释中的语句代替前面的语句,完全等效,只不过,使用前面的语句时,必须在文件的前面使用

#include <intrins.h>

才可以使用它里面的本征函数 _testbit_。此句用来检测进位位被设置,移植到其它类单片机中时,注意替代此句。

相关帖子

沙发
i55| | 2013-11-2 22:18 | 只看该作者
用51的真苦逼,连这个都要自己写。换m0吧

使用特权

评论回复
板凳
ayb_ice| | 2013-11-3 15:53 | 只看该作者
典型的应该用浮点数了

使用特权

评论回复
地板
bingmcu| | 2013-11-3 16:16 | 只看该作者
用移位相加法简单

使用特权

评论回复
5
zwz7219|  楼主 | 2013-11-4 09:04 | 只看该作者
看了几位的回复,各有特点,我简单回答一下:


如果可以用成本低的单片机,就没必要采用成本高的单片机。
单片机是否有替代产品,否则遇到前两年 AVR 那种情况,哭到找不到地方


用浮点数开销太大,并且它的精度也不够,long double 倒是够,但是 C51 不支持,就是支持,开销更大。如果 C51 支持 long long 型的话就不用这样了,那就太好了,直接写就行,都不用函数调用,但是只适合本例,也不支持任意精度(或者说长度)大整数乘法,失去了本文原有的意义。


移位相加法一般用在没有内部乘法器的单片机中,在有内部乘法器的单片机中,移位相加的运算时间远大于直接采用乘法器的方法,对于本例,移位相加法要用 32 次循环(乘数有 32 位)和 32 * 8 次移位(对齐 LSB),在每个循环中,要用 8 + 7 + 6 + 5 次加法(被乘数四个,每次加过后要检测进位)。

使用特权

评论回复
6
ayb_ice| | 2013-11-4 09:18 | 只看该作者
“精度不够吗”

你需要6位半的精度吗

使用特权

评论回复
7
sunhq02| | 2013-11-4 13:24 | 只看该作者
这个用浮点精度应该够了

使用特权

评论回复
8
zwz7219|  楼主 | 2013-11-4 15:59 | 只看该作者
浮点数运算开销大,能用整数运算的就不用浮点数,更何况运算结果还要转回整数,以得到可供显示的 BCD 码,来回折腾,更费事。

使用特权

评论回复
9
戈卫东| | 2013-11-4 16:30 | 只看该作者
这个是汇编语言显现威力的地方. 用C效率差很多...

使用特权

评论回复
10
ccmc| | 2013-11-5 11:07 | 只看该作者
  C51还分大端小端?

使用特权

评论回复
11
zwz7219|  楼主 | 2013-11-5 11:43 | 只看该作者
ccmc 发表于 2013-11-5 11:07
C51还分大端小端?

不管你用什么语言主,只要你使用的数据类型大于一个字节,都分大小端,即使在汇编语言里也是如此,如:你用两个字节表示一个变量,那么这个变量的高 8 位和低 8 位肯定有一个先后的关系。

使用特权

评论回复
12
奔牛滚滚| | 2013-11-6 09:44 | 只看该作者
乘法除法通常要一起用的吧?乘法有了除法怎么办?

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

8

主题

95

帖子

1

粉丝