整数と浮動小数点数

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

Juliaは広範囲のプリミティブな数値型を提供し,それらの上で標準的な数学関数,および 算術演算子やビット演算子の完全な補完が定義されています.これらは最新のコンピュータで ネイティブにサポートされている数値型や演算に直接マッピングされているため,Juliaは計算 リソースを最大限に活用することができます.さらに,JuliaはArbitrary Precision Arithmetic をソフトウェアでサポートしており,ネイティブのハードウェア表現では効果的に表現できない 数値の演算を扱うことができますが,その引き換えに比較的動作は遅くなってしまいます.

以下がJuliaのプリミティブな数値型です:

  • 整数型:
TypeSigned?Number of bitsSmallest valueLargest value
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)
  • 浮動小数点数型:
TypePrecisionNumber of bits
Float16half16
Float32single32
Float64double64

さらに,これらのプリミティブな数値型の上に,Complex and Rational Numbersの完全なサポートが構築 されています.全ての数値型は,柔軟でユーザが拡張可能なtype promotion system のおかげで,明示的なキャストを行わなくても自然に相互運用することができます.

整数型

リテラルな整数は標準的な方法で表現されます:

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ではIntUIntという型も定義されますが,これはそれぞれ,システムの符号付整数型と 符号なし整数型のエイリアスとなっています:

# 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進リテラルは符号なし整数型を生成します.2進数データ アイテムのサイズは,リテラルの先頭の桁が0でない場合,必要最小限のサイズです.先頭に0が並ぶ 場合は,サイズは,同じ長さだが先頭の桁に1が並んでいるようなリテラルの必要最小限のサイズ によって決定されます.これにより,ユーザはサイズを制御することができます.UInt128に格納 できない値は,そのようなリテラルとして書き込むことはできません.

2進,8進,16進リテラルは,符号なしリテラルの直前に-をつけることができます.これらの リテラルは,符号なしリテラルが,値の2の補数行うのと同じサイズの符号なし整数を生成します.

julia> -0x2
0xfe

julia> -0x0002
0xfffe

整数のようなプリミティブな数値型で表現可能な最小値と最大値は,typemintypemax関数を使って調べることができます.

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 loopsStringsInterpolationといった,まだ紹介していないいくつかの機能を 使用していますが,プログラミング経験のあるユーザにとっては十分に理解しやすいはずです.)

オーバーフロー

Juliaでは,指定された型で表現可能な最大値を超えると,ラップアラウンド動作が発生します:

julia> x = typemax(Int64)
9223372036854775807

julia> x + 1
-9223372036854775808

julia> x + 1 == typemin(Int64)
true

このように,Juliaの整数を用いた算術は,実際にはmodular arithmeticの 一形態です.これは,現代の計算機に実装されている基本的な整数演算の特性を反映しています. オーバーフローが起きうるアプリケーションでは,オーバーフローによって生じるラップアラウンド を明示的にチェックすることが不可欠です.そうでない場合はArbitrary Precision ArithmeticBigInt型を使用することを推奨します.

オーバーフローの動作例と,それを解決する可能性のある方法を以下に示します:

julia> 10^19
-8446744073709551616

julia> big(10)^19
10000000000000000000

除算エラー

整数の除算(div関数)には,2つの例外的なケースがあります.0で除算する場合と,表現できる中 で最も小さい負の数(typemin)を-1で除算する場合です.これらのケースはどちらでも, DivideErrorをスローします.剰余関数とモジュラス関数(remmod)は2番目の 引数が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

値は簡単に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)

浮動小数点数のゼロ

浮動小数点数には,正のゼロと負のゼロの2つのゼロ があります.これらは互いに等しいですが,bitstring関数を使えばわかるように, 異なる2進数表現がされています.

julia> 0.0 == -0.0
true

julia> bitstring(0.0)
"0000000000000000000000000000000000000000000000000000000000000000"

julia> bitstring(-0.0)
"1000000000000000000000000000000000000000000000000000000000000000"

特別な浮動小数点数の値

実数線上のどの点にも対応しない浮動小数点数の指定された3種類の標準値があります:

Float16Float32Float64NameDescription
Inf16Inf32Infpositive infinity全ての有限な浮動小数点数の値よりも大きい値
-Inf16-Inf32-Infnegative infinity全ての有限な浮動小数点数の値よりも小さい値
NaN16NaN32NaNnot a number(自分自身を含む)全ての浮動小数点数の値に対して==が成り立たない値

これらの非有限浮動小数点数の値が,お互いや他の浮動小数点数に対してどのように順序付けられてい るかについては,Numeric Comparisonsを参照してください.IEEE 754 standard では,これらの浮動小数点数の値は特定の演算の結果です:

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は1.0とその次に大きい浮動小数点数との間の距離を与えるepsを提供しています:

julia> eps(Float32)
1.1920929f-7

julia> eps(Float64)
2.220446049250313e-16

julia> eps() # same as eps(Float64)
2.220446049250313e-16

これらの値はFloat32Float64の値で各々,2.0^-232.0^-52と なっています.eps関数は引数として浮動小数点数の値を取ることもでき,その値と 次の表現可能な浮動小数点数の値との差の絶対値を与えます.つまり,eps(x)x + eps(x)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つの表現可能な浮動小数点数の間の距離は一定ではありませんが,値が小さいほど小さく, 値が大きいほど大きくなります.言い換えれば,表現可能な浮動小数点数はゼロ付近の実数線で最も 密であり,ゼロから遠ざかるにつれて指数関数的に疎になります.定義により,1.0は64ビットの 浮動小数点数の値なので,eps(1.0)eps(Float64)と同じになります.

またJuliaはnextfloatprevfloat関数を提供しています.これはそれぞれ, 引数に与えられた数の次の最大または最小の表現可能な浮動小数点数を返します:

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 754standard で提示されている丸めモードにしたがって変更することができます.

デフォルトモードとして使用されるのは常にRoundNearestで,最も近い表現可能な 値に丸められ,タイは最も近い値に向かって偶数の最下位ビットで丸められます.

背景と参考文献

浮動小数点演算には,低レベルの実装の詳細に慣れていないユーザにとっては驚くような多くの細かな 特徴が含まれています.しかしこうした部分については科学的計算に関する帆とのどの書籍や,以下の 参考文献で詳しく説明されています.

  • 浮動小数点演算の決定的なガイドはIEEE 754-2008 Standardですが,オンラインで無料で手に入れることはできません.
  • 浮動小数点がどのように表現されているのかの簡単ではあるが明確な情報については,John D. Cookのarticleや,この表現が実数の理想化された抽象化とどのように動作が異なるかといったところから生じる問題のいくつかを紹介したintroductionを参照してください.
  • また浮動小数点数に関するBruce Dawsonのseries of blog posts on floating-point numbersもお勧めです.
  • 浮動小数点数と浮動小数点数を使った演算を行う際に発生する数値制度の問題についての詳細な議論については,David Goldbergの論文What Every Computer Scientist Should Know About Floating-Point Arithmeticを参照してください.
  • 浮動小数点数の歴史や根拠,浮動小数点数の問題点,数値計算の他の多くのトピックについては,「浮動小数点の父」として知られるWilliam Kahancollected writingsを参照してください.特に興味深いのは,An Interview with the Old Man of Floating-Pointかもしれません.

任意の精度の演算

任意の精度の整数と浮動小数点数の計算を可能にするために,JuliaはGNU Multiple Precision Arithmetic Library (GMP)GNU MPFRLibraryをそれぞれラップしています.Juliaでは,BigInt型 とBigFloat型が,それぞれ任意精度の整数と浮動小数点数の表現に利用できます.

プリミティブな数値型からこれらの型を作成するためのコンストラクタが存在し, string literal@big_strまたはparse を使用して,AbstractString型からこれらの型を作成することができます.一度作成された数値型 は,Juliaのtype promotion and conversion mechanismにより, 他の全ての数値型と一緒に算術に参加します.

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
Note

暗黙の乗算に使用される数値リテラル係数の優先順位は,乗算(*)や除算(/, \, and //) などの他の2進演算子よりも高くなります.これは例えば,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

どちらの式も関数のアプリケーションとして解釈されます.つまり,数値リテラルではない任意の式は ,その直後に括弧が続く場合には,括弧内の値に適用される関数として解釈されます(関数についての 詳細はFunctionsを参照してください).したがって,これらのケースでは,左側の値は関数 ではないため,エラーが発生します.

上記の構文の強化により,一般的な数式を書くときに発生する視覚的なノイズが大幅に削減されまし た.数値リテラル係数と,それが乗算する識別子または括弧で囲まれた式の間には,空白を入れては いけないことに注意してください.

構文の競合

並置リテラル係数構文は,2つの数値リテラル構文(16進整数リテラルと浮動小数点リテラルの工学的 表記法)と競合することがあります.ここでは構文上の競合が発生する状況をいくつか紹介します:

  • 16進整数リテラル式0xffは,数値リテラル0に変数xffを掛けたものとして解釈される可能性があります.
  • 浮動小数点リテラル式1e10は,数値リテラル1に変数e10を掛けたものとして解釈され,等価なE形式と同様に解釈される可能性があります.
  • 32ビット浮動小数点リテラル1.5f22は,変数f221.5を掛けた数値リテラルとして解釈できます.

全ての場合において,曖昧さは数値リテラルとして解釈することで解決されます:

  • 0xで始まる式は常に16進リテラルです.
  • ある数値リテラルの後にeまたはEが続くような式は,常に浮動小数点リテラルです.
  • ある数値リテラルの後にfが続くような式は,常に32ビット浮動小数点リテラルになります.

歴史的な理由から,数値リテラルでは,eと同等であるEとは異なり,Fは単なる別の文字で あり,数値リテラルではfのように動作しません.したがって,数値リテラルの後にFが続く式 始まる式は,数値リテラルに変数を掛けたものとして解釈され,例えば1.5F221.5 * F22と 等しいということを意味しています.

リテラル0と1

Juliaは,指定された型や,与えられた変数の型に対応するリテラル0と1を返す関数を提供しています.

FunctionDescription
zero(x)xまたは変数xの型のリテラルゼロ
one(x)xまたは変数xの型のリテラル1

これらの関数は,Numeric Comparisonsの際に,不要なtype conversion によるオーバーヘッドを回避するのに便利です.

以下に例を示します:

julia> zero(Float32)
0.0f0

julia> zero(1.0)
0.0

julia> one(Int32)
1

julia> one(BigFloat)
1.0