编程语言如何有效地评估基础数学?


22

随着我越来越多地参与编程背后的理论,我发现自己对看似简单的事物着迷并感到困惑。

:这是如何运作的?

因为有!

我讨厌这种认识!我喜欢知识,最重要的是我喜欢学习,这使我想到了我的问题(尽管这是一个广泛的问题)。

题:

如何用编程语言评估基本数学运算符?

当前的方法如何改进?

var = 5 * 5; 

我的解释:

$num1 = 5; $num2 = 5; $num3 = 0;
while ($num2 > 0) {
    $num3 = $num3 + $num1;
    $num2 = $num2 - 1;
}
echo $num3;

这似乎效率很低。对于较高的因子,此方法非常慢,而内置方法的标准是瞬时的。如何在不迭代加法的情况下模拟乘法?

var = 5 / 5;

怎么做到的?我想不出一种从字面上将其5分成5个相等部分的方法。

var = 5 ^ 5; 

加法迭代的迭代?我的解释:

$base = 5;
$mod = 5;
$num1 = $base;
while ($mod > 1) {

    $num2 = 5; $num3 = 0;
    while ($num2 > 0) {
        $num3 = $num3 + $num1;
        $num2 = $num2 - 1;
    }
    $num1 = $num3;
    $mod -=1;
}
echo $num3;

同样,这是极其低效的,但是我想不出另一种方法来做到这一点。这个问题同样适用于自动处理的所有数学相关函数。


1
我有点背叛,我要去大学攻读计算机科学,然后再去生活数学理论以及哲学和理论物理学。愿望很多,时间很少。
Korvin Szanto 2011年

10
假设您已经看过en.wikipedia.org/wiki/Category:Computer_arithmetic的所有链接,是否安全?
JB金

2
从根本上讲,这与您在小学时教您进行多位乘法和长除法的方式相似。取A的一位数,乘以B。移位十。取A的下一个数字,乘以B。对所有数字重复一次,将它们加在一起。因为它是二进制数,所以一位数乘法更简单(它是x0或x1),而不是移位十,而是加倍。划分是相似的。
询问莫妮卡

Answers:


35

要真正了解算术如何在计算机内部工作,您需要使用汇编语言进行编程。最好是一个小的字长并且没有乘法和除法指令的人。类似于6502。

在6502上,几乎所有算术都在称为累加器的寄存器中完成。(寄存器是处理器内部的特殊存储位置,可以快速访问。)因此,要添加两个数字,请将第一个数字加载到累加器中,然后将第二个数字添加到累加器中。

但这过于简单了。由于6502是8位处理器,因此它只能处理0到255之间的数字。大多数时候,您将希望能够使用更大的数字。您必须一次将它们分块添加8位。当两个数字相加的结果使累加器溢出时,处理器将设置一个进位标志。处理器在进行加法运算时将其加进去,因此假设您从数字的最低位字节开始,则可用于“携带1”。在6502上多字节添加如下所示:

  1. 清除进位标志(CLC)
  2. 加载第一个数字的最低顺序字节(LDA,负载累加器)
  3. 添加第二个数字的最低顺序字节(ADC,加进位)
  4. 存储结果的最低位字节(STA,存储累加器)
  5. 对连续较高顺序的字节重复步骤2-4
  6. 如果最后设置了进位,则说明您溢出了;采取适当的措施,例如生成错误消息(BCS / BCC,如果进位设置/清除,则分支)

减法类似,不同之处在于首先设置进位,使用SBC指令代替ADC,最后,如果有下溢,则进位是清除的

可是等等!那负数呢?好吧,有了6502,它们以称为二进制补码的格式存储。假设一个8位数字,-1被存储为255,因为当您将255加到某物上时,累加器中的位数减少了一个(加一个进位)。-2存储为254,依此类推,一直下降到-128,存储为128。因此,对于有符号整数,字节0-255范围的一半用于正数,一半用于负数。(此约定使您可以仅检查数字的高位以查看是否为负。)

可以将它想象成一个24小时制的时钟:将时间加上23将导致时间提前一个小时(第二天)。因此23是时钟的模数等效于-1。

如果使用的字节数超过1个,则负数必须使用更大的数字。例如,16位整数的范围是0-65536。因此65535用于表示-1,以此类推,因为将65535加到任意数字上会导致减1(加一个进位)。

在6502上只有4种算术运算:加,减,乘以2(向左移动)和除以2(向右移动)。二进制处理时,仅使用这些运算即可完成乘法和除法运算。例如,考虑将5(二进制101)和3(二进制11)相乘。与十进制长乘法一样,我们从乘数的右数开始,将101乘以1,得到101。然后,将被乘数向左移,再将1010乘以1,得到1010。然后将这些结果加在一起,得到1111,或者15.由于我们只乘以1或0,所以实际上并没有乘。乘法器的每一位都只是一个标志,告诉我们是否要添加(移位的)被乘数。

除以二进制以外,除法类似于使用试验除数的手动长除法。如果要除以常数,则可以采用类似于减法的方式进行此操作:您可以将其乘以预先计算的1 / X演算,而不是将其除以X,从而产生所需的结果以及上溢。即使在今天,这也快于分裂。

现在尝试在汇编中进行浮点数学运算,或在汇编中将浮点数转换为漂亮的输出格式。请记住,那是1979年,时钟速度是1 MHz,因此您必须尽可能高效地进行操作。

除了更大的字长和更多的寄存器外,今天的事情仍然像这样工作,当然,现在大多数数学运算都是由硬件完成的。但是它仍然以相同的基本方式完成。例如,如果您将移位数相加并乘以一个乘法,则它与具有该指令的早期处理器(例如6809)在执行该指令的早期处理器上的硬件乘法指令所需的周期数有很好的相关性在微代码中的操作方式与手动操作大致相同。(如果您的晶体管预算较大,则可以采用更快的方式进行移位和相加,因此现代处理器不会按顺序执行这些操作,并且可以在一个周期内执行乘法运算。)


3
嘿,谢谢您的详细解释!这正是我想要的!以我的水平,您常常会忘记,支持您的工作通常比您所做的任何事情都要复杂。这就是我要学习计算机科学的确切原因。我讨厌这样的事实,如果我想回到过去,我将不会改变世界,只知道如何制定适当的SQL语句;)无论如何,非常感谢您花时间写出这个答案,您已经给我一个品味测试员,以了解我要研究的内容。
Korvin Szanto 2011年

7
不同意,组装还是太高,如果你想知道计算机如何做算术题,你必须看看硬件,或至少硬件算法
JK。

嗯 一旦知道加法器和移位器,就可以轻松想象它们是由硬件控制还是由软件控制,并且更容易使用软件。
kindall 2011年

4
-1。硬件乘法尚未随移位完成,并且相加已近三十年了,许多CPU可以在一个周期内执行乘法。Binary Multiplier有关详细信息,请参阅Wikipedia文章。
梅森惠勒

24

最后,基本的算术运算是在硬件中完成的。更具体地说,在CPU中(或者实际上是其子部分)

换句话说,它是电子电路。设置适当的位作为输入,您将获得适当的位作为输出。它是基本逻辑门的组合。

http://en.wikipedia.org/wiki/Adder_%28electronics%29

http://en.wikipedia.org/wiki/Binary_multiplier


3
精心设计了硬件算法,可以与硬件分开研究。
S.Lott

@ S.Lott:我觉得您的评论令人困惑。对我来说,算法涉及到您遵循的一系列步骤,一个过程以及可以编程的东西。在这里,我们谈论的是执行基本算术运算的电子电路。换句话说,只是电流流过的一系列栅极。因此,充其量它比“算法”更“逻辑”。我的2美分
dagnelies 2011年

6
算法是“有限的,确定的和有效的”算法,可以在电路中进行,也可以用纸和铅笔完成,或者可以用Tinkertoys或碟子或DNA中的分子完成。算法可以是任何东西。电子电路必须遵循定义的算法。它没有神奇地超越了对算法的需求。
S.Lott

1
将仅包含一个步骤的过程视为“算法”吗?FWIW,电子电路通常遵循真值表-一步处理。真值表最终被“编译”为多层门,这并不能否认它是一个一步的过程。
slebetman,2015年

2
@ S.Lott:一个更合适的第一条评论是:硬件的“逻辑”经过仔细指定,可以与硬件分开研究。确实是这样。对二进制逻辑的研究称为布尔代数。
slebetman 2015年


5

它是通过皮秒在皮秒内完成的。有关详细信息,请参见Google“硬件乘数”等。现代CPU是数十年来不断改进的极其复杂的结果。

顺便说一句,既然您不乘以重复加法,为什么您会想像一台计算机呢?


我的问题是关于功能背后的推理,而不是功能本身,我理解它是由处理器解释的,我很好奇。特别是其背后的理论以及如何以伪代码复制它。
Korvin Szanto 2011年

1
我脑海中的乘法就是记忆。同样,长乘法也需要按照我的方式进行迭代。我将继续讨论长时间乘法的功能
Korvin Szanto 2011年

2
@Korvin,如果您对此感兴趣,我推荐的书将对您有帮助。我还建议Harold Abelson和Gerald Jay Sussman撰写“计算机程序的结构和解释”。它深入处理了这些问题。
乔纳森·亨森

几台早期的计算机仅支持加法和减法。一些只支持减法!因此,将x = y * z的运算实现为do(z次){x + y},类似地,将x = y / z的除法运算为(y> z){x +1; y = y-z}
James Anderson

@詹姆斯:他们支持轮班吗?我希望乘法是通过移位和加法完成的,而除法是移位,比较,减法。
凯文·克莱恩

4

无论如何,这都不意味着是一个彻底的答案,但它应该使您知道如何实现。如您所知,数字以二进制表示。例如,一台计算机可以将数字5表示为00000101。计算机可以执行的一个非常基本的操作是向左移动,将得到00001010(十进制为10)。如果向右移动两次,则为00010100(十进制20)。每次将数字左移1次,数字都会加倍。假设我有一个数字x,我想将其乘以17。我可以将x左移4次,然后将x加到结果中(16x + x = 17x)。这是将数字乘以17的有效方法。这应该使您对计算机如何不使用重复加法就能乘大数字有一些了解。

除法可以使用加法,减法,右移,左移等组合。还有许多技巧可以将数字提高到指数。


需要明确的是,您通常一次只能移位一位以上。因此,这4个移位操作实际上只是一个操作,例如:shl r0, 4
Caleb

4

当我还是个孩子的时候,我学会了如何用钢笔和纸进行乘法和除法,而又不会浪费太多的时间。后来我知道平方根也是可以计算的。

在大学里,我学习了如何通过十二个乘法,除法和加法来计算三角和对数运算。他们称其为泰勒级数。

在那之前,父亲给我写了一本书,其中已经针对数百个值计算了这些复杂的运算,并以表格的形式显示。当您想要两个计算值之间的正弦值时,还有一些估计错误的解释。

整数单元,浮点单元,GPU和DSP只是在硅片上实现了所有这些旧技术。


3

我将尝试向您介绍如何设计数字电路,以解决您所使用的问题:CPU如何实现加法和乘法。

首先,让我们排除直接的问题:编程语言如何有效地评估乘法和加法。答案很简单,他们将它们编译为乘法和加法指令。例如,以下代码:

a = 1 + 1;
b = a * 20;

可以简单地编译成如下形式:

ADD 1 1  a
MUL a 20 b

(请注意,为简单起见,上面的程序集用于一个不存在的虚构CPU)。

至此,您意识到上述答案只是转移了问题并通过硬件魔术解决了问题。后续问题显然是该硬件魔术如何工作?

首先让我们看一个更简单的问题:加法。

首先,我们做一个熟悉的问题,添加常规的以10为底的数字:

 17
+28

第一步是将7和8相加。但这将导致15,这不仅仅是个位数。因此,我们进行1:

(1)
 17
+28
= 5

现在我们将1、1和2加在一起:

 17
+28
=45

因此,我们得到以下规则:

  1. 当加法的结果超过一个数字时,我们保留最低有效数字,并向前携带最高有效数字

  2. 如果我们有一个数字结转到我们的列中,我们将其与我们要添加的数字相加

现在该解释以2为底的布尔布尔代数了。

因此,在布尔代数中,将0和1加在一起=1。将0和0 = 0加在一起。将1和1 = 10加起来,这是一个多位数,因此我们将1向前进行。

由此我们可以构造一个真值表:

a b  |  sum  carry
-------------------
0 0  |   0     0
0 1  |   1     0
1 0  |   1     0
1 1  |   0     1

由此,我们可以构造两个电路/布尔方程-一个用于求和的输出,另一个用于进位的输出。最幼稚的方法是简单列出所有输入。任何真值表,无论多么大和复杂,都可以用这种形式重新表述:

(AND inputs in first row) OR (AND of inputs in second row) OR ...

这基本上是乘积形式的总和。我们仅查看导致结果为1而忽略0的输出:

sum = (NOT a AND b) OR (a AND NOT b)

让我们用编程语言符号替换AND OR和NOT,以使其更易于阅读:

sum = (!a & b) | (a & !b)

基本上,我们将表转换为:

a b  |  sum  equation
-------------------
0 0  |   0   
0 1  |   1   (!a & b)
1 0  |   1   (a & !b)
1 1  |   0   

这可以直接实现为电路:

                _____
 a ------------|     |
    \          | AND |-.     ____
     \  ,-NOT--|_____|  \   |    |
      \/                 `--| OR |----- sum
      /\        _____    ,--|____|
     /  `-NOT--|     |  /
    /          | AND |-`
 b ------------|_____|

细心的读者此时会注意到,上述逻辑实际上可以实现为单个门-XOR门,该门具有我们的真值表所需的行为:

                _____
 a ------------|     |
               | XOR |---- sum
 b ------------|_____|

但是,如果您的硬件没有为您提供XOR门,那么上述步骤就是您将如何使用AND,OR和NOT门定义和实现它的方法。

将逻辑门转换为实际硬件的方式取决于所拥有的硬件。可以使用各种物理机制来实现它们,只要该机制提供某种切换行为即可。逻辑门已经实现了从喷水或空气(流体)到晶体管(电子)到掉落的大理石的一切事物。它本身就是一个很大的话题,因此我将简单介绍一下,并说有可能将逻辑门实现为物理设备。

现在我们对进位信号进行相同的操作。由于只有一个条件中的进位信号为真,因此方程式很简单:

carry = a & b

因此,携带很简单:

                _____
 a ------------|     |
               | AND |---- carry
 b ------------|_____|

将它们组合在一起,我们得到了所谓的半加器:

                _____
 a ------;-----|     |
         |     | XOR |---- sum
 b --;---|-----|_____|
     |   |      _____
     |   '-----|     |
     |         | AND |---- carry
     '---------|_____|

顺便说一下,上述电路的方程式如下:

sum = a ^ b
carry = a & b

半加法器缺少某些内容。我们已经执行了第一条规则-如果结果比结转多了一个数字,但是我们还没有执行第二条规则-如果有进位,则将其与数字相加。

因此,要实现一个完整的加法器,一个可以将多个数字相加的加法电路,我们需要定义一个真值表:

a b c  |  sum  carry
---------------------
0 0 0  |   0     0
0 0 1  |   1     0
0 1 0  |   1     0
0 1 1  |   0     1
1 0 0  |   1     0
1 0 1  |   0     1
1 1 0  |   0     1
1 1 1  |   1     1

现在求和的等式为:

sum = (!a & !b & c) | (!a & b & !c) | (a & !b & !c) | (a & b & c)

如上所述,我们可以通过相同的过程来分解和简化方程式,并将其解释为电路等,但是我认为答案太长了。

到现在为止,您应该对数字逻辑的设计有所了解。我还没有提到其他技巧,例如Karnaugh映射(用于简化真值表)和逻辑编译器(例如espresso)(这样您就不必手工分解布尔方程式),但是基本上我已经掌握了这些技巧上面概述:

  1. 分解问题,直到可以在单个位(数字)级别工作为止。

  2. 使用真值表定义所需的输出。

  3. 将表转换为布尔方程并简化方程。

  4. 将方程解释为逻辑门。

  5. 通过实现逻辑门将您的逻辑电路转换为真实的硬件电路。

这就是真正解决根本(或更底层)问题的方式-很多真相表。真正的创意工作是将诸如MP3解码之类的复杂任务分解为位级别,以便您可以使用真值表进行处理。

抱歉,我没有时间解释如何实现乘法。您可以尝试弄清楚乘法的有效时间,然后以二进制解释它,然后尝试将其分解为真值表,从而对它进行破解。或者,您可以阅读Wikipedia:http : //en.wikipedia.org/wiki/Binary_multiplier


2

基本的算术指令是通过高效的汇编指令执行的。

更复杂的(或抽象的)指令要么通过循环机制在汇编中完成,要么在标准库中处理。

在大学学习数学时,您将开始学习Lambda微积分和Big-O表示法。程序员使用所有这些以及更多内容来评估和创建有效的算法。无论如何,基本的东西通常是在底层完成的,例如在汇编中或在c语言中使用指针。

查尔斯·佩佐尔德(Charles Petzold)撰写的“代码”是对该主题的出色介绍。


1
或查询表。更快地预计算值并查找它们。示例Sin / Cos / Tan(整数除法,尽管已预先计算并存储在硬件中)。
马丁·约克

1

获得一本类似《数字逻辑基础...》的书,我认为这对于大一/大二电子工程专业的学生来说还是很标准的,并逐步解决(编者:这笔钱不多,所以请寻找二手或旧版的)。它)。这将带您了解加法器和乘法器,并为您提供足够的背景知识,让他们开始了解硬件工作背后的一些原理。

在短期内,您的答案将变为“因为它可以整合许多简单的逻辑来唤起这种复杂的行为”,而不是“因为它确实可以”。

如果您想尝试了解有关如何编译和运行程序的所有原理,请结合使用,以便最终了解所有内容在中间如何实现。


1

这里有很多很好的答案。您也从一个正确的想法开始:乘法等复杂的操作是从更简单的操作中构建的。如您所料,没有乘法指令比使用一系列加法有更快的乘法方法。任何乘法都可以实现为较小乘法的总和,也可以实现为移位和加法的组合。例子:

a = 5 + 5 + 5 + 5 + 5;           // 5*5, but takes 5 operations
b = (5 << 2) + 5;                // 5*5 in only 2 operations
c = (41 << 4) + (41 << 2) + 41   // 41*21 in 4 operations

除法同样可以分解为较小的运算。XOR(^)是我看过的每个处理器上的内置指令,但是即使如此,它也可以实现为AND,OR和NOT的组合。

不过,我有种感觉,即与一个处理器提供的指令种类以及如何将这些指令组合成更复杂的操作的一般概念相比,特定的答案对您的满意度较低。对于这种好奇心,没有什么比健康的汇编语言更好的了。这是对MIPS汇编语言的非常平易近人的介绍。


1

这是现代处理器如何实现两个64位整数相乘的方式:

您知道如何进行长手乘法。要将两个10位数字相乘,您可以将一个10位数字乘以另一个数字的10位数字,将11位数字的结果写在另一个数字的下方,然后移位,然后将所有数字相加。

现代处理器使用所有的64 x 64位来做到这一点。但是,两个单个位数的乘积非常简单:1 x 1 = 1,所有其他乘积为零。这是通过逻辑和实现的。与十进制乘积不同,在十进制乘积中结果可以是两位数,而单个位数的二进制乘积始终是一位。

因此,现在您需要添加64行的64位。但是64位数字的64位加法是很糟糕的。因此,处理器使用3/2加法器或7/3加法器:如果您添加3个单位数,则结果可以是0、1、2或3,可以容纳两位。如果您添加7个单一位数,则结果是0到7之间的数字,可以用3位表示。IBM声称他们仅用18个原始电路就可以制造7/3加法器(PowerPC文档),我敢打赌英特尔和ARM也可以做到这一点。

您有4096位,在相同的位位置将它们分为大约600组的7位,然后使用大约600个7/3加法器将结果从4096位减少到小于2,000。然后,一次又一次地执行相同的操作,直到最终得到可以馈送到普通全加器中的成对比特。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.