未執筆部分があります
OCaml 数値型で標準で使えるものは、
int
, float
, int32
, int64
, native_int
, nat
, big_int
, Complex.t
の 8つ:
int
標準の整数。 32bit 環境では 31bit, 64bit 環境では 63bit 符号付き整数。
float
倍精度(64bit)浮動小数点数。 IEEE 754 で言うところの double であって、float ではない。
nativeint
OS でサポートする基本整数幅を持つ。
32bit 環境では 32bit, 64bit 環境では 64bit 符号付き整数。
int32
32bit 符号付き整数。
int64
64bit 符号付き整数。
nat
num ライブラリで定義されている無限制精度自然数。
big_int
num ライブラリで定義されている無限制精度符号付数。
Complex.t
複素数。 単に float 二つ組で表現されている。
OCaml で「整数」と言うと int
を指す。
32bit 環境では 31bit, 64bit 環境では 63bit の符号付き整数。
「失われた 1bit」は GC が int とポインタを識別するために使われる。
この 1bit のおかげで int
は box化されず、そのまま扱うことができるので
「1bit」が失われていないが、box化が必要な nativeint
と比べると高速に動作する。
通常の10進表記の他に、2進、8進、16進での表記が可能:
# 10;;
- : int = 10
# 0b10;;
- : int = 2
# 0o10;;
- : int = 8
# 0x10;;
- : int = 16
Pervasives に基本的は演算子 (+)
, (-)
, ( * )
, (/)
などが
記載されているが、ほとんどがプリミティブ
(external
宣言による C関数呼び出し)である。
int
掛け算の二項演算子 *
を前置関数として使う場合、
(*)
とスペース無しで書くとコメント様の文面と受け取られエラーになる。
必ず括弧と *
の間に空白を入れること: ( * )
:
# ( * ) 2 3;;
- : int = 6
int
は環境によってサイズが異なることに注意してほしい。その環境での int
の
上限と下限は Pervasives
モジュール内の変数 max_int
, min_int
に格納されている。
31bitの範囲を超える64bitアーキテクチャでの int
を output_value
などでシリアライズし、 32bit アーキテクチャの OCaml プログラムで読み込むとエラーが発生する。
max_int
, min_int
を超える演算結果はオーバフローおよびアンダーフローを起こすが、
起こした事実は特にレポートされない。オバーフロー、アンダフローを起こしたか知りたい場合は
自分で何かしら検査コードを書く必要がある:
# max_int + 1 = min_int;;
- : bool = true (* オーバーフローが起こっている *)
倍精度(64bit)浮動小数点数。 IEEE754 や C で言うところの double である。 単精度(32bit)浮動小数点数(いわゆる IEEE754 や C で言うところの float) は OCaml には標準では用意されていない。
OCaml では定数(どころか他の全てもそうだが)にはオーバーローディングがない。
そのため、 float
はもし小数点以下が 0 だったとしても、 int
との
混乱を避けるため、小数点を付けなければならない:
# 1.23;;
- : float = 1.23
# 1.0;;
- : float = 1.
# 42.;;
- : float = 42.
最後の 42.
のように少数点以下を書かない記法は他の言語ではエラーになる場合があるので注意が必要だ。
int
と同様、Pervasives に基本的は演算子
(+.)
, (-.)
, ( *.)
, (/.)
などが宣言されている。
演算子の最後に .
が付いていることに注意。 OCaml ではオーバーローディングが無いため、
int
の演算子 (+)
, (-)
, ( * )
, (/)
と同じ名前を
float
用の演算子名として用いることができない。そのための苦肉の策である。
後述の int
以外の演算子でも普通の四則演算子を使うも参照のこと。
(CR jfuruse: 未執筆)
float
の演算は IEEE754 に準拠するため、無限値や nan
などが存在する。
浮動小数点数がこれらの特殊な常態かどうかを調べるためには
Pervasives.classify_float
を使う。
float
の内容を文字列に変換するには string_of_float
を使うが、
この関数は小数点以下12桁までしか文字列に変換しない:
(* pervasives.ml *)
let string_of_float f = valid_float_lexem (format_float "%.12g" f);;
OCaml では float
を完全に正確に文字列表現に変換することは難しいが、
桁数を上げることで誤差を少なくすることは可能である。例えば次のコードでは
20桁まで出力を行なっている:
let default_string_of_float = ref (fun n -> sprintf "%.20G" n)
正確に float
を外部に出力・記録したい場合は、その 64bit 表現をそのまま
とり出さなければならない。 Pervasives.output_value
や Printf.printf
の %h
による16進内部表現などを使うことができる:
let () =
let f n =
let s = Printf.sprintf "%h" n in
Printf.printf "%f = %h\n" n (float_of_string s)
in
f 1.234;
f nan;
f infinity
int
は OS がサポートする基本整数型の幅から 1bit 少ない範囲の整数しか
取ることが出きなかった。OS のシステムコールなどはフルに整数幅を使うことが多いため、
OCaml でシステムプログラミングを行う際には int
では不便なことが多い。
(関連 OS 機能が整数の範囲としてフルサイズを使わないと確信できるときは int
を
使ってももちろん構わない。)
nativeint
は OS の基本整数型と同じ幅を持つ符号付き整数型であり、
int
とは違い「失われた1bit」は無い。そのためシステムプログラミングに向いた整数型
といえる。一方、 int
が 1bit を犠牲とすることで unbox化された表現を持ち
高速な演算が可能であるのに対し、 nativeint
はフルサイズで box化されたデータ表現となり、
GC の対象となるため、 nativeint
の計算は int
と比べるとメモリ領域を多く使用するし、遅くなってしまう。
(CR jfuruse: どれぐらいおそいか)
nativeint
の定数は整数の後に n
をつける。すなはち:
# 1n;;
- : nativeint = 1n
# 0x1234n;;
- : nativeint = 4660n
(* 0oxxxn (8進), 0bxxxn (2進) も可能 *)
Printf
系フォーマット文字列では %nd %nx などやはり n
を使う:
# Printf.sprintf "%nd" 42n;;
- : string = "42"
# Printf.sprintf "%06nx" 123n;;
- : string = "00007b"
nativeint
の関数は Nativeint モジュールに定義されているが、
四則演算が add
, sub
, mul
, div
と二項演算子ではなく
普通の関数となっているなど、このままでは、不便である。
後述の int
以外の演算子でも普通の四則演算子を使う も参照のこと。
(CR jfuruse: 未執筆)
int32
と int64
は nativeint
と同じく「失われた1bit」の
無い、 box化された符号付き整数型だが、幅はアーキテクチャに関係なく
int32
は 32bit、 int64
は 64bit固定である。
int32
は整数の後に l
, int64
は後に oL
と書く。
# 1l;;
- : int32 = 1l
# 0x1234L;;
- : int64 = 4660L
(* 0oxxxl (8進), 0bxxxL (2進) も可能 *)
Printf
系フォーマット文字列では %ld %Lx などやはり l
や L
を使う:
# Printf.sprintf "%ld" 42l;;
- : string = "42"
# Printf.sprintf "%06Lx" 123L;;
- : string = "00007b"
int32
と int64
のための関数はそれぞれ Int32 と Int64 モジュールに
定義されている。四則演算が add
, sub
, mul
, div
と二項演算子ではなく
普通の関数となっているなど、このままでは、不便である。
後述の int
以外の演算子でも普通の四則演算子を使う も参照のこと。
(CR jfuruse: 未執筆)
(CR jfuruse: 未執筆)
OCaml の標準ライブラリには何故か複素数のためのモジュール Complex
があり、
Complex.t
として複素数の型が定義されている。実装は非常に素直:
type t = { re: float; im: float }
実数(re
)と虚数(im
)部分を float
で表した二つ組。
この t
に対して幾つかの基本的な
演算が用意されている。が、文字列への変換も文字列からの変換も存在しない。
何のためにあるのかよくわからないモジュールである。
OCaml の標準ライブラリには、中の人が自分が便利だからと
勢いだけで足してしまったこういうモジュールがいくつか有る。
(CR jfuruse: 未執筆)
OCaml 標準で使用することのできる整数は全て符号付き(signed)である。符号無し(unsigned)整数を使いたい場合は外部ライブラリである ocaml-stdint
を使うことができる。ocaml-stdint
は符号無し整数だけでなく符号付き整数でも非標準のサイズ(8,24,40,48,56,128 bits)をサポートしている。