0%

《动手写虚拟机》加减乘除

前面咱们已经实现声明变量、打印变量以及终止程序

这篇文章咱们实现加减乘除

INT64、FLOAT64两个类型合并成NUMBER类型

在实现加法的过程中,我发现如果数字类型分成太多比如int8、int16、int32、float8等等,这将对虚拟机的编写带来很多的不便利

这让我想到了为什么js中的数字类型只有一个number,原来是有道理的

所以我将INT64、FLOAT64两个类型合并成了NUMBER类型,对应于Golang中的float64,虽然像1这种数字完全用不着float64

但是咱们的虚拟机定位不是系统语言,用用就可以了,所以我选择了空间换复杂度的做法

ADDQ等指令后缀Q去掉

既然我们把所有数字类型统一成了一个number类型,那咱们自然就不用区分64位或者32位的数字了,后缀Q自然就要删除了

解决计算精度问题

在我实现乘法运算的时候,发现Golang计算精度问题(其实很多语言都有)

两个float64类型的小数相乘,比如1.11 * 2.22,发现并不等于2.4642,而是后面还带个尾巴2.4642000000000004

所以顺带解决了这个精度问题,当然同步的降低了运算效率

这里就是通过降低效率的代价解决了精度问题

代码逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
switch instruction.opCode {
case ADD:
currentStackFrame := vm.stack.GetTopStackFrame()
v1, err := currentStackFrame.Pop().GetNumber()
if err != nil {
return err
}
v2, err := currentStackFrame.Pop().GetNumber()
if err != nil {
return err
}
float64_, err := strconv.ParseFloat(go_decimal.Decimal.Start(v1).AddForString(v2), 64)
if err != nil {
return err
}
currentStackFrame.Push(&Value{data: float64_, valueType: ValueType_NUMBER})
case SUB:
currentStackFrame := vm.stack.GetTopStackFrame()
v1, err := currentStackFrame.Pop().GetNumber()
if err != nil {
return err
}
v2, err := currentStackFrame.Pop().GetNumber()
if err != nil {
return err
}
float64_, err := strconv.ParseFloat(go_decimal.Decimal.Start(v1).SubForString(v2), 64)
if err != nil {
return err
}
currentStackFrame.Push(&Value{data: float64_, valueType: ValueType_NUMBER})
case MUL:
currentStackFrame := vm.stack.GetTopStackFrame()
v1, err := currentStackFrame.Pop().GetNumber()
if err != nil {
return err
}
v2, err := currentStackFrame.Pop().GetNumber()
if err != nil {
return err
}
float64_, err := strconv.ParseFloat(go_decimal.Decimal.Start(v1).MultiForString(v2), 64)
if err != nil {
return err
}
currentStackFrame.Push(&Value{data: float64_, valueType: ValueType_NUMBER})
case DIV:
currentStackFrame := vm.stack.GetTopStackFrame()
v1, err := currentStackFrame.Pop().GetNumber()
if err != nil {
return err
}
v2, err := currentStackFrame.Pop().GetNumber()
if err != nil {
return err
}
float64_, err := strconv.ParseFloat(go_decimal.Decimal.Start(v1).DivForString(v2), 64)
if err != nil {
return err
}
currentStackFrame.Push(&Value{data: float64_, valueType: ValueType_NUMBER})

开源地址

go-vm

下篇预告

跳转




微信关注我,及时接收最新技术文章