原文链接

一、进制

(1)概念

几进制指的是几进一,日常生活中常常使用十进制,在计算机语言中,2进制最常见。

同时,还有8进制,16进制等进制。

(2)进制的表示:

①二进制:用前缀 $0b$ 表示;或用后缀 $B$ 表示,如:$01101001B$。

②八进制:用前缀 $0o$ 表示,如 $0o76$ ;或用后缀 $O$ 表示,如:$257O$。

③十进制:用前缀 $0d$ 表示,或用后缀 $D$ 表示。

④十六进制:用前缀 $0x$ 表示,或用后缀 $H$ 表示。

在表示9以上的数时使用用$A、B、C、D、E、F$。

(3)进制的转换

①n进制转10进制

思路:将第a位数字乘以$n^{a-1}$,再作和。

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iomanip>
using namespace std;
int n,b;
string a;
int main()
{
    cin>>n>>a;
    int l=a.size();
    for(int i=1;i<=l;i++)
    {	
        if (a[l-i]>='0'&&a[l-i-1]<='9')
        a[l-i]-='0';
        if ( a[l-i]>='A' && a[l-i]<='F' )
        {
            a[l-i]-='A';
            a[l-i]+=10;
        }
        b+=a[l-i]*pow(n,i-1);
    }
    cout<<b;
    return 0;
}

也可以用C++库函数实现,用到函数strtol()。

用法strtol( 字符串(char*型) , NULL,n )

代码

#include<bits/stdc++.h>
using namespace std;
int n;
char a[1000];
int main()
{
    cin>>n>>a;
    cout<<strtol(a,NULL,n);
    return 0;
}

②10进制转n进制

思路:模n取余法,将十进制数向n取余,记录余数,知道十进制数为零,反向书写余数,即为n进制数。

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iomanip>
using namespace std;
int n,b,i,ans[10000];
int main()
{
    cin>>n>>b;
    while(b>0)
    {
        ans[i++]=b%n;
        b/=n;
    }
    for ( int q=i-1 ; q>=0 ; q-- )
        if(ans[q]>=10&&ans[q]<16 )
        cout<<char(ans[q]+55);
        else cout<<ans[q];
    return 0;
}

③二进制转十六进制。

思路:一个有趣的性质

1位十六进制数码对应 4 位数的二进制数码。

所以将十六进制和二进制之间相互转换时并不需要以 10 进制为中间跳板,直接进行翻译即可。

例如二进制数 1010110111 经过分组可以变为 0010 1011 0111,直接查表(或者口算)可以翻译为 2B7。

反过来也是一样的。

④n进制转m进制

思路:先将n进制转10进制,再将10进制转m进制。

代码:

#include<bits/stdc++.h>
using namespace std;
string a;
int c[10000000],d,e,f,g,sum,ans;
int main()
{
    scanf("%d",&d);
    cin>>a;
    scanf("%d",&f);
    for(int x=0;x<a.size();x++){
        if(a[x]<'A'){
            e=pow(d,a.size()-x-1);
            e*=(a[x]-'0');
            sum+=e;
        }
        else{
            e=pow(d,a.size()-1-x);
            e*=(a[x]-'A'+10);
            sum+=e;
        }
    }
        while(sum>0){
        c[g++]=sum%f;
        sum/=f;
    }
    for(int x=g-1;x>=0;x--){
        if(c[x]>=10)printf("%c",c[x]+'A'-10);
        else printf("%d",c[x]);
    }
    return 0;
}

⑤浮点数的进制转换

思路:先转换整形,再将浮点数部分乘n取整。

(4) 二进制的深入探究

在二进制中一个 0 或 1 的数码被称为一位。一个内存地址对应的 8 位,被称为一字节( Byte )。

一个 int 类型或 float 类型的变量占用 32 位空间。

而一个 char 的变量占用 8 位。

有些变量类型也有无符号数,例如 unsigned int 类型。

这个类型占用和 int 类型一样,为 32 位,但是以放弃存储正负符号为代价,可以储存比 int 多一倍的整数值(0 到$2^{32}$−1,接近4.3×$10^9$ )。

计算机中还有其他的表示数据大小的单位,比如 1 KB 是 $2^{10}$ =1024 字节(大约一千),1 MB 是 $2^{20}$ 字节(大约一百万), 1 GB 是 $2^{30}$ 字节。

在带符号整数的情况下,$-57$ 如何被表示成负数呢?在计算机中又是如何计算 $66-57$ 呢?

我们用 8 位的 $signed$ $char$ 类型来举例

$57$ 用二进制表示为 $0011$ $1001$(补足 8 位)。

如何表示一个负数?

可以占用最高位的一位来表示正负,0 表示非负,1 表示负数。

然后再经过以下方法处理

  1. 用除了第一位的数字表示这个负数的绝对值,第一位变成 1。这样的话 $-57$ 表示为 $1011$ $1001$。

    这种表示方式称为原码。不过计算机不使用这种方式来表示负数。

  2. 将这个负数的绝对值的数全部取反,由 1 变为 0,0 变为 1。这样的话 $-57$ 表示为 $1100$ $0110$。这种表示方式成为反码

    使用反码有一个问题,对于$0$,既可以表示为$0000$ $0000$也可以表示为$1111$ $1111$,所以也不常用。

  3. 先计算这个负数的反码,然后加 1。这样的话 $-57$ 表示为 $1100$ $0111$。

    这是计算机使用的表示数的方法,被称为补码。这种表达方式下,零只有 1 个,而$1111$ $1111$则代表 -1。


二、位运算

(1)定义

直接对整数在内存中的二进制位进行按位操作。

(2)运算符

位运算包括与、或、非、异或等操作。

①与运算:$A∧B$(在$C$语言写作”A&B”)A与B全为真时,得数为真。(即: 1&1 = 1 ; 1&0 = 0 ; 0&0 = 0)

②或运算:$A∨B$(在$C$语言写作” A|B”)A或B中有一个为真是,得数为真。(即:$1|1=1$ ; $1|0=1$ ; $0|0=0$)

③非运算:$¬A$(在$C$语言写作” ~A “)进行取反操作。(即:~1=0 ; ~0=1)

④异或运算:$A⊕B$(在$C$语言写作” $A$^$B$ “ )只有当两个值一假一真时,得数为真,等效于 “ ( A & ~B ) | (~A & B) “

(即:除$1$^$0$得数位1外,得数均为零)

  • 性质1:任何数与 0 异或即为本身。

  • 性质2:任何偶数个相同的数异或后结果为 0。

  • 性质3:任何数异或偶数次另一个数结果不变。

⑤左移:$A<<B$ 将A的二进制向左移B位,空位用 0 补齐。

⑥右移:$A>>B$ 将A的二进制向右移B位,非正数位删去。


I am ordinary yet unique.