整数と浮動小数点数

整数と浮動小数点数は算術と計算における基礎単位です。こういった値の組み込みの表現は数値プリミティブ (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 は IntUInt という型も定義します。この二つの型はそれぞれシステムの符号付き整数型と符号無し整数型の別名です:

# 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]

typemintypemax が返す値は必ず与えられた型を持ちます。(上の例では 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 を送出します。剰余演算とモジュロ演算 (remmod) で第二引数を 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.01.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 です。これは表現可能な一番近い値に丸めることを基本として、一番近い値が同着で二つある場合には最下位ビットが偶数である値に丸めるモードです。

背景知識と参考文献

浮動小数点数算術には細かな注意点が多くあり、低レベルの実装詳細をよく知らないと驚くような挙動もあります。しかし、こういった注意点はたいていの科学計算に関する書籍、およびここに示す参考文献で詳しく説明されています:

任意精度算術

任意精度の整数と浮動小数点数を使った計算のために、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 の演算におけるデフォルトの精度 (仮数のビット数) と丸めモードは setprecisionsetrounding でグローバルに設定できます。設定すれば、以降の全ての計算が影響を受けます。また 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^3x2^(3x) となり、2x^32*(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 記法) です。構文の衝突が起こる状況の例を示します:

全ての場合において、曖昧さは数値リテラルを優先する形で解決されます:

歴史的な理由により数値リテラル中の e の代わりに E を使うことができますが、数値リテラル中の f の代わりに F を使うことはできません。そのため、数値リテラルで始まりその直後に F が続く式は数値リテラルと変数の乗算として解釈されます。例えば 1.5F221.5 * F22 と等価です。

リテラルの 0 と 1

Julia は指定された型 (もしくは指定された値の型) を持つリテラルの 0 または 1 を返す関数を提供します:

関数 説明
zero(x) x (または変数 x の型) のリテラル 0
one(x) x (または変数 x の型) のリテラル 1

こういった関数は数値の比較型の変換によるオーバーヘッドを避けるために利用できます。

zeroone の使用例を示します:

julia> zero(Float32)
0.0f0

julia> zero(1.0)
0.0

julia> one(Int32)
1

julia> one(BigFloat)
1.0
広告