algorithm - 多个 8 位字 block 如何转换为单个数字?

标签 algorithm cpu-architecture

好吧,首先,我知道如果我有一台 8 位计算机,它只能处理 8 位数字,不能高于 8,但我知道仍然可以表示 16 位数字,甚至是 32 位数字,64,128位数字通过在RAM中分配更多内存。 但为了简单起见,我们仅使用 16 位数字作为示例。

假设我们在 ram 中有一个 16 位数字,如下所示:

12 34 <-- That's Hexadecimal btw

我们也将其写成二进制,以防万一你们更喜欢二进制形式:

00010010 00110100 <-- Binary
              &
      4660 in decimal

现在,我们知道计算机无法将这个大数字(4660)理解为一个数字,因为计算机只能理解最多 255 的 8 位数字。因此右侧的字节将保持原样是:

00110100 <-- 52 in decimal

但是左边的字节:

00010010 <-- would be 18 if it was the byte on the right, 
             but since it is on the left, it means that its
             4608 

所以我的问题是,如果计算机只能理解低于 255 的数字,那么它如何将第二个字节读取为 4608,然后它如何将这两个字节解释为单个数字(4660)?

谢谢,如果您感到困惑,请随时在评论中询问我。我已经说得尽可能清楚了。

最佳答案

这与硬件架构相比是更多的编程问题,因为CPU在您的测试用例中仅执行8位操作,并且不了解16位。您的示例是:8 位ALU 上的 16 位算术,通常通过拆分为数字的高半部分和低半部分(然后加入后者)来完成。这可以通过更多方式完成,例如这里的几种方式(使用C++):

  1. 转移

    const int _h=0; // MSB location
    const int _l=1; // LSB location
    BYTE h,l; // 8 bit halves
    WORD hl;  // 16 bit value
    h=((BYTE*)(&hl))[_h];
    l=((BYTE*)(&hl))[_l];
    // here do your 8bit stuff on h,l
    ((BYTE*)(&hl))[_h]=h;
    ((BYTE*)(&hl))[_l]=l;
    

    您需要从 8 位/16 位“寄存器”副本进行复制,这很慢,但有时可以缓解问题。

  2. 指针

    const int _h=0; // MSB location
    const int _l=1; // LSB location
    WORD hl;  // 16 bit value
    BYTE *h=((BYTE*)(&hl))+_h;
    BYTE *l=((BYTE*)(&hl))+_l;
    // here do your 8bit stuff on *h,*l or h[0],l[0]
    

    您不需要复制任何内容,只需使用指针访问*h,*l而不是h,l即可。指针初始化只需完成一次。

  3. 联盟

    const int _h=0; // MSB location
    const int _l=1; // LSB location
    union reg16 
     {
     WORD dw;  // 16 bit value
     BYTE db[2]; // 8 bit values
     } a;
    // here do your 8bit stuff on a.db[_h],a.db[_l]
    

    这与#2相同,但形式更易于管理

  4. CPU 8/16 位寄存器

    即使是 8 位 CPU,通常也有 16 位寄存器,可以通过其半寄存器甚至全寄存器进行访问。例如,在Z80上,您有AF、BC、DE、HL、PC、SP,其中大部分也可以通过其半寄存器直接访问。因此,有使用 hl 的指令,也有单独使用 h,l 的指令。 例如,在 x86 上是相同的:

    mov AX,1234h
    

    与以下相同(除了时间和可能的代码长度之外):

    mov AH,12h
    mov AL,34h
    

简而言之,这就是 8/16 位之间的转换,但我假设您更多地询问如何完成操作。这是通过使用进位标志来完成的(遗憾的是,大多数高级语言都没有使用该标志,然后是汇编语言)。例如,8 位 ALU(x86 架构)上的 16 位加法是这样完成的:

// ax=ax+bx
add al,bl
adc ah,bh

因此,首先添加最低的BYTE,然后添加最高的+进位。欲了解更多信息,请参阅:

有关如何实现其他操作的更多信息,请参阅 bignum 算术的任何实现。

[编辑1]

这里是C++的小示例,说明如何仅使用 8 位算术打印 16 位数字。您可以使用 8 位 ALU 作为构建 block 来进行 N*8 位运算,就像我进行 16 位运算一样......

//---------------------------------------------------------------------------
// unsigned 8 bit ALU in C++
//---------------------------------------------------------------------------
BYTE cy;                    // carry flag cy = { 0,1 }
void inc(BYTE &a);          // a++
void dec(BYTE &a);          // a--
BYTE add(BYTE a,BYTE b);    // = a+b
BYTE adc(BYTE a,BYTE b);    // = a+b+cy
BYTE sub(BYTE a,BYTE b);    // = a-b
BYTE sbc(BYTE a,BYTE b);    // = a-b-cy
void mul(BYTE &h,BYTE &l,BYTE a,BYTE b);    // (h,l) = a/b
void div(BYTE &h,BYTE &l,BYTE &r,BYTE ah,BYTE al,BYTE b);   // (h,l) = (ah,al)/b ; r = (ah,al)%b
//---------------------------------------------------------------------------
void inc(BYTE &a) { if (a==0xFF) cy=1; else cy=0; a++; }
void dec(BYTE &a) { if (a==0x00) cy=1; else cy=0; a--; }
BYTE add(BYTE a,BYTE b)
    {
    BYTE c=a+b;
    cy=DWORD(((a &1)+(b &1)   )>>1);
    cy=DWORD(((a>>1)+(b>>1)+cy)>>7);
    return c;
    }
BYTE adc(BYTE a,BYTE b)
    {
    BYTE c=a+b+cy;
    cy=DWORD(((a &1)+(b &1)+cy)>>1);
    cy=DWORD(((a>>1)+(b>>1)+cy)>>7);
    return c;
    }
BYTE sub(BYTE a,BYTE b)
    {
    BYTE c=a-b;
    if (a<b) cy=1; else cy=0;
    return c;
    }
BYTE sbc(BYTE a,BYTE b)
    {
    BYTE c=a-b-cy;
    if (cy) { if (a<=b) cy=1; else cy=0; }
    else    { if (a< b) cy=1; else cy=0; }
    return c;
    }
void mul(BYTE &h,BYTE &l,BYTE a,BYTE b)
    {
    BYTE ah,al;
    h=0; l=0; ah=0; al=a;
    if ((a==0)||(b==0)) return;
    // long binary multiplication
    for (;b;b>>=1)
        {
        if (BYTE(b&1))
            {
            l=add(l,al);    // (h,l)+=(ah,al)
            h=adc(h,ah);
            }
        al=add(al,al);      // (ah,al)<<=1
        ah=adc(ah,ah);
        }
    }
void div(BYTE &ch,BYTE &cl,BYTE &r,BYTE ah,BYTE al,BYTE b)
    {
    BYTE bh,bl,sh,dh,dl,h,l;
    // init
    bh=0; bl=b; sh=0;   // (bh,bl) = b<<sh so it is >= (ah,al) without overflow
    ch=0; cl=0; r=0;    // results = 0
    dh=0; dl=1;         // (dh,dl) = 1<<sh
    if (!b) return;     // division by zero error
    if ((!ah)&&(!al)) return;   // division of zero
    for (;bh<128;)
        {
        if (( ah)&&(bh>=ah)) break;
        if ((!ah)&&(bl>=al)) break;
        bl=add(bl,bl);
        bh=adc(bh,bh);
        dl=add(dl,dl);
        dh=adc(dh,dh);
        sh++;
        }
    // long binary division
    for (;;)
        {
        l=sub(al,bl);   // (h,l) = (ah,al)-(bh,bl)
        h=sbc(ah,bh);
        if (cy==0)      // no overflow
            {
            al=l; ah=h;
            cl=add(cl,dl);  // increment result by (dh,dl)
            ch=adc(ch,dh);
            }
        else{           // overflow -> shoft right
            if (sh==0) break;
            sh--;
            bl>>=1;     // (bh,bl) >>= 1
            if (BYTE(bh&1)) bl|=128;
            bh>>=1;
            dl>>=1;     // (dh,dl) >>= 1
            if (BYTE(dh&1)) dl|=128;
            dh>>=1;
            }
        }
    r=al;       // remainder (low 8bit)
    }
//---------------------------------------------------------------------------
// print 16bit dec with 8bit arithmetics
//---------------------------------------------------------------------------
AnsiString prn16(BYTE h,BYTE l)
    {
    AnsiString s="";
    BYTE r; int i,j; char c;
    // divide by 10 and print the remainders
    for (;;)
        {
        if ((!h)&&(!l)) break;
        div(h,l,r,h,l,10);  // (h,l)=(h,l)/10; r=(h,l)%10;
        s+=char('0'+r);         // add digit to text
        }
    if (s=="") s="0";
    // reverse order
    i=1; j=s.Length();
    for (;i<j;i++,j--) { c=s[i]; s[i]=s[j]; s[j]=c; }
    return s;
    }
//---------------------------------------------------------------------------

我使用VCL AnsiString进行文本存储,您可以将其更改为任何字符串,甚至char[]。 您需要将整个数字分开,而不仅仅是单独的字节。了解 div 函数的工作原理。这里是 264 打印 264%10... 的最低有效数字的示例...

a = 264 = 00000001 00001000 bin
b =  10 = 00000000 00001010 bin
d =   1 = 00000000 00000001 bin
// apply shift sh so b>=a 
a = 00000001 00001000 bin
b = 00000001 01000000 bin
d = 00000000 00100000 bin
sh = 5
// a-=b c+=d while a>=b
// a<b already so no change
a = 00000001 00001000 bin b = 00000001 01000000 bin c = 00000000 00000000 bin d = 00000000 00100000 bin
// shift right
b = 00000000 10100000 bin d = 00000000 00010000 bin sh = 4
// a-=b c+=d while a>=b
a = 00000000 01101000 bin c = 00000000 00010000 bin
// shift right
b = 00000000 01010000 bin d = 00000000 00001000 bin sh = 3
// a-=b c+=d while a>=b
a = 00000000 00011000 bin c = 00000000 00011000 bin
// shift right
b = 00000000 00101000 bin d = 00000000 00000100 bin sh = 2
b = 00000000 00010100 bin d = 00000000 00000010 bin sh = 1
// a-=b c+=d while a>=b
a = 00000000 00000100 bin c = 00000000 00011010 bin
// shift right
b = 00000000 00001010 bin d = 00000000 00000001 bin sh = 0
// a<b so stop a is remainder -> digit = 4
//now a=c and divide again from the start to get next digit ...

关于algorithm - 多个 8 位字 block 如何转换为单个数字?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41367781/

相关文章:

algorithm - 如何在 OCR 扫描代码中添加冗余

c - 用边长为 2^K, 2^(K - 1), ..., 4, 2, 1 的正方形填充 M x N 矩形

mips - 采取或不采取分支的顺序,可降低分支预测错误率

c - 优化以下 hackerrank 程序

algorithm - 为什么Eratosthenes算法的时间复杂度没有参数sqrt(n)?

java - Java中的合并排序实现问题

c - 使用时间戳计数器测量内存延迟

x86-64 - 使用索引寻址模式时的瓶颈

io - 内存映射 IO - IO 设备如何知道值已更改?

performance - Haswell AVX/FMA 延迟测试比英特尔指南慢 1 个周期