整数と浮動小数点数
整数と浮動小数点の値は、算術と計算の基本的な構成要素です。 このような値の組み込み表現は数値プリミティブと呼ばれ、コード内の即時値としての整数と浮動小数点数の表現は数値リテラルと呼ばれます。 たとえば、1
は整数リテラルで、1.0
は浮動小数点リテラルです。オブジェクトとしてのバイナリインメモリ表現は数値プリミティブです。
Julia では、様々なプリミティブ数値型を提供され、そのそれぞれの型に対して、標準的な数学関数と同様に、算術演算子とビット演算子が定義されています。これら数値型や演算は、最新のコンピュータでネイティブにサポートされている数値型と演算に直接対応付けられているため、Julia は計算リソースを最大限に活用できます。さらに、Julia は任意精度演算をソフトウェア的にサポートしており、ネイティブなハードウェア表現では表現できない数値の演算を扱えますが、パフォーマンスは比較的遅くなってしまいます。
Juliaのプリミティブ数値型を次に示します:
- 整数型:
型 | 符号の有無 | ビット数 | 最小値 | 最大値 |
---|---|---|---|---|
Int8 | ✓ | 8 | -2^7 | 2^7 - 1 |
UInt8 | 8 | 0 | 2^8 - 1 | |
Int16 | ✓ | 16 | -2^15 | 2^15 - 1 |
UInt16 | 16 | 0 | 2^16 - 1 | |
Int32 | ✓ | 32 | -2^31 | 2^31 - 1 |
UInt32 | 32 | 0 | 2^32 - 1 | |
Int64 | ✓ | 64 | -2^63 | 2^63 - 1 |
UInt64 | 64 | 0 | 2^64 - 1 | |
Int128 | ✓ | 128 | -2^127 | 2^127 - 1 |
UInt128 | 128 | 0 | 2^128 - 1 | |
Bool | N/A | 8 | false (0) | true (1) |
- 浮動小数点型:
型 | 精度 | ビット数 |
---|---|---|
Float16 | half | 16 |
Float32 | single | 32 |
Float64 | double | 64 |
さらに、これらのプリミティブ数値型を用いて、Juliaは 複素数と有理数 に完全対応しています。すべての数値型は明示的なキャストなしで自然に相互運用されます。これは柔軟でユーザーが拡張可能な 型昇格システムのおかげです。
整数
整数リテラルは、標準的な方法で表現できます:
julia> 1
1
julia> 1234
1234
整数リテラルのデフォルトの型は、ターゲット・システムが 32 ビット・アーキテクチャーと 64 ビット・アーキテクチャーのどちらであるかによって異なります:
# 32-bit system:
julia> typeof(1)
Int32
# 64-bit system:
julia> typeof(1)
Int64
Julia の内部変数 Sys.WORD_SIZE
を見ると、ターゲット・システムが 32 ビットか 64 ビットかがわかります:
# 32-bit system:
julia> Sys.WORD_SIZE
32
# 64-bit system:
julia> Sys.WORD_SIZE
64
Julia には、システムの符号付きネイティブ整数型と符号なしネイティブ整数型のエイリアスである Int
型と UInt
がそれぞれ定義されています:
# 32-bit system:
julia> Int
Int32
julia> UInt
UInt32
# 64-bit system:
julia> Int
Int64
julia> UInt
UInt64
大きな整数リテラルで、32 ビットのみを使用して表すことができず、64 ビットで表すことができるものに対しては、システムの種類に関係なく、常に 64 ビットの整数が生成されます:
# 32-bit or 64-bit system:
julia> typeof(3000000000)
Int64
符号なしの整数の入出力には、0x
プレフィックスを持つ16進数の数字 0-9a-f
(大文字の数字 A-F
も利用可能)を使います。符号なしの値のサイズは、使用する16進数の桁数で決まります:
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
こうした振る舞いは、符号なし整数の16進リテラルを使用する場合、通常は、単なる整数値というよりは、固定長バイト列として使われることが多いだろうという考察に基づいています。
変数 ans
には、対話型セッションで最後に評価された式の値が格納されていることを思い出してください。これは、Julia コードが対話セッション以外の他の方法で実行されている場合にはあてはまりません。
2進数・8進数リテラルにも対応しています:
julia> 0b10
0x02
julia> typeof(ans)
UInt8
julia> 0o010
0x08
julia> typeof(ans)
UInt8
julia> 0x00000000000000001111222233334444
0x00000000000000001111222233334444
julia> typeof(ans)
UInt128
16進・2進・8進リテラルに対しては、符号なし整数型が生成されます。リテラルの先頭桁を 0
で埋めない場合、バイナリ データのサイズは必要最小限のサイズになります。先頭にゼロが付加されている場合、その先頭の桁が1
である同じ桁数のリテラルに必要な最小サイズになります。これにより、ユーザーはデータのサイズを制御できます。 UInt128
に格納できない値は、このようなリテラルとして記述できません。
2進・8進・16進リテラルは、リテラルの直前に -
の符号をつけることができます。この場合生成されるのは、符号を付ける前の数とサイズの等しい符号なしの整数で、元の数の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 の整数型での演算は、実際には モジュラー演算の一形態です。 これは、現代のコンピュータに実装されている整数の基礎となる算術演算の特徴を反映しています。オーバーフローが起こり得るアプリケーションでは、オーバーフローによって生成されるラップアラウンドを明示的にチェックすることが不可欠です。そうでなければ、代わりに任意精度演算のBigInt
型を使用することをお勧めします。
除算エラー
整数除算 (div
関数) には 2つの例外的なケースがあります。一つは、0 除算、もうひとつは、最小の負の数 (typemin
) の -1 除算です。どちらの場合もDivideError
をスローします。 また、剰余関数 (rem
と mod
) は、2 番目の引数がゼロの場合に 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
値はFloat32
に簡単に変換できます:
julia> Float32(-1.5)
-1.5f0
julia> typeof(ans)
Float32
16進表記の浮動小数点リテラルも有効ですが、基数2の指数の前に p
をつけて利用し、値は Float64
になります:
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)
浮動小数点 の0
浮動小数点数には正と負2 つのゼロがあります。これらは互いに等しい値ですが、異なる2進数表現を持っています。bitstring
関数で確認することができます:
julia> 0.0 == -0.0
true
julia> bitstring(0.0)
"0000000000000000000000000000000000000000000000000000000000000000"
julia> bitstring(-0.0)
"1000000000000000000000000000000000000000000000000000000000000000"
特殊な浮動小数点の値
浮動小数点の値で実数の数直線上のどの点にも対応しない値が3つあります:
Float16 | Float32 | Float64 | 名前 | 説明 |
---|---|---|---|---|
Inf16 | Inf32 | Inf | 正の無限大 | すべての有限の浮動小数点数よりも大きい値 |
-Inf16 | -Inf32 | -Inf | 負の無限大 | すべての有限の浮動小数点よりも小さい値 |
NaN16 | NaN32 | 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)
マシンイプシロン
ほとんどの実数は浮動小数点数で正確に表現することができないため、多くの目的で、2つの隣接する浮動小数点数の間の距離を知ることは重要なことです。これは、マシンイプシロン として知られています。
Julia では eps
関数で、各数値型を引数にとり、その型においての 1.0
という値と、その値の次に大きな浮動小数点数の間の距離を得ることができます:
julia> eps(Float32)
1.1920929f-7
julia> eps(Float64)
2.220446049250313e-16
julia> eps() # same as eps(Float64)
2.220446049250313e-16
これらの値、2.0^-23
と2.0^-52
はそれぞれ Float32
とFloat64
の値です。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
隣接する 2 つの 表現可能な浮動小数点数の間の距離は一定ではなく、その距離は元の値が小さいほど小さくなり、大きいほど大きくなります。つまり、表現可能な浮動小数点数の数直線は0付近で最も密度が高く、0から遠ざかるにつれて指数関数的に疎になります。定義上、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"
この例では、隣接する表現可能な浮動小数点数は、2進数表現でもおなじく隣接する、という一般的な原則を示しています。
丸めモード
浮動小数点で正確に表現できない数は、適切な表現可能な値に丸める必要があります。丸め処理の方法は、IEEE 754 規格に複数のモードが示されており、必要に応じてそのモードを変更することができます。
デフォルトの丸めモードは、常に RoundNearest
(最近接丸め)で、これは、最も近い表現可能な値に丸める方法で、仮に最近接の表現可能な数が2つある場合には、最下位ビットが偶数になるように丸めます。
背景と参照
浮動小数点演算には、低レベルの実装の詳細に慣れていないユーザーが驚くような多くの微妙な点がたくさんあります。ただし、これらの微妙な点は、科学計算に関する多くの書籍や下記の資料で詳しく説明されています:
- 浮動小数点演算の最も信頼のおけるガイドは、IEEE 754-2008規格です。ただしオンライン・無料での利用はできません。
- 浮動小数点数の表現について完結だが明快な解説として、John D.Cook'の記事を参照のこと。イントロダクションでは、浮動小数点数と理想化された抽象的な実数との乖離から生じる問題について書かれています。
- また、Bruce Dawsonの浮動小数点数に関する一連のブログ記事もお勧めします。
- 浮動小数点数と及び、浮動小数点数を用いた演算で遭遇する数値精度の問題についての詳細な議論については、David Goldbergの論文What Every Computer Scientists Should Know Aboutを参照してください。
- 浮動小数点数の歴史、理論、問題、および数値計算における他の多くのトピックの議論について、さらに広く扱った文書については、「浮動小数点の父」として知られているWilliam Kahanの 著作集を参照してください。特に興味深いのは「浮動小数点の老人のインタビュー」(https://people.eecs.berkeley.edu/~wkahan/ieee754status/754story.html)です。
任意精度演算
任意精度の整数と浮動小数点数を使用した計算を可能にするために、Julia はGNU Multiple Precision Arithmetic Library(GMP)とGNU MPFR Libraryをラップしています。BigInt
とBigFloat
は、それぞれ任意の精度整数と浮動小数点数でJuliaで使用できます。
コンストラクターは、プリミティブな数値型からこれらの型を作成するために存在し、parse
はAbstractString
からそれらを構築するために使用することができます。一度こうして値を生成したら、他のすべての数値型との演算が可能です。これは Juliaの型昇格と変換メカニズムによって実現されています:
julia> BigInt(typemax(Int64)) + 1
9223372036854775808
julia> parse(BigInt, "123456789012345678901234567890") + 1
123456789012345678901234567891
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
ただし、2 つの括弧式の並べておいたり、括弧式の前に変数を配置しても、乗算とはみなされません:
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
括弧式を並べる場合も、括弧式の直前に変数を置いた時も、関数の適応として解釈されます: 数値リテラルではない式の直後に括弧を付けると、括弧内の値に適用される関数として解釈されます (関数についての詳細は、関数の項を参照してください)。したがって、いずれの場合も、左側の値が関数ではないため、エラーが発生します。
このような構文機能の強化により、一般的な数式を記述するときに発生する視覚的なノイズが大幅に低減されます。数値リテラル係数と、乗算する識別子または括弧式の間に空白が生じないことに注意してください。
構文の競合
リテラル係数を2つ並べる構文と競合するかもしれない2つの数値リテラル構文があります:16進整数リテラル と浮動小数点リテラルの指数表記です。構文上の競合が発生する状況を以下のようなものです:
- 16進整数値
0xff
は、数値リテラル0
と変数xff
の掛け算と解釈できる。 - 浮動小数点リテラル
1e10
は、数値リテラル1
と 変数e10
の掛け算と解釈できる。E
形式も同様。 - 32 ビット浮動小数点リテラル
1.5f22
は、数値リテラル1.5
と 変数f22
の掛け算と解釈できる。
どの場合についても、数値リテラルとしての解釈を優先して解決されます。つまり:
0x
で始まる式は、常に16進数リテラルです。- 数値リテラルで始まり、
e
またはE
が続く式は、常に浮動小数点リテラルです。 - 数値リテラルで始まり、
f
が続く式は、常に 32 ビット浮動小数点リテラルです。
歴史的な理由から数値リテラルの e
に相当する E
とは異なり、F
は単なる文字であり、数値リテラルでは f
のように動作しません。したがって、数値リテラルで始まりF
が続く式は、数値リテラルに変数を掛けたものとして解釈され、例えば1.5F22
は`1.5
- F22`に等しいことを意味します。
0と1のリテラル
Julia は、指定された型または特定の変数の型に対応するリテラル 0 と 1 を返す関数を提供します。
関数 | 説明 |
---|---|
zero(x) | 型 x もしくは 変数 x と同じ型を持つリテラル0 |
one(x) | 型 x もしくは 変数 x と同じ型を持つリテラル1 |
これらの関数は、数値比較の際に、不要な 型変換 のオーバーヘッドを回避するのに役立ちます。
例:
julia> zero(Float32)
0.0f0
julia> zero(1.0)
0.0
julia> one(Int32)
1
julia> one(BigFloat)
1.0