FORTHは「スタックにすべてを積む」言語である。そのため、他言語のように型を区別することは少ないが、数値の桁あふれ(overflow) や ビット単位の操作 を扱うには、内部表現を意識する必要がある。
本章では、gforthで扱うダブル整数(double-cell integer) とビット演算を学び、整数演算をより低レベルで制御する方法を身につける。
数値変換
FOTHのテキストインタープリタは、入力された文字列を数値に変換しようとする。
| 表記 | 例 | 説明 |
|---|---|---|
| <digit><digit>* | 123 | 正の整数 |
| -<digit><digit>* | -123 | 負の整数 |
| <digit><digit>*.<digit>* | 123.45 | 正のダブル整数(12345) |
| -<digit><digit>*.<digit>* | -123.45 | 負のダブル整数(-12345) |
| {+ -}<decimal digit>{.}<decimal digit>*{e E}{+ -}<decimal digit><decimal digit>* | 1e 1e0 1.e 1.e0 +1e+0 | 浮動小数点 |
| 接頭辞 | 例 | 説明 |
|---|---|---|
| & | &123 | 10進数 |
| # | #123 | 10進数 |
| % | %1011 | 2進数 |
| $ | $1B | 16進数 |
| 0x | 0x1B | 16進数 |
BASE(基数)
BASE という変数で、入力時の数値基数を変更できる。
DECIMAL 123 456 . . \ → 123 456
HEX 1A 7B . . \ → 1A 7B
BASE @ DECIMAL . \ → 16 (16進数)
シングルセルとダブルセル
gforthの整数は通常1セル(cell)=32bitまたは64bit(環境依存)である。これを倍のサイズ(2セル)で扱う整数が「ダブル整数」である。
通常、#123. の様に、頭に#をつけ、最後に.をつける。123. の様に書くとgforthがワーニングを表示する。
#123. \ ダブル整数
123. \ ダブル整数 → warning: '123.' is a double-cell integer
#123 \ シングル整数
スタック上の構造
低位・高位の2セルで保存される。( lower upper )
12345.123 \ → 12345123 0
ダブル整数を扱うワードは D+, D-, D*, D/ など、頭に D が付いている。
基本操作ワード一覧
| ワード | スタック効果 | 説明 |
|---|---|---|
2@ |
( addr -- x1 x2 ) | メモリから2セル読み出し |
2! |
( x1 x2 addr -- ) | メモリに2セル書き込み |
2DROP |
( x1 x2 -- ) | ダブル整数を削除 |
2DUP |
( x1 x2 -- x1 x2 x1 x2 ) | ダブル整数を複製 |
D. |
( d -- ) | ダブル整数を表示 |
D+ |
( d1 d2 -- d3 ) | ダブル整数の加算 |
D- |
( d1 d2 -- d3 ) | ダブル整数の減算 |
DNEGATE |
( d -- -d ) | 符号反転 |
DABS |
( d -- ud ) | 符号なしダブル整数 |
DMIN |
( d1 d2 -- d ) | ダブル整数の最小値 |
DMAX |
( d1 d2 -- d ) | ダブル整数の最大値 |
D>S |
( d -- n ) | ダブル整数をシングル整数に変換 |
S>D |
( n -- d ) | シングル整数をダブルに変換 |
ダブル整数を扱う例
1234567890000000000 9876543210000000000 + . \ → -7335632973709551616 桁あふれ
1234567890000000000. 9876543210000000000. D+ D. \ → 11111111100000000000
通常の整数では桁あふれする範囲も、D+ により安全に扱える。
メモリへの格納と読み出し
2@ と 2! により、メモリ上にダブル整数を記録できる。
CREATE BIGNUM 2 CELLS ALLOT
12345678901234567890 1 BIGNUM 2! ok
BIGNUM 2@ D. \ → 30792422974944119506
ビット演算ワードの基礎
ビット単位での操作は、ハードウェア制御・暗号・圧縮などに不可欠である。特にセンサーとの通信や計算能力の低い機器、使用できるメモリが限られている場合に活用される。
| ワード | スタック効果 | 内容 |
|---|---|---|
AND |
( n1 n2 -- n3 ) | ビットごとのAND |
OR |
( n1 n2 -- n3 ) | ビットごとのOR |
XOR |
( n1 n2 -- n3 ) | ビットごとのXOR |
INVERT |
( n1 -- n2 ) | ビット反転(NOT) |
LSHIFT |
( x u -- x<<u ) | 左シフト |
RSHIFT |
( x u -- x>>u ) | 右シフト |
1+, 1-
|
( n -- n±1 ) | 加減算(1単位) |
ビットシフト
10 1 LSHIFT . \ 10 * 2^1 = 20
10 3 LSHIFT . \ 10 * 2^3 = 80
80 3 RSHIFT . \ 80 / 2^3 = 10
HEX
AB CONSTANT BYTE \ 0xAB = 1010 1011b
BYTE 4 RSHIFT . \ → A(上位4bit) A
BYTE 0F AND . \ → B(下位4bit) B
DECIMAL
例題:偶数・奇数判定
: EVEN? ( n -- flag )
1 AND 0= ;
5 EVEN? . \ → 0(奇数)
8 EVEN? . \ → -1(偶数)
ビットANDで最下位ビット(LSB)を確認する簡単な例。
例題:ビットマスクによるフラグ管理
1 CONSTANT FLAG-A
2 CONSTANT FLAG-B
4 CONSTANT FLAG-C
VARIABLE FLAGS
\ AとCをONにする
FLAG-A FLAG-C OR FLAGS !
\ Bフラグが立っているか確認
FLAGS @ FLAG-B AND 0<> .
ビットごとのORやANDを使うことで、複数の状態(ON/OFF)を1つの整数で管理できる。
## 例題:チェックサムの作成(単純XOR法)
: CHECKSUM ( addr len -- n )
0 SWAP 0 DO
OVER I + C@ XOR
LOOP
NIP ;
この簡易的なチェックサム関数は、各バイトを順にXORして1バイトの結果にまとめる。低レベル通信やファイル検査などで使われる基本構造である。
例題:32bitチェックサム
: D-CHECK ( addr len -- dsum )
0 0 2SWAP 0 DO
OVER I + C@ S>D D+ \ バイトをダブルに加算
LOOP
2DROP ;
このように、S>D と D+ を組み合わせることで32bitを超える累積和を安全に保持できる。
例題:ビットカウント(セットビット数)
: BIT-COUNT ( n -- u )
0 SWAP
BEGIN
DUP 0= IF DROP EXIT THEN
DUP 1 AND IF SWAP 1+ SWAP THEN
1 RSHIFT
AGAIN ;
これは、整数の中に「1」がいくつあるかを数える例。ビット演算がループと結びつく典型的なパターンである。
練習課題
- 64bit整数
D+,D*を使って、階乗(factorial)のダブル精度版を作成せよ。 -
AND,OR,XORを用いて、4bitの論理演算表を出力せよ。 -
LSHIFTとRSHIFTを組み合わせて、ビット反転を行う関数を実装せよ。 -
UM*を使って、65535 × 65535の正確な積を計算せよ。 -
CHECKSUMを改良し、16bit(ダブルセル)累積チェックを行うよう変更せよ。