rtforth 0.6.8

Forth implemented in Rust for realtime application
Documentation
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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
# 比較及邏輯運算

## 十進制、二進制、十六進制

人的十根手指頭,決定了人類使用十進制的數字系統。電腦由數量龐大的微型開關構成,只懂開關,或是 0 與 1,使用的是二進制系統。
為了以更簡捷的方式來閱讀二進制系統,人們將二進制中的數字每四位一組,形成了 16 進制。

| 十進制 | 二進制 | 十六進制 |
|------:|------:|--------:|
| 0     | 0     | 0       |
| 1     | 1     | 1       |
| 2     | 10    | 2       |
| 3     | 11    | 3       |
| 4     | 100   | 4       |
| 5     | 101   | 5       |
| 6     | 110   | 6       |
| 7     | 111   | 7       |
| 8     | 1000  | 8       |
| 9     | 1001  | 9       |
| 10    | 1010  | A       |
| 11    | 1011  | B       |
| 12    | 1100  | C       |
| 13    | 1101  | D       |
| 14    | 1110  | E       |
| 15    | 1111  | F       |

本文中以下標來表示數字的進制。如 11<sub>10</sub> 代表 10 進制的 11。11<sub>16</sub> 代表 16 進制的 11。11<sub>2</sub> 代表 2 進制的 11。

Forth 輸出二進制數字的方法是在數字之前加上 `%` 。輸入十六進制的方法是在數字之前加上 `$`。而將堆疊上的整數以十六進制印出來的指令則是 `h.` 。大多數的 Forth 系統未提供以二進制形式印出整數的指令。因為二進制並不適合人閱讀。

例一:以十六進制顯示 11<sub>10</sub>。
```
rf> 11 h.
B  ok
```

例二:以十進制顯示十六進制的 11<sub>16</sub>。
```
rf> $11 .
17  ok
```

例三:以十六進制印出 -1<sub>10</sub> 和 -2<sub>10</sub>。
```
rf> -1 h.
FFFFFFFFFFFFFFFF  ok
rf> -2 h.
FFFFFFFFFFFFFFFE  ok
```
在此我們看到負數在電腦中是如何用 0 和 1 表示的。若仔細算算,會發現 -1<sub>10</sub> 的十六進制顯示了 16 個 F,每個 F 都是二進制的 1111<sub>2</sub>。因此總共有 64 個二進制的 1 ,這是因為本書使用的是 64 位元的作業系統。 如果使用 32 位元的作業系統,我們會看到 8 個 F 。

## 本節指令集

| 指令 | 堆疊效果及指令說明                        | 口語唸法 |
|-----|----------------------------------------|--------|
| `h.` | ( n -- ) &emsp; 以十六進制顯示堆疊上的數字 | h-dot |

--------
## 真和假

電腦也用 0 和 1 表示真假。Forth 指令 `true` 和 `false` 將真和假放上資料堆疊,讓我們看看它倆的十六進制表示法:

例四:以十六進制印出 `true` 和 `false` 。
```
rf> true h.
FFFFFFFFFFFFFFFF  ok
rf> false h.
0  ok
```
所以 `true` 是所有位元都是 1 的整數,也就是十進制的 -1<sub>10</sub>。 `false` 是所有位元都是 0 的整數,也就是十進制的 0<sub>10</sub> 。

這和許多語言不同,C 語言的 true 是 1 。Forth 以 -1 為真,在進行位元運算時有其好處。請見位元運算一節。

| 指令 | 堆疊效果及指令說明                       | 口語唸法 |
|-----|---------------------------------------|--------|
| `true`  | ( -- true ) &emsp; 回傳一所有位元都為 1 的整數 | true |
| `false` | ( -- false ) &emsp; 回傳一所有位元都為 0 的整數 | false |

----------
## 整數比較

Forth 有許多指令的結果是真或假。其中大宗就是比較指令。在整數部份,Forth 提供了跟 0 比較的指令,以及比較兩個整數的指令。請參考本節的指令集並練習以下例子。

例五:使用 `0<` `0=` `0>` `0<>` 比較 -1 、 0 、 1 和 0 的關係。
```
rf> -1 0< .  0 0= .  1 0> .
-1 -1 -1  ok
rf> 0 0<> .
0  ok
```
所以 -1 < 0, 0 = 0, 1 > 0 且 0 不會不等於 0。

例六:使用 `<` `=` `<>` `>` 比較 2 、 6 、 8 和 6 之間的關係。
```
rf> 2 6 < .  6 6 = .  6 6 <> .  8 6 > .
-1 -1 0 -1  ok
```
所以 2 < 6, 6 = 6, 6 不會不等於 6, 8 大於 6。

例七:`within` 用來判斷一整數 n1 是否落在某個區間 [n2, n3) 。請見指令集對 `within` 的說明。請問依照 `within` 的定義, 3 是否落在  [3, 5) 之間。 5 是否落在 [3, 5) 之間?
```
rf> 3 3 5 within  .
-1  ok
rf> 5 3 5 within  .
0  ok
```

### 本節指令集

| 指令 | 堆疊效果及指令說明                       | 口語唸法 |
|-----|---------------------------------------|--------|
| `0<`    | ( n -- flag ) &emsp; 當 n < 0 時 flag 為真 | zero-less |
| `0=`    | ( n -- flag ) &emsp; 當 n = 0 時 flag 為真 | zero-equals |
| `0<>`   | ( n -- flag ) &emsp; 當 n 為不 0 時 flag 為真 | zero-not-equals |
| `0>`    | ( n -- flag ) &emsp; 當 n > 0 時 flag 為真 | 0-greater |
| `<`     | ( n1 n2 -- flag ) &emsp; 當 n1 < n2 時 flag 為真 | less-than |
| `=`     | ( n1 n2 -- flag ) &emsp; 當 n1 = n2 時 flag 為真 | equals |
| `>`     | ( n1 n2 -- flag ) &emsp; 當 n1 > n2 時 flag 為真 | greater-than |
| `<>`    | ( n1 n2 -- flag ) &emsp; 當 n1 不等於 n2 時 flag 為真 | not-equals |
| `within`    | ( n1 n2 n3 -- flag ) &emsp; 當 n1 落在 [n2, n3) 之間, n2 <= n1, n1 < n3 時 flag 為真 | within |

------------
## 浮點數比較

不熟悉浮點數的人最容易犯的錯,就是採用錯誤的方式判斷浮點數是否相等。本節先說明比較相等時可能遇上的問題。

* 浮點數不是實數,只是近似實數。這近似會有誤差。比如 0.15 + 0.15 和 0.2 + 0.1 從實數觀點來看都應該得到 0.3。但是不幸的,因為浮點運算的誤差很可能得到的只是一個很接近 0.3 的數。
* 浮點數的編碼,+0 和 -0 有不同的編碼。但是都是 0。因此用編碼相等的方式判斷會有 0 不等於 0 的問題。
* 浮點數的 NaN (Not a Number) 可以用 0.0 / 0.0 產生。但也可以用 &infin; &times; 0 產生。同樣是 NaN,但不應該認為這兩種方式產生的數字是相等的。
* 浮點數的編碼方式,在 0.0 的附近能表達的數字很密,遠離 0.0 的地方數字稀疏。因此對靠近 0.0 的地方可以進行較精密的判斷。遠離 0.0 的地方則不行。

因此 Forth 並未提供 `f=` 這樣的指令,以免誤用。
Forth 提供了指令 `f~` 用來比較浮點數是否近似。它是個比較複雜的指令,有三種比較方式。
* 絕對似近,當堆疊為 ( r1 r2 r3 ) ,且 r3 > 0 時,判斷是否 |r1-r2| < r3 。
* 相對似近,當堆疊為 ( r1 r2 r3 ) ,且 r3 < 0 時,判斷是否 |r1-r2| < |r3*(|r1|+|r2|)|。
* 編碼相等,當堆疊為 ( r1 r2 r3 ) ,且 r3 = 0 時,判斷是否 r1 和 r2 的編碼相等。要注意就算是在同一台電腦上,不同的 Forth 系統對於「編碼相等」的解釋很可能不同。

不同的判斷方式有不同的適用場合。

### 以 f~ 比較編碼相等

適用場合:用於二分逼進法最後的停止條件。當重覆得到同一編碼時終止計算。

以下幾個例子,我們利用比較編碼相等的方式,來熟悉浮點數的特性。

例八:0.15 + 0.15 和 0.2 + 0.1 都會得到 0.3 ,請以 `f~` 檢查其編碼是否相等。
```
rf> 0.15e 0.15e f+  0.2e 0.1e f+  0.0e f~ .
0  ok
```
使用 Intel 64bit 的 rtForth 得到的答案是不相等。

例九: 0.0 / 0.0 會得到 NaN,依照 NaN 的規則,NaN 不等於 NaN。但 NaN 的編碼應是一樣的。以下分別以 rtForth 、 SwiftForth 和 gforth 使用 `f~` 測試 NaN 是否等於 NaN。

使用 rtForth
```
rf> 0e 0e f/ 0e 0e f/ 0e f~ .
0  ok
```

使用 gforth
```
0e 0e f/ 0e 0e f/ 0e f~ . -1  ok
```

使用 SwiftForth
```
0e 0e f/ 0e 0e f/ 0e f~ . 0
```

可以看出不同版本的 Forth 對於 Forth 2012 中「編碼應是一樣的」有不同的詮釋。gforth 符合標準,卻不符合 NaN 不等於 NaN 的原則。

### 以 f~ 判斷是否絕對近似

適用場合:當兩個浮點數差的絕對值必須小於某固定值時。比如量測設備的量測容許誤差。

例十:某量測儀器會進行三次量測再求平均。若三次量測結果和平均值距離大於 2.5 微米就會重新量測。當三次量測結果分別是 1032 微米 、 1035 微米 、 1037 微米時是否要重新量測?
```
rf> 1032e 1035e 1037e f+ f+ 3e f/ f.
1034.6666667  ok
rf> 1032e 1034.666e 2.5e f~ .
0  ok
rf> 1035e 1034.666e 2.5e f~ .
-1  ok
rf> 1037e 1034.666e 2.5e f~ .
-1  ok
```
因為 1032 微米和平均值差太多,所以要重新量測。

### 以 f~ 判斷是否相對近似

適用場合:
* 進行數值運算時,可能因演算法未考慮浮點數的誤差而導至運算過程中失去太多的精度。相對近似適合用來分析這類情況。
* 當在意的是兩浮點數差的相對值時。比如一般人會認為 1.0001 公尺和 1.0002 公尺差不多。但會認為 1.1 公釐和和 1.2 公釐差很多。雖然兩者都只差 0.1 公釐。 

例十一:正切函式在 &pi; / 2 附近變化很大。以 `f~` 確定 1.55 和 1.56 的相對近似度可達 1%,但是 tan(1.55) 和 tan(1.56) 的相對近似度不到 30%,落在 30% 和 40% 之間。
```
rf> 1.55e  1.56e  -0.01e f~ .
-1  ok
rf> 1.55e ftan  1.56e ftan  -0.3e f~ .
0  ok
rf> 1.55e ftan  1.56e ftan  -0.4e f~ .
-1  ok
```

例十二:判斷 0.0 是否相對近似於 0.0 ?
```
rf> 0e 0e -0.5e f~  .
0  ok
```
0.0 不相對近似 0.0。這是因為相對判斷的數學式是 |r1-r2| < |r3*(|r1|+|r2|) ,將 0.0 代入 0.0 < 0.0。因此相對近似不適合用在進行比較的兩個數都是 0.0 的場合。

### 其他浮點比較指令

除了判斷近似的 `f~`,Forth 還另有 `f0=` 、 `f0<` 和 `f<`。

指令 `f0=` 判斷浮點數是否為 0.0。指令 `f0<` 判斷浮點數是否小於 0.0。`f<` 則判斷兩個浮點數之間的大小關係。

例十三:cos(1) 是否小於 0.0? sin(0) 是否等於 0.0?
```
rf> 1e fcos f0< .
0  ok
rf> 0e fsin f0= .
-1  ok
```
所以 cos(1) 不小於 0.0,且 sin(0) = 0.0。

例十四:cos(1) 是否小於 0.7? sin(1) 呢?
```
rf> 1e fcos  0.7e f< .  1e fsin  0.7e f< .
-1 0  ok
rf> 1e fcos f.  1e fsin f.
0.5403023 0.8414710  ok
```

### 本節指令集

| 指令 | 堆疊效果及指令說明                       | 口語唸法 |
|-----|---------------------------------------|--------|
| `f0<`   | ( -- flag ) ( F: r -- ) &emsp; 當浮點數 r < 0 時 flag 為真 | f-zero-less-than |
| `f0=`   | ( -- flag ) ( F: r -- ) &emsp; 當浮點數 r = 0 時 flag 為真  | f-zero-equals |
| `f<`    | ( -- flag ) ( F: r1 r2 -- ) &emsp; 當浮點數 r1 < r2 時 flag 為真 | f-less-than |
| `f~`    | ( -- flag ) ( F: r1 r2 r3 -- ) &emsp; 依據 r3 提供三種比較方式:如果 r3 為正,(r1 - r2) 的絕對值小於 r3 時為真。如果 r3 為 0,r1 和 r2 的二進位編碼相同時為真。如果 r3 為負,(r1 - r2) 的絕對值小於 r3 乘以 r1 和 r2 的絕對值的和時為真 | f-proximate |

-----------------------------
## 位元運算 (Bitwise operation)

指令 `true` 代表真,會得到一個所有位元都是 1 的整數。指令 `false` 代表假,會得到一個所有位元都是 0 的整數。那麼,二進位的 111<sub>2</sub> 是真還是假呢?答案蠻簡單的,就是部份為真,部份有假,有 3 個為真,其他的都是假。所以更精確的說,指令 `true` 代表全為真,指令 `false` 代表全為假。

我們可以用二進制數字不同的位元來代表不同事物的真假。比如,我們可以使用最右邊的位元代表冷氣是否打開,用它左邊的位元代表電風扇是否打開。於是,二進制的 11<sub>2</sub> 代表冷氣和電風扇都開著。10<sub>2</sub> 則代表冷氣關著,但電風扇開著。

賦與每個位元意義後,可以對這些位元進行運算。Forth 提供了 `invert` 、 `and` 、 `or` 、 `xor` 、 `lshift` 、 `rshift` 這幾個位元運算指令。以下小節依常用的情境說明這些指令。

### 以 INVERT 否定

指令 `invert` 對堆疊上整數的每個位元個別的進行以下運算:

| 位元 | 結果 |
|---:|---:|
| 0 | 1 |
| 1 | 0 |

也就是將真變成假,假變成真。

例十五:Forth 2012 的標準只提供了 `<` 、 `>` 、 `<>` 及 `=` 四個整數比大小的指令,並未提供 `<=` 和 `>=`。請以 `invert` 計算「 3 大於或等於 2 」的真假
```
rf> 3 2 < invert  .
-1  ok
```
因為「大於或等於」就是「不小於」,所以可以使用 `< invert` 來代替 `>=`。同樣,也可以用 `> invert` 來代替 `<=` 。大多數的 Forth 系統都會提供 `<=` 和 `>=` 指令。同樣的,大多數的 Forth 系統也會提供 Forth 2012 標準中沒有的 `f>` ,因此程式員不必以 `f< invert` 來替代。

由於 `invert` 是對每個位元進行否定,如果我們想要的是「只要有一個位元為真,結果就是假」的這一種否定時,不適合使用 `invert`,此時可以使用 `0=`。rtForth 提供的非標準指令 `not` 是 `0=` 的同義字,

例十六:執行 `1 invert h.` 、 `1 0= h.` 和 `1 not h.` 以瞭解它們的異同。
```
rf> 1 invert h.  1 0= h.  1 not h.
FFFFFFFFFFFFFFFE 0 0  ok
```

### 以 AND 判斷

Forth 程式常用計算來代替邏輯判斷,像是「如果堆疊上的整數為 true 就 n,否則 0」這樣的句子,可以使用位元運算指令 `and` 計算出來。

指令 `and` 對堆疊上兩個整數的每個位元個別的進行以下運算:

| 位元 1 | 位元 2 | 結果 |
|---:|---:|---:|
| 0 | 0 | 0 |
| 0 | 1 | 0 |
| 1 | 0 | 0 |
| 1 | 1 | 1 |

也就是說,只要有一個位元為 0,結果位元就是 0。


例十七:蘋果買超過 5 顆,就每顆便宜 3 元。現在買了 10 顆,請問便宜多少錢?
```
rf> 10 5 >  3 and  10 *  .
30  ok
```
例子中 `10 5 >` 判斷是否多於 5 顆,如果是就會回傳全真。全真和 3 進行 AND 運算,會得到 3。之後的 `10 *` 將每顆便宜 3 元乘於 10 顆,得到總共便宜 30 元。

例十八:承上例,請問買了 4 顆便宜多少錢?
```
rf> 4 5 >  3 and  4 *  .
0  ok
```
作法和上例類似,但是因為 `4 5 >` 得到的是全假,所以便宜 0 元。

之所以能用 and 來計算「如果堆疊上的整數為 true 就 n,否則 0」這樣的問題,就是因為 Forth 的 `true` 的每個位元都是 1 ,和 n 進行 AND 運算會得到 n。而 `false` 的每個位元都是 0,和 n 進行 AND 運算會得到 0。

例十九:溫度大於 25 度,要打開冷氣機。以整數最右邊的位元代表冷氣機。請以 Forth 的邏輯運算計算 26 度時是否要開冷氣。
```
rf> 25e 26e f<  1 and . 
1  ok
```
以上指令中的 `25e 26e f<` 判斷 26 度是否高於 25 度。會在資料堆疊上留下一個全真或全假的整數。之後的 `1` 是十進制的 1<sub>10</sub>,剛好也等於二進制的 1<sub>2</sub>,是代表冷氣機的位元的位置。之後的 `and` 對堆疊上的兩個整數進行位元運算。最後印出的結果是 1 ,代表冷氣機要打開。

```
      1111111111111111111111111111111111111111111111111111111111111111  溫度大於 25 度
and   0000000000000000000000000000000000000000000000000000000000000001  冷氣機
-------------------------------------------------------------------------------------
      0000000000000000000000000000000000000000000000000000000000000001  要開冷氣機
```

因為 Forth 的比較指令回傳的是所有位元全為真或全為假的 `true` 和 `false`,指令 `and` 也可以不用在位元運算,而用邏輯運算的場合,用來檢查兩個比較的結果是否都是 'true'。

例二十:以 `and` 驗證 6 大於 5 且 6 小於 7 。
```
rf> 6 5 >  6 7 <  and .
-1  ok
```

例二十一:以 `and` 驗證 6 大於 5 且 6 小於 3 中至少有一項不成立。
```
rf> 6 5 >  6 3 <  and .
0  ok
```

### 以 OR 合併

指令 `or` 對堆疊上兩個整數的每個位元個別的進行以下運算:

| 位元 1 | 位元 2 | 結果 |
|---:|---:|---:|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 1 |

因此只要有一個位元為 1,結果就會得到 1。`or` 的這個特性可以用來合併數個用 AND 判斷得到的位元。

例二十二:有某個調水溫的設備,當溫度比目標溫度低 3 度時要開熱水,關冷水。比目標溫度高 3 度時要開冷水,關熱水,否則熱水冷水都要開。

|目前溫度|熱水|冷水|
|-------|---|---|
| 比目標溫度低 3 度以上 | 開 | 關 |
| 在目標溫度的正負 3 度內 | 開 | 開 |
| 比目標溫度高 3 度以上 | 關 |  開 |

將以上表格拆成兩張表並分別簡化。

熱水的真值表

|比目標溫度高 3 度以上|熱水|
|-----------------|---|
| 假              | 開 |
| 真              | 關 |


冷水的真值表

|比目標溫度低 3 度以上|冷水|
|-----------------|---|
| 假              | 開 |
| 真              | 關 |

如果我們使用整數最右方的位元代表熱水,在其左方的位元代表冷水,並以 1 代表開, 0 代表關,我們可以分別用以上兩張真值表分別計算這兩個位元的值,再以指令 `or` 合併。

假設目標溫度是 30 度。目前溫度是 28 度,使用 Forth 指令計算如下:
```
rf> 28  30 3 +  > invert  1 and  .
1  ok
rf> 28  30 3 -  < invert  2 and  .
2  ok
rf> 1 2 or  .
3  ok
```
第一行計算熱水是否要開,結果要開。第二行計算冷水是否要開,同樣要開。第三行合併了第一、第二行的結果,得到兩者都要開。使用 invert 指令的原因是,當判斷式為真時要關,為假時要開,所以用 invert 否定。

因為 Forth 的比較指令回傳的是所有位元全為真或全為假的 `true` 和 `false`,指令 `or` 也可以不用在位元運算,而用邏輯運算的場合,用來檢查兩個比較的結果中是否有一個為 'true'。

例二十三:判斷 6 大於 5 和 6 小於 3 中至少有一項成立。
```
rf> 6 5 >  6 3 <  or .
-1  ok
```

例二十四:判斷 6 大於 7 和 6 小於 3 中都不成立。
```
rf> 6 7 >  6 3 <  or .
0  ok
```

### 以 XOR 偵測狀態改變

在之前冷水熱水的例子中,如果我們想知道冷水熱水的開關是否有變化,可以使用 `xor` 指令。

指令 `xor` 對堆疊上兩個整數的每個位元個別的進行以下運算:

| 位元 1 | 位元 2 | 結果 |
|---:|---:|---:|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |

指令 `xor` 只在參與運算的兩個位元不同時為 1,否則為 0。這個特性可以用來偵測哪個位元改變了。 

例二十五:承之前冷水和熱水的例子,原本開關的狀態是二進制的 11<sub>2</sub>,冷水和熱水都開著 。後來變成 10<sub>2</sub>,請見下圖,
```
      11   舊的狀態
xor   10   新的狀態
-------------------
      01   改變了的狀態
```
請以二進制輸入 11<sub>2</sub> 及 10<sub>2</sub> ,以 `xor` 求出有變化的部份後,以十六進位印出,判斷改變的是冷水還是熱水。
```
rf> %11 %10 xor  h.
1  ok
```
因為十六進制的 1<sub>16</sub> 就是二進制的 01<sub>2</sub>,所以變化的部份是熱水。

XOR 運算被廣泛用於錯誤偵測、類神經網路及密碼學中。本書進階課題 [Modbus RTU](modbus.md) 中使用 XOR 計算循環冗餘碼 (CRC)。有興趣可以參考。

### 以 LSHIFT 和 OR 編碼

本節參考了
* W3school 的 [Colors Tutorial](https://www.w3schools.com/colors/default.asp)

整數不只可以表示數字,表示真假,也可以表示其他資訊。用整數表示這些非整數以外資訊的方法稱為編碼 (encoding)。

知道編碼的方式時,可以使用 Forth 的位元運算指令 `lshift` 以及 `or` 來產生編碼。

舉例來說,設計網頁常使用的層疊樣式表 (CSS) 使用 16 進制數值 RRGGBB 來表示顏色。其中 RR 是兩位數的 16 進制數字,代表紅色的成份,GG 和 BB 同樣是兩位數的 16 進制數字,分別代表綠色和藍色的成份。由於兩位數的 16 進制數值可以從 00<sub>16</sub> 到 FF<sub>16</sub> ,也就是十進制的 0<sub>10</sub> 到 255<sub>10</sub>。因此紅、綠、藍各有 256<sub>10</sub> 種變化。這種編碼可以表達 256 &times; 256 &times; 256 等於 16,777,216 種顏色。

例二十六:已知顏色為純紅,也就是 255<sub>10</sub>,請計算其編碼。
```
rf> 255 16 lshift  h.
FF0000  ok
```
上例中的 lshift 是位元左移指令,將 255<sub>10</sub> 往左移了 16 個位元到紅色的位置。如下圖:

```
     0000 0000 0000 0000 1111 1111  十進制的 255
     1111 1111 0000 0000 0000 0000  左移 16 位元
        F    F    0    0    0    0  純紅
```

例二十七:已知顏色的綠色成份為十六進制的 78<sub>16</sub>,藍色成份為十六進制的 F0<sub>16</sub>,求顏色編碼。
```
rf> $78 8 lshift  $f0 or  h.
78F0  ok
```
顏色的編碼為 0078F0<sub>16</sub>。因為綠色的部份在整數第 8 位元到第 15 位元處,所以使用 lshift 將綠色成份左移 8 位元後和藍色的成份合併。如下圖。
```
     00 00 78  綠色成份
     00 78 00  以 lshift 左移 8 位元,也就是兩位十六進制數字
     00 00 f0  藍色成份
     00 78 f0  以 or 合併
```

在處理工業自動化的問題時常將相關的資料編碼。本書進階課題 [Modbus RTU](modbus.md) 除了討論 Modbus 編碼外,為了方便處理從站的 IO,也設計了從站 IO 點的編碼。有興趣可以參考。

### 以 RSHIFT 和 AND 解碼

有了編碼,就需要解碼。前一節我們使用了指令 `lshift` 和 `or` 對顏色編碼,這一節則使用 `rshift` 和 `and` 對顏色進行解碼。

例二十八:請問顏色 5601520<sub>10</sub> 的紅色、綠色、藍色成份各多少?
```
rf> 5601520 $ff0000 and  16 rshift  .
85  ok
rf> 5601520 $00ff00 and  8 rshift  .
120  ok
rf> 5601520 $0000ff and  .
240  ok
```
在上例中,我們先以 `$ff0000 and` 移除色碼中綠色和藍色的部份,只保留紅色的部份,再將結果往右移 16 位元,得到紅色的成份為十進制的 85<sub>10</sub>。再用 `$00ff00 and` 取得綠色的部份,往右移 8 位元得到綠色成份 120<sub>10</sub> 。

這兒的 `$ff0000` 和 `$00ff00` 被稱為遮罩 (mask) ,用來遮住我們不想要的位元,只保留想要的,這是一個之前未提及的,使用 `and` 的技巧。

### 本節指令集

| 指令 | 堆疊效果及指令說明                        | 口語唸法 |
|-----|----------------------------------------|--------|
| `invert` | ( n1 -- n2 ) &emsp; 將 n1 的每個為 0 的位元都變成 1,為 1 的變成 0 | invert |
| `not`    | ( n -- flag ) &emsp; 當 n = 0 時 flag 為真,為 `0=` 的同義字 | not |
| `<=` | ( n1 n2 -- flag ) &emsp; 當 n1 <= n2 時 flag 為真 | less-or-equals |
| `>=` | ( n1 n2 -- flag ) &emsp; 當 n1 >= n2 時 flag 為真 | greater-or-equals |
| `f>` | ( -- flag ) ( F: r1 r2 -- ) &emsp; 當浮點數 r1 > r2 時 flag 為真 | f-greater-then |
| `and`    | ( n1 n2 -- n3 ) &emsp; n3 的每個位元都是將 n1 及 n2 中對應的位元進行 AND 運算的結果 | and |
| `or`     | ( n1 n2 -- n3 ) &emsp; n3 的每個位元都是將 n1 及 n2 中對應的位元進行 OR 運算的結果 | or |
| `xor`    | ( n1 n2 -- n3 ) &emsp; n3 的每個位元都是將 n1 及 n2 中對應的位元進行 XOR 運算的結果 | xor |
| `lshift` | ( n1 n2 -- n3 ) &emsp; 將 n1 左移 n2 個位元。右方補上位元 0。如果 n2 大於或等於系統的位元長度,不同的 Forth 系統會有不同的答案 | l-shift |
| `rshift` | ( n1 n2 -- n3 ) &emsp;  將 n1 右移 n2 個位元。左方補上位元 0。如果 n2 大於或等於系統的位元長度,不同的 Forth 系統會有不同的答案 | r-shift |

-------------
## 本章重點整理

* 二進制
* 十進制
* 十六進制
* 真假值
* 真值表
* 邏輯運算
* 比較運算
* 位元運算
* 編碼
* 解碼
* 遮罩 (mask)

-------------------------------------
## 本章指令集

| 指令 | 堆疊效果及指令說明                        | 口語唸法 |
|-----|----------------------------------------|--------|
| `h.` | ( n -- ) &emsp; 以十六進制顯示堆疊上的數字 | h-dot |
| `true`  | ( -- true ) &emsp; 回傳一所有位元都為 1 的整數 | true |
| `false` | ( -- false ) &emsp; 回傳一所有位元都為 0 的整數 | false |
| `0<`    | ( n -- flag ) &emsp; 當 n < 0 時 flag 為真 | zero-less |
| `0=`    | ( n -- flag ) &emsp; 當 n = 0 時 flag 為真 | zero-equals |
| `0<>`   | ( n -- flag ) &emsp; 當 n 為不 0 時 flag 為真 | zero-not-equals |
| `0>`    | ( n -- flag ) &emsp; 當 n > 0 時 flag 為真 | 0-greater |
| `<`     | ( n1 n2 -- flag ) &emsp; 當 n1 < n2 時 flag 為真 | less-than |
| `=`     | ( n1 n2 -- flag ) &emsp; 當 n1 = n2 時 flag 為真 | equals |
| `>`     | ( n1 n2 -- flag ) &emsp; 當 n1 > n2 時 flag 為真 | greater-than |
| `<=`    | ( n1 n2 -- flag ) &emsp; 當 n1 <= n2 時 flag 為真 | less-or-equals |
| `>=`    | ( n1 n2 -- flag ) &emsp; 當 n1 >= n2 時 flag 為真 | greater-or-equals |
| `<>`    | ( n1 n2 -- flag ) &emsp; 當 n1 不等於 n2 時 flag 為真 | not-equals |
| `within`    | ( n1 n2 n3 -- flag ) &emsp; 當 n1 落在 [n2, n3) 之間, n2 <= n1, n1 < n3 時 flag 為真 | within |
| `f0<`   | ( -- flag ) ( F: r -- ) &emsp; 當浮點數 r < 0 時 flag 為真 | f-zero-less-than |
| `f0=`   | ( -- flag ) ( F: r -- ) &emsp; 當浮點數 r = 0 時 flag 為真  | f-zero-equals |
| `f<`    | ( -- flag ) ( F: r1 r2 -- ) &emsp; 當浮點數 r1 < r2 時 flag 為真 | f-less-than |
| `f>`    | ( -- flag ) ( F: r1 r2 -- ) &emsp; 當浮點數 r1 > r2 時 flag 為真 | f-greater-then |
| `f~`    | ( -- flag ) ( F: r1 r2 r3 -- ) &emsp; 依據 r3 提供三種比較方式:如果 r3 為正,(r1 - r2) 的絕對值小於 r3 時為真。如果 r3 為 0,r1 和 r2 的二進位編碼相同時為真。如果 r3 為負,(r1 - r2) 的絕對值小於 r3 乘以 r1 和 r2 的絕對值的和時為真 | f-proximate |
| `invert` | ( n1 -- n2 ) &emsp; 將 n1 的每個為 0 的位元都變成 1,為 1 的變成 0 | invert |
| `not`    | ( n -- flag ) &emsp; 當 n = 0 時 flag 為真,為 `0=` 的同義字 | not |
| `and`    | ( n1 n2 -- n3 ) &emsp; n3 的每個位元都是將 n1 及 n2 中對應的位元進行 AND 運算的結果 | and |
| `or`     | ( n1 n2 -- n3 ) &emsp; n3 的每個位元都是將 n1 及 n2 中對應的位元進行 OR 運算的結果 | or |
| `xor`    | ( n1 n2 -- n3 ) &emsp; n3 的每個位元都是將 n1 及 n2 中對應的位元進行 XOR 運算的結果 | xor |
| `lshift` | ( n1 n2 -- n3 ) &emsp; 將 n1 左移 n2 個位元。右方補上位元 0。如果 n2 大於或等於系統的位元長度,不同的 Forth 系統會有不同的答案 | l-shift |
| `rshift` | ( n1 n2 -- n3 ) &emsp;  將 n1 右移 n2 個位元。左方補上位元 0。如果 n2 大於或等於系統的位元長度,不同的 Forth 系統會有不同的答案 | r-shift |