Javaで作っている自作Lispの数字型について考察しています。
浮動小数
common lispの浮動小数には、4つもの精度があります!
| タイプ名 | 精度 | 文字列表記 |
|---|---|---|
| SHORT-FLOAT | 短精度 | 例: 1.23s0 |
| SINGLE-FLOAT | 単精度 | 例: 1.23f0 |
| DOUBLE-FLOAT | 倍精度 | 例: 1.23d0 |
| LONG-FLOAT | 高精度(処理系依存) | 例: 1.23l0 |
分裂していたLisp方言を統一するため作成されたcommon lispの仕様ですが、
その頃はLispマシンというLisp専用のコンピュータが存在していて、
浮動小数の扱いがコンピュータ毎に違っていたそうです。
そのため、どの専用機であっても不公平にならないように4タイプもの精度が決められたそうです。
4つの精度を全て実装する必要はなく、1つだけでも良いそうです。
なおcommon lispの仕様が決まったのは、以下の年度です。
| 規格・名称 | 発行(承認)年 |
|---|---|
| CLtL1 | 1984年 |
| CLtL2 | 1990年 |
| ANSI Common Lisp | 1994年 |
IEEE 浮動小数点規格
「IEEE 浮動小数点規格」が決まった年を調べてみると、たしかにcommon lispの規格より後の年度になっています。
| 規格・名称 | 発行(承認)年 |
|---|---|
| IEEE 754-1985 | 1985年 |
| IEEE 854-1987 | 1987年 |
など
SBCLでは2タイプらしい
SBCLでは、2タイプにまとめられているそうです。
上2つが単精度、下2つが倍精度になっています。
| タイプ名 | 精度 | 文字列表記 |
|---|---|---|
| SHORT-FLOAT | 単精度 | 例: 1.23s0 |
| SINGLE-FLOAT | 単精度 | 例: 1.23f0 |
| DOUBLE-FLOAT | 倍精度 | 例: 1.23d0 |
| LONG-FLOAT | 倍精度 | 例: 1.23l0 |
Javaの浮動小数事情
Javaは初期のバージョンから一貫して IEEE 754-1985に完全に準拠するよう設計されています。
本Lispでは、JavaのFloatとDoubleをそのまま使っている
結果的に、SBCLと同じです。
| Lispタイプ名 | 精度 | Lisp文字列表記 |
|---|---|---|
| SHORT-FLOAT | javaのFloat | 例: 1.23s0 |
| SINGLE-FLOAT | javaのFloat | 例: 1.23f0 |
| DOUBLE-FLOAT | javaのDouble | 例: 1.23d0 |
| LONG-FLOAT | javaのDouble | 例: 1.23l0 |
(タイプ名は、まだ実装していません。たぶん実装しないと思う)
固定小数
common lispでは、固定小数をfixnumとか、bignumと呼びます。
本Lispは、Java25で実装しており、LispからJava呼び出しを簡単に行える事も目指している為、Javaの数値型をそのまま使います。
| データ型 | 最小値 | 最大値 |
|---|---|---|
| int | -2,147,483,648 | 2,147,483,647 |
| long | -9,223,372,036,854,775,808 | 9,223,372,036,854,775,807 |
| BitInteger | 不明 | 不明 |
Javaのintの動作
以下は、Javaのプログラムです。
public static void main(String[] args) {
int x = Integer.MAX_VALUE;
System.out.println("x = " + x);
x = x + 1;
System.out.println("x + 1 = " + x);
}
実行結果は以下になります。
x = 2147483647
x + 1 = -2147483648
intで扱える最大値を超えると、マイナスの値になります。
本Lispでも、Javaと同様の結果となります。
cl-user >> 2147483647
2147483647
java.lang.Integer
cl-user >> (+ 2147483647 1)
-2147483648
java.lang.Integer
cl-user >>
Javaのlongの動作
以下は、Javaのプログラムです。
public static void main(String[] args) {
long x = Long.MAX_VALUE;
System.out.println("x = " + x);
x = x + 1;
System.out.println("x + 1 = " + x);
}
実行結果は以下になります。
x = 9223372036854775807
x + 1 = -9223372036854775808
longで扱える最大値を超えると、マイナスの値になります。
本Lispでも、Javaと同様の結果となります。
cl-user >> 9223372036854775807
9223372036854775807
java.lang.Long
cl-user >> (+ 9223372036854775807 1)
-9223372036854775808
java.lang.Long
bignum(BigInteger)
本LispのREPLにlongの範囲外の数値を入れると、BigIntegerになります。
cl-user >> 9223372036854775808
9223372036854775808
java.math.BigInteger
Javaで、+ - * / が使えるのは、int, long, short, doubleのタイプです。
BigInteger型の足し算は addというメソッドを使う必要があります。
そのあたりも、本Lispでは、Javaの考え方に合わせています。
Common Lispにある、分数やbignumも、本Lispでは少しだけ実装しています。
しかしLispの + - * / では、分数やbignumは扱わない事にしました。