Common Lisp には、integer、rational、floating point、complex など、豊富な数値型があります。
参考資料:
- Common Lisp the Language, 2nd Edition の
Numbers Numbers, Characters and Strings(Practical Common Lisp)
はじめに
integer 型
Common Lisp は bignum と呼ばれる真の integer 型を提供します。これは machine word size ではなく、利用可能な総 memory 量だけに制限されます。たとえば次の値は 64 bit integer を大きく overflow します。
* (expt 2 200)
1606938044258990275541962092341162602522202993782792835301376
効率のため、integer は固定 bit 数に制限できます。これは fixnum 型と呼ばれます。表現可能な integer の範囲は次で得られます。
* most-positive-fixnum
4611686018427387903
* most-negative-fixnum
-4611686018427387904
integer を操作する、または integer を返す関数には次のものがあります。
isqrtは、自然数の正確な正の平方根以下で最大の integer を返します。
* (isqrt 10)
3
* (isqrt 4)
2
他の低水準プログラミング言語と同じく、Common Lisp は 16 進数や 36 までの基数のリテラル表記を提供します。例:
* #xFF
255
* #2r1010
10
* #4r33
15
* #8r11
9
* #16rFF
255
* #36rz
35
rational 型
ratio 型の rational number は、分子と分母という 2 つの bignum から構成されます。そのため、どちらも任意の大きさにできます。
* (/ (1+ (expt 2 100)) (expt 2 100))
1267650600228229401496703205377/1267650600228229401496703205376
これは integer と同じく rational クラスの subtype です。
floating point 型
Common Lisp the Language, 2nd Edition, section 2.1.3 を参照してください。
floating point 型は、連続した real number を有限個の bit で表そうとします。つまり多くの real number は正確には表現できず、近似されます。特に 10 進表現と内部の 2 進表現を相互変換するとき、これは厄介な驚きにつながることがあります。floating point number を扱うなら、What Every Computer Scientist Should Know About Floating-Point Arithmetic を読むことを強く勧めます。
Common Lisp standard は複数の floating point 型を許します。精度が低い順に short-float、single-float、double-float、long-float です。これらの精度は処理系 dependent であり、すべての型が 1 つの floating point precision だけを持つ処理系もありえます。
定数 short-float-epsilon, single-float-epsilon, double-float-epsilon and long-float-epsilon は floating point 型の精度の目安を与えるもので、処理系 dependent です。
特に ECL は long-float を C の long double に基づけているため、より高い精度を持ちます。
CL-USER> (lisp-implementation-type)
"ECL"
CL-USER> most-positive-single-float
3.4028235e38
CL-USER> most-positive-double-float
1.7976931348623157d308
CL-USER> most-positive-long-float
1.189731495357231765l4932
floating point literal
floating point number を読むとき、default の型は special 変数 *read-default-float-format* で設定されます。default は SINGLE-FLOAT なので、数値を double precision として確実に読ませたい場合は末尾に d0 suffix を付けます。
* (type-of 1.24)
SINGLE-FLOAT
* (type-of 1.24d0)
DOUBLE-FLOAT
他の suffix は s (short)、f (single float)、d (double float)、l (long float)、e (default、通常は single float) です。
デフォルトの型は変更できますが、single-float 型を仮定しているパッケージを壊す可能性がある点に注意してください。
* (setq *read-default-float-format* 'double-float)
* (type-of 1.24)
DOUBLE-FLOAT
一部の language と違って、数値の末尾に decimal point を 1 つ付けても float にはならない点に注意してください。
* (type-of 10.)
(INTEGER 0 4611686018427387903)
* (type-of 10.0)
SINGLE-FLOAT
floating point error
floating point calculation の結果が大きすぎる場合、floating point overflow が発生します。SBCL (および他の処理系) では default でエラーコンディションになります。
* (exp 1000)
; Evaluation aborted on #<FLOATING-POINT-OVERFLOW {10041720B3}>.
このエラーは処理できます。また、この振る舞いを変更して +infinity を返すようにもできます。SBCL では次のようにします。
* (sb-int:set-floating-point-modes :traps '(:INVALID :DIVIDE-BY-ZERO))
* (exp 1000)
#.SB-EXT:SINGLE-FLOAT-POSITIVE-INFINITY
* (/ 1 (exp 1000))
0.0
これで計算はエラーコンディションなしに静かに続行されます。
floating overflow エラーを無効にする同様の機能は CCL にもあります。
* (set-fpu-mode :overflow nil)
SBCL では floating point mode を調べられます。
* (sb-int:get-floating-point-modes)
(:TRAPS (:OVERFLOW :INVALID :DIVIDE-BY-ZERO) :ROUNDING-MODE :NEAREST
:CURRENT-EXCEPTIONS NIL :ACCRUED-EXCEPTIONS NIL :FAST-MODE NIL)
任意精度
任意の高精度計算には、QuickLisp に computable-reals ライブラリがあります。
* (ql:quickload :computable-reals)
* (use-package :computable-reals)
* (sqrt-r 2)
+1.41421356237309504880...
* (sin-r (/r +pi-r+ 2))
+1.00000000000000000000...
表示する精度は *PRINT-PREC* で設定され、デフォルトは 20 です。
* (setq *PRINT-PREC* 50)
* (sqrt-r 2)
+1.41421356237309504880168872420969807856967187537695...
complex 型
complex number には 5 種類があります。実部と虚部は同じ型でなければならず、rational またはいずれかの floating point 型 (short、single、double、long) にできます。
complex 値は #C reader マクロまたは関数 complex で作成できます。reader マクロでは、実部と虚部に式を使うことはできません。
* #C(1 1)
#C(1 1)
* #C((+ 1 2) 5)
; Evaluation aborted on #<TYPE-ERROR expected-type: REAL datum: (+ 1 2)>.
* (complex (+ 1 2) 5)
#C(3 5)
異なる型を混ぜて構築した場合、両方の部分により高精度の型が使われます。
* (type-of #C(1 1))
(COMPLEX (INTEGER 1 1))
* (type-of #C(1.0 1))
(COMPLEX (SINGLE-FLOAT 1.0 1.0))
* (type-of #C(1.0 1d0))
(COMPLEX (DOUBLE-FLOAT 1.0d0 1.0d0))
complex number の実部と虚部は realpart and imagpart で取り出せます。
* (realpart #C(7 9))
7
* (imagpart #C(4.2 9.5))
9.5
complex arithmetic
Common Lisp の mathematical 関数は一般に complex number を扱え、本来の結果が complex number である場合は complex number を返します。例:
* (sqrt -1)
#C(0.0 1.0)
* (exp #C(0.0 0.5))
#C(0.87758255 0.47942555)
* (sin #C(1.0 1.0))
#C(1.2984576 0.63496387)
文字列から number を読む
parse-integer 関数は文字列から integer を読みます。
parse-number ライブラリは任意の式を evaluate できないため、信頼できない input に対してより安全に使えるはずです。float も parse できます。
* (ql:quickload :parse-number)
* (use-package :parse-number)
* (parse-number "23.4e2")
2340.0
6
Serapeum ライブラリにももちろん parse-float 関数があります。たとえば double float のように、出力の型を指定することもできます。
* (ql:quickload "serapeum")
* (serapeum:parse-float "23.4e2" :type 'double-float)
2340.0d0
;; ^^ double
文字列と数値の相互変換については 文字列セクション を参照してください。
数値の変換
ほとんどの数値関数は必要に応じて型を自動変換します。coerce 関数は、数値型を含むオブジェクトをある型から別の型へ変換します。
Common Lisp the Language, 2nd Edition, section 12.6 を参照してください。
float を rational に変換する
rational and rationalize 関数 は real numeric 引数を rational に変換します。rational は floating point 引数が正確だと仮定します。rationalize は floating point number がその精度の範囲でしか正確でないという事実を利用するため、より単純な rational number を見つけられることがよくあります。
rational を integer に変換する
計算結果が rational number で、その分子が分母の倍数である場合、自動的に integer に変換されます。
* (type-of (* 1/2 4))
(INTEGER 0 4611686018427387903)
floating-point number と rational number の丸め
ceiling, floor, round and truncate 関数は floating point number または rational number を integer に変換します。結果と input の差が第 2 戻り値として返るため、input は 2 つの出力の和になります。
* (ceiling 1.42)
2
-0.58000004
* (floor 1.42)
1
0.41999996
* (round 1.42)
1
0.41999996
* (truncate 1.42)
1
0.41999996
負の数では floor と truncate に違いがあります。
* (truncate -1.42)
-1
-0.41999996
* (floor -1.42)
-2
0.58000004
* (ceiling -1.42)
-1
-0.41999996
類似の関数 fceiling、ffloor、fround、ftruncate は、引数と同じ型の floating point として結果を返します。
* (ftruncate 1.3)
1.0
0.29999995
* (type-of (ftruncate 1.3))
SINGLE-FLOAT
* (type-of (ftruncate 1.3d0))
DOUBLE-FLOAT
number の比較
Common Lisp the Language, 2nd Edition, Section 12.3 を参照してください。
= predicate は、すべての引数が数値的に等しい場合に T を返します。floating point number の比較には、すべての real number を表現できずエラーが蓄積するという性質のため、ある程度の誤差の余地が含まれる点に注意してください。
定数 single-float-epsilon は、1.0 に加えたとき = comparison が fail する最小の number です。
* (= (+ 1s0 5e-8) 1s0)
T
* (= (+ 1s0 6e-8) 1s0)
NIL
これは single-float が常に 6e-8 以内の精度を持つという意味ではない点に注意してください。
* (= (+ 10s0 4e-7) 10s0)
T
* (= (+ 10s0 5e-7) 10s0)
NIL
むしろ、これは single-float が約 7 桁の精度を持つという意味です。一連の計算を行うとエラーが蓄積し、より大きな誤差の許容範囲が必要になることがあります。この場合は絶対差を比較できます。
* (< (abs (- (+ 10s0 5e-7)
10s0))
1s-6)
T
= で数値を比較するときは、型が混在していてもかまいません。数値と型の両方を検査するには eql を使います。
* (= 3 3.0)
T
* (eql 3 3.0)
NIL
数列を操作する
多くの Common Lisp 関数はシーケンスを操作します。シーケンスはリストまたはベクトル(1 次元配列)です。mapping の節を参照してください。
多次元配列に対する操作は配列の節で説明しています。
数値の「無限」シーケンスを含む遅延シーケンスを定義し操作するライブラリもあります。例:
- Clazy は QuickLisp にあります。
- folio2 は QuickLisp にあります。効率的なシーケンスのための
- Series パッケージへのインターフェイスを含みます。
- lazy-seq.
ローマ数字を扱う
format 関数は ~@r ディレクティブで数値をローマ数字に変換できます。
* (format nil "~@r" 42)
"XLII"
ローマ数字を読むための gist by tormaroe があります。
乱数の生成
random 関数は、その引数の型に応じて整数または浮動小数点数の乱数を生成します。
* (random 10)
7
* (type-of (random 10))
(INTEGER 0 4611686018427387903)
* (type-of (random 10.0))
SINGLE-FLOAT
* (type-of (random 10d0))
DOUBLE-FLOAT
SBCL では Mersenne Twister という擬似乱数生成器が使われています。詳細は SBCL manual の 7.13 の節を参照してください。
乱数のシードは *random-state* に保存され、その内部表現は処理系に依存します。関数 make-random-state は、新しい乱数の状態を作成したり、既存の状態をコピーしたりするために使えます。
同じ乱数の集合を複数回使うには、(make-random-state nil) で現在の *random-state* のコピーを作成します。
* (dotimes (i 3)
(let ((*random-state* (make-random-state nil)))
(format t "~a~%"
(loop for i from 0 below 10 collecting (random 10)))))
(8 3 9 2 1 8 0 0 4 1)
(8 3 9 2 1 8 0 0 4 1)
(8 3 9 2 1 8 0 0 4 1)
これはループ内で 10 個の乱数を生成しますが、毎回シーケンスは同じです。なぜなら *random-state* というスペシャル変数が、let フォームの前でその状態のコピーに動的束縛されるからです。
その他の資料:
- random-state パッケージは QuickLisp で利用でき、複数の移植性のある乱数生成器を提供します。
ビット単位の演算
Common Lisp はビット単位の算術演算を行う関数も多数提供しています。よく使われるものを、対応する C/C++ の表現とともに以下に示します。
| Common Lisp | C/C++ | 説明 |
|---|---|---|
| (logand a b c) | a & b & c | 複数 operand の bit-wise AND |
| (logior a b c) | a | b | c | 複数 operand の bit-wise OR |
| (lognot a) | ~a | 単一 operand の bit-wise NOT |
| (logxor a b c) | a ^ b ^ c | 複数 operand の bit-wise exclusive or (XOR) |
| (ash a 3) | a << 3 | bit-wise left shift |
| (ash a -3) | a >> 3 | bit-wise right shift |
負の数は 2 の補数として扱われます。忘れてしまった場合は Wiki ページ を参照してください。
例:
* (logior 1 2 4 8)
15
;; 説明:
;; 0001
;; 0010
;; 0100
;; | 1000
;; -------
;; 1111
* (logand 2 -3 4)
0
;; 説明:
;; 0010 (2)
;; 1101 (-3 の two's complement)
;; & 0100 (4)
;; -------
;; 0000
* (logxor 1 3 7 15)
10
;; 説明:
;; 0001
;; 0011
;; 0111
;; ^ 1111
;; -------
;; 1010
* (lognot -1)
0
;; 説明:
;; 11 -> 00
* (lognot -3)
2
;; 説明:
;; 101 -> 010
* (ash 3 2)
12
;; 説明:
;; 11 -> 1100
* (ash -5 -2)
-2
;; 説明
;; 11011 -> 110
より詳しい説明や他の bit-wise 関数については CLHS page を参照してください。
付録: number tower
太字で実線の box に入っている type は、通常よく使うものです。
Page source: ja/numbers.md