二进制转8421BCD码的算法

1. BCD码的求和

BCD码使用4位二进制数来表示十进制中0~9这10个数的数码。例如,十进制的237,其BCD码就是0010_0011_0111,但是其二进制是1110_1101

我们先来研究两个4位的BCD码相加的情况。设这两个BCD码对应的十进制是a,b,其中a,b∈{0,1,2,...,9}。此时只有3种情况:

  1. 0≤a+b≤9
  2. 10≤a+b≤15
  3. 16≤a+b≤18

也就是说:

  • 对于第一种情况,结果本身就是对应的BCD码。例如,0100+0101=1001,即4+5=9;
  • 对于第二种情况,其结果对于4位运算来说没有产生进位,但是结果超过了BCD码表示的范围(因为4位BCD码最多表示9)。例如,5+8=13,0101+1000=1101
  • 对于第三种情况,其结果对于4位运算来说不仅产生了进位,而且其结果也超过了BCD码表示的范围。例如,4+13=17,0100+1101=1_0001

第一种情况显然不需要再修正。
第二种情况,例如,5+8=13,我们希望得到BCD码是0001_0011,但是运算结果1101,因此如果我们加上了6,就可以得到正确结果:1101+0110=0001_0011。这是因为,十进制是逢十进一,但是4位BCD加法,在看作是二进制数做加法时,是逢十六进一。因此,如果结果是10≤a+b≤15,加上6以后就是16+0≤a+b+6≤16+5,此时因为逢十六进一的原因,就得到了结果1_0≤[a+b+6]≤1_5,这个结果就是对的。
第三种情况,因为16≤a+b≤18,逢十六进一后,我们得到了1_0≤[a+b]≤1_2,为了使结果正确,如果我们加上一个修正值6,就得到1_6≤[a+b+6]≤1_8,从而结果也变得正确。

综上所述,如果两个BCD码相加:

  • 如果结果小于9,则不做操作
  • 如果结果大于9,则需要加上6作为修正值

考虑一个例子,比如 35+99=134。35和99的BCD码分别是0011_01011001_1001。先计算低4位:0101+1001=1110,因为这个值大于9,因此加上6作为修正:1110+0110=1_0100。现在计算高四位,同时注意到还有一个进位:0011+1001+0001=1101,这个值还是大于9,加上6,得到1101+0110=1_0011。因此最终结果是1_0011_0100,这刚好就是134的BCD码。

我们之所以能够安全地加上进位,是因为BCD加法比照的就是十进制的加法,只不过前者是4位为一个单位,而后者是以1位数字作为一个单位。加上修正值后,BCD加法的进位就相当于十进制加法的进位。图示如下:


2. 二进制转BCD码

给定一个二进制数,要转BCD码。一个常用算法就是不断将该数除以10,以此依次分解出个位、十位、百位……上的数字,这些数字的4位二进制数就是对应的BCD。但是这样的算法需要不断做除法操作十分的麻烦。我们可以使用名为加三左移法来完成。

这个算法基于以下的事实:

  • 一个数乘以2,相当于其二进制左移1位
  • 两个BCD码相加,如果结果大于9,需要加上6作为修正

一个n位二进制数h=b_{n-1}b_{n-2}\dotsc b_0,其展开是h=\sum_{i=0}^{n-1} b_i2^i如果使用秦九韶算法的嵌套形式写法,可以写成:h=2(2(\dotsc(2(2b_{n-1}+b_{n-2})+b_{n-3})\dotsc)+b_1)+b_0或者若令\begin{cases} \mathcal{H}_i&=2\mathcal{H}_{i+1}+b_{i}\\ \mathcal{H}_{n-1}&=b_{n-1}\end{cases}h=\mathcal{H}_0如果使用这种形式,我们先计算的是\mathcal{H}_{n-1},然后是\mathcal{H}_{n-2}=2\mathcal{H}_{n-1}+b_{n-2},然后是\mathcal{H}_{n-3}=2\mathcal{H}_{n-2}+b_{n-3},……,最后是\mathcal{H}_{0}=2\mathcal{H}_{1}+b_{0}

注意到2\mathcal{H}_i就是把\mathcal{H}_i左移1位,这样就会在最右边空出一个位,之后再加b_{i-1}就是用b_{i-1}填充这个最低位,从而我们得到了\mathcal{H}_{i-1}。不断左移,最终就能得到h,现在我们来设计一个算法使得左移结束后能得到对应的BCD码。

R是一个无限长的、初始状态为所有位都是0的理想寄存器,h是欲转换的数。我们使用下面的归纳法来构造证明我们通过不断左移最终能够得到存储在R中的h对应的BCD码:

  1. 初始:h左移b_{n-1}进入R,通过若干运算后,R中是\mathcal{H}_{n-1}的BCD码。这个显然是成立的,因为\mathcal{H}_{n-1}=b_{n-1}是1位(不是0就是1,对应BCD码就是本身),将其左移进R后,R的值立即是\mathcal{H}_{n-1}的BCD码

  2. 假设:设某一时刻,已经将b_i左移入R,通过若干运算后,此时R中是\mathcal{H}_{i}的BCD码

  3. 归纳:现在准备移入b_{i-1},我们希望这个步骤结束后,R的值是\mathcal{H}_{i-1}的BCD码。因为此时R\mathcal{H}_{i}的BCD码,现在对R从最低位开始每四位作为一个单位u,即将R划分为R=\dotsc\dotsc u_{2}u_{1}u_{0},设i=0,做如下处理:

    1. 如果从u_i开始之后全部为0,则过程结束
    2. 否则如果u_i<5,则转第4步
    3. 否则,令u_i=u_i+3,保留4位
    4. i=i+1,转第1步

    为什么要加3呢?这是因为如果u_i\geqslant 5,那么加法结果要加修正值6,也就是2u_i+6,这等价于2(u_i+3);如果u_i<5,那么加法结果就不需要修正。此外,因为R目前本身就是BCD码,因此必然u_i\leqslant9,从而加3不会产生进位。如此处理后,将R左移一位,也就是乘以2,此时得到的就是2\mathcal{H}_{i-1}的BCD码。现在,因为乘以2的关系,R必然是偶数,故而BCD最低位的数值u_0\leqslant8,于是加上b_{i-1}后有u_0+b_{i-1}\leqslant9。从而,得到的就是\mathcal{H}_{i-1}正确的BCD码。

由数学归纳原理,移动len(h)次后,我们最终可以得到h的BCD码。

作为一个例子,考虑使用该算法将134的二进制1000_0110转为BCD码:

  1. 初始:R=..._0000_0000,h=1000_0110(下面使用#作为占位符)
  2. R中的u_i均小于5,不做处理。R左移1位,h左移一位进入R:R=..._0000_0001,h=0000_110#
  3. R中的u_i均小于5,不做处理。R左移1位,h左移一位进入R:R=..._0000_0010,h=0001_10##
  4. R中的u_i均小于5,不做处理。R左移1位,h左移一位进入R:R=..._0000_0100,h=0011_0###
  5. R中的u_i均小于5,不做处理。R左移1位,h左移一位进入R:R=..._0000_1000,h=0110_####
  6. R中的u_0=8\geqslant5,对其做加3处理得到R=..._0000_1011。R左移1位,h左移一位进入R:R=..._0001_0110,h=110#_####
  7. R中的u_0=6\geqslant5,对其做加3处理得到R=..._0001_1001。R左移1位,h左移一位进入R:R=..._0011_0011,h=10##_####
  8. R中的u_i均小于5,不做处理。R左移1位,h左移一位进入R:R=..._0110_0111,h=0###_####
  9. R中的u_0=7\geqslant5,u_1=6\geqslant5,对其都做加3处理得到R=..._1001_1010。R左移1位,h左移一位进入R:R=..._0001_0011_0100,h=####_####

现在,h已经全部移入,此时R的值就是0001_0011_0100,它就是134的BCD码。

C语言的算法如下:

#include<stdio.h>
#define N 50
#define DIGITS_NUM 8*sizeof(byte_t)
typedef char byte_t;

byte_t regstr[N];

void resetRegstr(){
    for(int i=0;i<N;i++) regstr[i]=0;
}

void show8421bcd(){
    int i=0;
    for(i=0;i<N;i++){
        if(regstr[i]!=0) break;
    }
    if(i==N) {
        printf("0\n");
        return;
    }
    for(;i<N;i++){
        byte_t unit=regstr[i];
        for(int j=0;j<DIGITS_NUM/4;j++){
            printf("%d",(unit>>(4*(DIGITS_NUM/4-j-1)))&0xF);
        }
    }
    printf("\n");
} 

byte_t _processUnit(byte_t unit){
    byte_t newval=0;
    for(int j=0;j<DIGITS_NUM/4;j++){
        int val=0xF&(unit>>(4*j));
        newval|=(val+(val>4?3:0))<<(4*j);
    }
    return newval;
}

int to8421bcd(byte_t *num,int n){
    resetRegstr();
    for(int k=0;k<n;k++){
        byte_t digit=num[k];
        for(int i=DIGITS_NUM-1;i>-1;i--){
            byte_t bi=(digit>>i)&0x1;
            for(int j=N-1;j>-1;j--){
                byte_t bi_=_processUnit(regstr[j]);
                regstr[j]=(bi_<<1)|bi;
                bi_=(bi_>>(DIGITS_NUM-1))&0x1;
                if(bi_==1&&j==0) return 0;
                bi=bi_;
            }
        }
    }
    return 1;
}

void main(){
    byte_t nums[]={/*2,321,973,245,437,681,127 */
        0x20,0x39,0x4E,0x5D,0x48,0x46,0x1D,0xE7
    };
    int ok=to8421bcd(nums,8);
    if(!ok){
        printf("something wrong...\n");
    }else{
        show8421bcd();
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容

  • 进制基本概念 什么是进制?进制是一种计数的方式,数值的表示形式 常见的进制十进制、二进制、八进制、十六进制 进制书...
    极客江南阅读 1,992评论 0 11
  • 二进制运算 八位七位六位五位四位三位二位一位111111111286432168421 按位与&1.两个二进制值的...
    云木杉阅读 582评论 0 0
  • 我们现实生活中用的最多的就是十进制,逢十进一. 但是我们的计算机为什么要采用二进制? 如果懂电路的朋友就很容易理解...
    温柔小黄阅读 1,620评论 0 1
  • 参考https://zh.wikipedia.org/wiki/ASCII ASCII, American Sta...
    极客圈阅读 19,370评论 0 2
  • Java源码 Integer Integer的签名如下,继承了Number类并实现Comparable接口 Com...
    wngn123阅读 1,243评论 0 2