查尔斯·佩佐德(Charles Petzold)的“代码”(Code)是从头开始构建计算机的迷人之处。从第11章开始,介绍创建逻辑门。这些可用于(除其他外)将数字相加在一起。
我的第一个想法是试着自己做一些这样的东西。然而,我不知道从哪里获得必要的补给,也不知道如何焊接。我甚至不确定是否需要焊接(或者如何拼写焊接)。
我能做的就是用代码实现逻辑门。我们试试吧。
与门可以被认为是由导线串联的两个开关。必须同时按下两个开关才能使信号通过导线。
使用传统的0表示低(或无)电压,1表示高电压,我们可以在JavaScript中将其写为。
函数(a,b){if(A){if(B){return 1;}}return 0;}and(0,0);//=>;0 and(0,1);//=>;0 and(1,0);//=>;0 and(1,1);//=>;1。
同样,我们可以用两个并联开关来构建OR门。如果按下任何一个开关,信号就会通过电线。
函数(a,b){if(A){return 1;}if(B){return 1;}return 0;}or(0,0);//=>;0or(0,1);//=>;1or(1,0);//=>;1or(1,1);//=>;1
请注意,我们可以在代码中某种程度上看到逻辑门的结构。由于嵌套的条件,AND门看起来像是串联的。OR门看起来像是并行的,每个条件独立执行。
函数(INPUT){IF(INPUT){RETURN 0;}RETURN 1;}INVERT(0);//=>;1INVERT(1);//=>;0。
我们可以使用反相器AND来创建NAND。可以将其视为AND的反函数-只有当两个输入都为1时,它才返回0。
函数(a,b){return invert(and(a,b));}NAND(0,0);//=>;1nand(0,1);//=>;1nand(1,0);//=>;1nand(1,1);//=>;0。
通过组合AND、OR和NAND,我们可以创建我们需要的最后一个门-异或(XOR)。当且仅当其一个输入为1时,它才返回1。
函数(a,b){return and(或(a,b),NAND(a,b),);}xor(0,0);//=>;0xor(0,1);//=>;1xor(1,0);//=>;1xor(1,1);//=>;0。
到现在为止还好。目前还没有太疯狂的事。但是我们如何使用这些来将二进制数相加呢?
让我们考虑一下如何添加十进制数-例如,16+7。
我们从把最低有效位加在一起开始。6加7等于13。我们使用数字3,但将1进位到下一个加法。对于下一个数字,我们加上1(从16开始),0(从07开始),以及进位1,剩下2。结合这些数字,我们得到23。
对于二进制数,这是完全相同的。我们将两个数字的每一位加在一起,必要时进位1。对于单个比特,这看起来如下所示:
0+0=00+1=11+0=11+1=10。
结果有两个组成部分-求和本身和将应用于下一位数的潜在进位(";Carry";):
0+0=0求和0进位0+1=1求和0进位1+0=1求和0进位1+1=0求和1进位。
在1+1示例中,我们将1转到下一个二进制位,结果是10(或十进制的2)。
这里有趣的是,结果的";sum";部分的输出与XOR相同,进位的输出与AND相同。我们可以使用这些逻辑门来实现这个逻辑,创建一个所谓的半加法器。
function(a,b){return{:XOR(a,b),:and(a,b),};}halfAdder(0,0);//=>;{sum:0,进位:0}halfAdder(0,1);//=>;{sum:1,进位:0}halfAdder(1,0);//=>;{sum:1,进位:0}HalfAdder(1,1);//=。{SUM:0,进位:1}
这并不能完全满足我们的需求(这就是为什么它只是一个加法器的一半)。要真正将二进制数字相加在一起,我们需要能够处理前面数字相加可能产生的进位。
函数(a,b,进位){const initialAddition=halfAdder(a,b);const addtionWithCarry=halfAdder(Carryin,initialAddition.sum);return{:addtionWithCarry.sum,:or(initialAddition.carry,addtionWithCarry.Carry),};}fullAdder(0,0,0);//=>;{sum:0,进位:0}fullAdder。//=>;{sum:1,进位:0}fullAdder(1,1,0);//=>;{sum:0,进位:1}fullAdder(0,0,1);//=>;{sum:1,进位:0}fullAdder(1,0,1);//=>;{sum:0,进位:1}fullAdder(1,1,1);//=>;{sum:1,进位:1}。
使用全加器,我们可以将一个数字中的每个数字相加,并将进位转移到每个数字。对于两个4位数字(0-16),如下所示:
//我选择将二进制数表示为数组,其中//最低有效位是第一位。常量编号A=[1,0,1,0];//5常量编号B=[0,1,1,1];//14//每个数字相加。最初进位为0,//我们将每个加法的进位前移到下一个。Const First=fullAdder(number A[0],number B[0],0);const Second=fullAdder(number A[1],number B[1],first.进位);const Third=fullAdder(number A[2],number B[2],Second d.Carry);const First=fullAdder(number A[3],number B[3],Third d.Carry);const result=[first.sum,Second d.sum,Third d.sum。
(读者练习:在循环中实现这一点,以便可以添加任何大小的数字)。
请注意,任何剩余进位都会作为最后一位相加,并且输出数字可以具有比输入更多的位数。这是有意义的,因为两个4位数字加起来可以是一个5位数字。
我们创造的电路叫做纹波加法器。出于性能原因,现代计算机实现加法的方式往往略有不同,但它们可以使用这种设计。
我仍然认为尝试用物理导线和晶体管来建造它会很有趣。但软件的好处之一是我们可以模拟事物并了解它们是如何工作的,而不需要构建物理上的东西。