整数と浮動小数点数
整数と浮動小数点数は算術と計算における基礎単位です。こういった値の組み込みの表現は数値プリミティブ (numeric primitive) と呼ばれ、コードにおける整数と浮動小数点数の即値としての表現は数値リテラル (numeric literal) と呼ばれます。例えば 1
は整数リテラルであり、1.0
は浮動小数点数リテラルです。そしてこれらの値をオブジェクトとしてメモリ上にバイナリで表現したものが数値プリミティブです。
Julia は様々なプリミティブ数値型 (primitive numeric type) を提供し、算術演算・ビット演算・標準的な数学関数は全てのプリミティブ数値型に対して定義されます。プリミティブ数値型は現代的なコンピューターがネイティブにサポートする数値型および数値演算に直接対応するので、Julia は計算資源を最大限に活用できます。加えて Julia は任意精度算術をソフトウェアでサポートします。任意精度算術とはハードウェアのネイティブ表現では効率的に表現できない数値に対する演算を処理する方式であり、精度の代わりに性能が犠牲になります。
Julia のプリミティブ数値型を次に示します:
- 整数型:
- 浮動小数点数型:
複素数と有理数の完全なサポートはこれらのプリミティブ数値型の上に成り立っています。なお複数の数値型をコード中に混在させたとしても、明示的なキャストなしに自然に動作します。これは Julia が持つ柔軟な型昇格システムのおかげです (このシステムはユーザーから拡張することもできます)。
整数
整数リテラルの表記に変わったところはありません:
julia> 1
1
julia> 1234
1234
整数リテラルのデフォルトの型は、ターゲットのシステムが 32 ビットアーキテクチャを持っているか 64 ビットアーキテクチャを持っているかによって変化します:
# 32 ビットシステム:
julia> typeof(1)
Int32
# 64 ビットシステム:
julia> typeof(1)
Int64
ターゲットシステムが 32 ビットか 64 ビットかは、Julia の内部変数 Sys.WORD_SIZE
で確認できます:
# 32 ビットシステム:
julia> Sys.WORD_SIZE
32
# 64 ビットシステム:
julia> Sys.WORD_SIZE
64
Julia は Int
と UInt
という型も定義します。この二つの型はそれぞれシステムの符号付き整数型と符号無し整数型の別名です:
# 32 ビットシステム:
julia> Int
Int32
julia> UInt
UInt32
# 64 ビットシステム:
julia> Int
Int64
julia> UInt
UInt64
32 ビットで表現できず 64 ビットで表現できる大きな整数は、システムの型に関わらず 64 ビット整数となります:
# 32 ビットシステムおよび 64 ビットシステム:
julia> typeof(3000000000)
Int64
符号無し整数は 0x
が最初に付いた十六進記法で入出力されます。十六進記法の桁には [0-9a-f]
が利用でき、入力のときは大文字の [A-F]
も利用できます。符号無し整数のサイズは十六進表記の桁数によって決まります:
julia> 0x1
0x01
julia> typeof(ans)
UInt8
julia> 0x123
0x0123
julia> typeof(ans)
UInt16
julia> 0x1234567
0x01234567
julia> typeof(ans)
UInt32
julia> 0x123456789abcdef
0x0123456789abcdef
julia> typeof(ans)
UInt64
julia> 0x11112222333344445555666677778888
0x11112222333344445555666677778888
julia> typeof(ans)
UInt128
このような動作になっている理由は、符号無し十六進リテラルで整数値を表すときは (ただの整数値ではなく) 幅が想定されたバイトシーケンスを表そうとしていることが多いためです。
変数 ans
は対話セッションで最後に評価された式の値となることに注意してください。対話セッションでないとき ans
変数は利用できません。
二進リテラルと八進リテラルもサポートされます:
julia> 0b10
0x02
julia> typeof(ans)
UInt8
julia> 0o010
0x08
julia> typeof(ans)
UInt8
julia> 0x00000000000000001111222233334444
0x00000000000000001111222233334444
julia> typeof(ans)
UInt128
十六進リテラルと同様に、二進リテラルと八進リテラルは符号無し整数型の値を生成します。二進リテラルの先頭の桁が 1
なら、生成される値のサイズはリテラルが表す整数値を格納できる最小のサイズとなります。また先頭の桁が 0
である二進リテラルのサイズは、そのリテラルと同じ幅を持つ先頭の桁が 1
であるリテラルと同じサイズとなります。この仕様を使えばリテラルのサイズを制御できます。なお UInt128
に格納できない値はリテラルとして書くことができません。
二進・八進・十六進の符号無しリテラルには先頭に -
を付けることができます。こうすると -
以降の符号無しリテラルと同じサイズを持った符号無し整数となり、その値は -
以降の符号無しリテラルの 2 の補数となります:
julia> -0x2
0xfe
julia> -0x0002
0xfffe
プリミティブ数値型が表現できる最小値と最大値は typemin
関数と typemax
関数で取得できます:
julia> (typemin(Int32), typemax(Int32))
(-2147483648, 2147483647)
julia> for T in [Int8,Int16,Int32,Int64,Int128,UInt8,UInt16,UInt32,UInt64,UInt128]
println("$(lpad(T,7)): [$(typemin(T)),$(typemax(T))]")
end
Int8: [-128,127]
Int16: [-32768,32767]
Int32: [-2147483648,2147483647]
Int64: [-9223372036854775808,9223372036854775807]
Int128: [-170141183460469231731687303715884105728,170141183460469231731687303715884105727]
UInt8: [0,255]
UInt16: [0,65535]
UInt32: [0,4294967295]
UInt64: [0,18446744073709551615]
UInt128: [0,340282366920938463463374607431768211455]
typemin
と typemax
が返す値は必ず与えられた型を持ちます。(上の例では for ループ・文字列・文字列補間といったまだ説明していない機能がいくつか使われていますが、プログラミングを少ししたことがあれば簡単に理解できるはずです。)
オーバーフローの挙動
Julia では整数型の値がその型で表現可能な最大の値よりも大きくなると、ラップアラウンドが起こります:
julia> x = typemax(Int64)
9223372036854775807
julia> x + 1
-9223372036854775808
julia> x + 1 == typemin(Int64)
true
このため Julia における整数算術は実際には合同算術の一種です。ここには Julia を実行する現代的なコンピューターで実装されている整数算術の特徴が現れています。オーバーフローの可能性のあるアプリケーションでは、オーバーフローによるラップアラウンドの明示的なチェックが不可欠です。このチェックを行わないのであれば、BigInt
型を使った任意精度算術が推奨されます。
オーバーフローとその回避を次の例に示します:
julia> 10^19
-8446744073709551616
julia> big(10)^19
10000000000000000000
除算エラー
整数の除算 (div
関数) には二つの例外的なケースがあります: 負の最小値 (typemin
) を -1 で割る処理と、ゼロ割りです。この二つはどちらも DivideError
を送出します。剰余演算とモジュロ演算 (rem
と mod
) で第二引数を 0 としたときにも DivideError
が送出されます。
浮動小数点数
浮動小数点数リテラルは標準的な形式で表されます。必要に応じて E 記法も利用できます:
julia> 1.0
1.0
julia> 1.
1.0
julia> 0.5
0.5
julia> .5
0.5
julia> -1.23
-1.23
julia> 1e10
1.0e10
julia> 2.5e-4
0.00025
上記の結果は全て Float64
型の値となります。Float32
型のリテラルを入力するには、e
の部分を f
とします:
julia> 0.5f0
0.5f0
julia> typeof(ans)
Float32
julia> 2.5f-4
0.00025f0
Float64
から Float32
への変換は簡単に行えます:
julia> Float32(-1.5)
-1.5f0
julia> typeof(ans)
Float32
十六進の浮動小数点数リテラルも利用できますが、必ず Float64
型となります。p
の後に基数 2 の指数を付けられます:
julia> 0x1p0
1.0
julia> 0x1.8p3
12.0
julia> 0x.4p-1
0.125
julia> typeof(ans)
Float64
半精度の浮動小数点数 (Float16
) もサポートされます。ただし実装はソフトウェアであり、計算は Float32
を使って行われます。
julia> sizeof(Float16(4.))
2
julia> 2*Float16(4.)
Float16(8.0)
アンダースコア _
を使って桁を区切ることができます:
julia> 10_000, 0.000_000_005, 0xdead_beef, 0b1011_0010
(10000, 5.0e-9, 0xdeadbeef, 0xb2)
浮動小数点数のゼロ
浮動小数点数には二つのゼロがあります: 正のゼロと負のゼロです。二つのゼロは等しいとみなされますが、異なるバイナリ表現を持ちます。bitstring
関数を使えばこの事実を確認できます:
julia> 0.0 == -0.0
true
julia> bitstring(0.0)
"0000000000000000000000000000000000000000000000000000000000000000"
julia> bitstring(-0.0)
"1000000000000000000000000000000000000000000000000000000000000000"
特殊な浮動小数点数の値
浮動小数点数の標準規格 IEEE 754 には、実数直線上のどの点にも対応しない値が三つ規定されています:
Float16 |
Float32 |
Float64 |
名前 | 説明 |
---|---|---|---|---|
Inf16 |
Inf32 |
Inf |
正の無限大 | 任意の有限の浮動小数点数より大きい値 |
-Inf16 |
-Inf32 |
-Inf |
負の無限大 | 任意の有限の浮動小数点数より小さい値 |
NaN16 |
NaN32 |
NaN |
not a number (非数) |
任意の浮動小数点数と == にならない値(NaN 自身とも == にならない) |
こういった有限でない浮動小数点数と他の浮動小数点数の振る舞いについて詳しくは数値の比較の節を参照してください。IEEE 754 規格により、有限でない浮動小数点数が関わる演算の結果は次のように規定されています:
julia> 1/Inf
0.0
julia> 1/0
Inf
julia> -5/0
-Inf
julia> 0.000001/0
Inf
julia> 0/0
NaN
julia> 500 + Inf
Inf
julia> 500 - Inf
-Inf
julia> Inf + Inf
Inf
julia> Inf - Inf
NaN
julia> Inf * Inf
Inf
julia> Inf / Inf
NaN
julia> 0 * Inf
NaN
typemin
関数と typemax
関数は浮動小数点数型にも適用できます:
julia> (typemin(Float16),typemax(Float16))
(-Inf16, Inf16)
julia> (typemin(Float32),typemax(Float32))
(-Inf32, Inf32)
julia> (typemin(Float64),typemax(Float64))
(-Inf, Inf)
計算機イプシロン
実数の多くは浮動小数点数として正確に表せないので、二つの隣接する浮動小数点数の差を知っておくことが様々な用途において重要になります。この差のことを計算機イプシロン (machine epsilon) と呼びます。
1.0
と 1.0
の次に大きい浮動小数点数の差を返す関数として、Julia は eps
を提供します:
julia> eps(Float32)
1.1920929f-7
julia> eps(Float64)
2.220446049250313e-16
julia> eps() # eps(Float64) と同じ
2.220446049250313e-16
eps
の値は Float32
では 2.0^-23
で、Float64
では 2.0^-52
です。eps
には浮動小数点数の値を渡すこともでき、このときは渡した値とそれより大きい最小の浮動小数点数の差が返ります。言い換えると、eps(x)
は x
と同じ型であって、x + eps(x)
が x
より大きい次の浮動小数点数となるような値です:
julia> eps(1.0)
2.220446049250313e-16
julia> eps(1000.)
1.1368683772161603e-13
julia> eps(1e-27)
1.793662034335766e-43
julia> eps(0.0)
5.0e-324
隣り合う二つの浮動小数点数の差は定数ではありませんが、小さい値では小さく、大きい値では大きくなります。言い換えると、表現可能な浮動小数点数は実数直線上のゼロに近い部分では密であり、ゼロから離れていくと指数的に疎になっていきます。1.0
は 64 ビットの浮動小数点数なので、定義により、eps(1.0)
と eps(Float64)
は同じ値です。
Julia は nextfloat
関数と prevfloat
関数も提供します。この二つの関数はそれぞれ引数の次に大きい浮動小数点数と次に小さい浮動小数点数を返します:
julia> x = 1.25f0
1.25f0
julia> nextfloat(x)
1.2500001f0
julia> prevfloat(x)
1.2499999f0
julia> bitstring(prevfloat(x))
"00111111100111111111111111111111"
julia> bitstring(x)
"00111111101000000000000000000000"
julia> bitstring(nextfloat(x))
"00111111101000000000000000000001"
この例では、隣り合う浮動小数点数は隣り合うバイナリ表現を持つという一般的な規則が示されています。
丸めモード
数値が浮動小数点数による正確な表現を持たない場合、その数値を表現できる値へ丸めなければなりません。このときに使われる丸めの方法は必要に応じて IEEE 754 規格に規定される丸めモード (rounding mode) のいずれかに切り替えることができます。
デフォルトのモードは必ず RoundNearest
です。これは表現可能な一番近い値に丸めることを基本として、一番近い値が同着で二つある場合には最下位ビットが偶数である値に丸めるモードです。
背景知識と参考文献
浮動小数点数算術には細かな注意点が多くあり、低レベルの実装詳細をよく知らないと驚くような挙動もあります。しかし、こういった注意点はたいていの科学計算に関する書籍、およびここに示す参考文献で詳しく説明されています:
- 浮動小数点数の決定的なガイドは IEEE 754-2008 規格です。ただし、オンラインでこの規格を無料で閲覧することはできません。
- 浮動小数点数の表現を短く明快に説明している記事として、John D. Cook による Anatomy of a floating point number があります。彼は他にも理想的な実数と浮動小数点数による表現の違いから生じる問題を Floating point numbers are a leaky abstraction で紹介しています。
- Bruce Dawson による 浮動小数点数に関するブログシリーズ もお勧めできます。
- 浮動小数点数の計算で生じる精度の問題を詳しく論じた素晴らしい論文として、David Goldberg による What Every Computer Scientist Should Know About Floating-Point Arithmetic があります。
- 浮動小数点数の歴史・理論的根拠・問題に関するさらに幅広いドキュメント、および数値計算における他の様々なトピックに関する議論については、「浮動小数点数の父」として知られる William Kahan による collected writings を参照してください。
任意精度算術
任意精度の整数と浮動小数点数を使った計算のために、Julia は GNU Multiple Precision Arithmetic Library (GMP) と GNU MPFR Library をラップしています。Julia の BigInt
型と BigFloat
型がそれぞれ任意精度整数と任意精度浮動小数点数に利用できます。
プリミティブ数値型からこれら二つの型を作成するためのコンストラクタが存在し、parse
や @big_str
(文字列リテラル) を使えば AbstractString
からも作成できます。Julia には自動的な型の変換と昇格があるので、任意精度の型の値を一度作成すれば、以降は他の数値型と混ぜて算術を行って構いません:
julia> BigInt(typemax(Int64)) + 1
9223372036854775808
julia> big"123456789012345678901234567890" + 1
123456789012345678901234567891
julia> parse(BigInt, "123456789012345678901234567890") + 1
123456789012345678901234567891
julia> big"1.23456789012345678901"
1.234567890123456789010000000000000000000000000000000000000000000000000000000004
julia> parse(BigFloat, "1.23456789012345678901")
1.234567890123456789010000000000000000000000000000000000000000000000000000000004
julia> BigFloat(2.0^66) / 3
2.459565876494606882133333333333333333333333333333333333333333333333333333333344e+19
julia> factorial(BigInt(40))
815915283247897734345611269596115894272000000000
ただし、プリミティブ数値型から BigInt
/BigFloat
への型昇格は自動的に行われないので、明示的に示す必要があります:
julia> x = typemin(Int64)
-9223372036854775808
julia> x = x - 1
9223372036854775807
julia> typeof(x)
Int64
julia> y = BigInt(typemin(Int64))
-9223372036854775808
julia> y = y - 1
-9223372036854775809
julia> typeof(y)
BigInt
BigFloat
の演算におけるデフォルトの精度 (仮数のビット数) と丸めモードは setprecision
と setrounding
でグローバルに設定できます。設定すれば、以降の全ての計算が影響を受けます。また do
ブロック内でこれらの関数を使えば、精度と丸めモードをブロックの内部でのみ変更できます:
julia> setrounding(BigFloat, RoundUp) do
BigFloat(1) + parse(BigFloat, "0.1")
end
1.100000000000000000000000000000000000000000000000000000000000000000000000000003
julia> setrounding(BigFloat, RoundDown) do
BigFloat(1) + parse(BigFloat, "0.1")
end
1.099999999999999999999999999999999999999999999999999999999999999999999999999986
julia> setprecision(40) do
BigFloat(1) + parse(BigFloat, "0.1")
end
1.1000000000004
数値リテラル係数
通常の計算式や数式を簡単に書くために、Julia では数値リテラルに変数を続けて書くことで乗算を表せるようになっています。これにより多項式がずっと簡単に書けます:
julia> x = 3
3
julia> 2x^2 - 3x + 1
10
julia> 1.5x^2 - .5x + 1
13.0
指数関数もエレガントに書けます:
julia> 2^2x
64
数値リテラル係数の優先順位は単項否定のような単項演算子より少し低いので、-2x
は (-2) * x
とパースされ、√2x
は (√2) * x
とパースされます。ただし指数と組み合わさった数値リテラル係数は単項演算子と同様にパースされます。例えば 2^3x
は 2^(3x)
となり、2x^3
は 2*(x^3)
となります。
数値リテラルは括弧で囲まれた式に対する係数にもなります:
julia> 2(x-1)^2 - 3(x-1) + 1
3
暗黙の乗算に使われる数値リテラル係数の優先順位は乗算 (*
) や除算 (/
, \
, //
) といった二項演算子より高くなっています。そのため、例えば 1 / 2im
は -0.5im
となり、6 // 2(2 + 1)
は 1 // 1
となります。
さらに、括弧で囲まれた式も変数に対する係数として使うことができます。括弧で囲まれた式と変数の乗算を表します:
julia> (x-1)x
6
これに対して、括弧で囲まれた式を二つ続けて書いても乗算にはなりません。また括弧で囲まれた式の前に変数を書いても乗算とはなりません:
julia> (x-1)(x+1)
ERROR: MethodError: objects of type Int64 are not callable
julia> x(x+1)
ERROR: MethodError: objects of type Int64 are not callable
この二つの式は関数適用と解釈されます。数値リテラルでない任意の式に続く括弧が続くと、それは囲まれた式の値を使った関数適用と解釈されます。そのため上の二つの例では左辺の値が関数でないというエラーが発生します (関数について詳しくは関数の章を参照してください)。
この構文の拡張により、普通の数式をコードとして書くときに生じる視覚的なノイズが大幅に減少します。数値リテラル係数と乗じられる識別子 (あるいは括弧に囲まれた式) の間に空白を入れてはいけないことに注意してください。
構文の衝突
数値リテラル係数を変数の隣に書く構文は、次の二つの数値リテラル構文と衝突する可能性があります: 十六進整数リテラルと、浮動小数点数リテラルの工学記法 (E 記法) です。構文の衝突が起こる状況の例を示します:
- 十六進整数リテラル式
0xff
は数値リテラル0
と変数xff
の乗算と解釈できる。 - 浮動小数点数リテラル式
1e10
は数値リテラル1
と変数e10
の乗算と解釈できる。E
を使っても同様となる。 - 32 ビットの浮動小数点数リテラル式
1.5f22
は数値リテラル1.5
と変数f22
の乗算と解釈できる。
全ての場合において、曖昧さは数値リテラルを優先する形で解決されます:
0x
で始まる式は必ず十六進リテラルである。- 数値リテラルで始まり、その直後に
e
またはE
が続く式は必ず浮動小数点数リテラルである。 - 数値リテラルで始まり、その直後に
f
が続く式は必ず 32 ビットの浮動小数点数リテラルである。
歴史的な理由により数値リテラル中の e
の代わりに E
を使うことができますが、数値リテラル中の f
の代わりに F
を使うことはできません。そのため、数値リテラルで始まりその直後に F
が続く式は数値リテラルと変数の乗算として解釈されます。例えば 1.5F22
は 1.5 * F22
と等価です。
リテラルの 0 と 1
Julia は指定された型 (もしくは指定された値の型) を持つリテラルの 0 または 1 を返す関数を提供します:
こういった関数は数値の比較で型の変換によるオーバーヘッドを避けるために利用できます。
zero
と one
の使用例を示します:
julia> zero(Float32)
0.0f0
julia> zero(1.0)
0.0
julia> one(Int32)
1
julia> one(BigFloat)
1.0