整数と浮動小数点数

整数と浮動小数点数

整数と浮動小数点の値は、算術と計算の基本的な構成要素です。 このような値の組み込み表現は数値プリミティブと呼ばれ、コード内の即時値としての整数と浮動小数点数の表現は数値リテラルと呼ばれます。 たとえば、1 は整数リテラルで、1.0 は浮動小数点リテラルです。オブジェクトとしてのバイナリインメモリ表現は数値プリミティブです。

Julia では、様々なプリミティブ数値型を提供され、そのそれぞれの型に対して、標準的な数学関数と同様に、算術演算子とビット演算子が定義されています。これら数値型や演算は、最新のコンピュータでネイティブにサポートされている数値型と演算に直接対応付けられているため、Julia は計算リソースを最大限に活用できます。さらに、Julia は任意精度演算をソフトウェア的にサポートしており、ネイティブなハードウェア表現では表現できない数値の演算を扱えますが、パフォーマンスは比較的遅くなってしまいます。

Juliaのプリミティブ数値型を次に示します:

符号の有無ビット数最小値最大値
Int88-2^72^7 - 1
UInt8802^8 - 1
Int1616-2^152^15 - 1
UInt161602^16 - 1
Int3232-2^312^31 - 1
UInt323202^32 - 1
Int6464-2^632^63 - 1
UInt646402^64 - 1
Int128128-2^1272^127 - 1
UInt12812802^128 - 1
BoolN/A8false (0)true (1)
精度ビット数
Float16half16
Float32single32
Float64double64

さらに、これらのプリミティブ数値型を用いて、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]

typemintypemaxによって返される値は、この関数に引数として与えられた型の数値になります。(上記の式では、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をスローします。 また、剰余関数 (remmod) は、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つあります:

Float16Float32Float64名前説明
Inf16Inf32Inf正の無限大すべての有限の浮動小数点数よりも大きい値
-Inf16-Inf32-Inf負の無限大すべての有限の浮動小数点よりも小さい値
NaN16NaN32NaN非数どんな浮動小数点数とも ==の成り立たない値 (自分自身とも)

これら非有限の浮動小数点値を互いにどのように順序付けされるかについての詳細は、数値の比較を参照してください。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^-232.0^-52はそれぞれ Float32Float64の値です。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つある場合には、最下位ビットが偶数になるように丸めます。

背景と参照

浮動小数点演算には、低レベルの実装の詳細に慣れていないユーザーが驚くような多くの微妙な点がたくさんあります。ただし、これらの微妙な点は、科学計算に関する多くの書籍や下記の資料で詳しく説明されています:

任意精度演算

任意精度の整数と浮動小数点数を使用した計算を可能にするために、Julia はGNU Multiple Precision Arithmetic Library(GMP)GNU MPFR Libraryをラップしています。BigIntBigFloatは、それぞれ任意の精度整数と浮動小数点数でJuliaで使用できます。

コンストラクターは、プリミティブな数値型からこれらの型を作成するために存在し、parseAbstractStringからそれらを構築するために使用することができます。一度こうして値を生成したら、他のすべての数値型との演算が可能です。これは 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

ただし、プリミティブ型と BigIntBigFloat との型昇格は自動には行われず、明示的に記述する必要があります。

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

ただし、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進整数リテラル と浮動小数点リテラルの指数表記です。構文上の競合が発生する状況を以下のようなものです:

どの場合についても、数値リテラルとしての解釈を優先して解決されます。つまり:

歴史的な理由から数値リテラルの e に相当する E とは異なり、F は単なる文字であり、数値リテラルでは f のように動作しません。したがって、数値リテラルで始まりFが続く式は、数値リテラルに変数を掛けたものとして解釈され、例えば1.5F22は`1.5

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