整數和浮點數

整數和浮點數值是算術和運算的基本構建塊。此類數值的內建表示稱為數值基本型,而整數和浮點數值在程式碼中作為立即數值的表示則稱為數值文字。例如,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> 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 也可用於輸入)進行輸入和輸出。無號值的長度由所使用的十六進位數字數量決定

julia> x = 0x1
0x01

julia> typeof(x)
UInt8

julia> x = 0x123
0x0123

julia> typeof(x)
UInt16

julia> x = 0x1234567
0x01234567

julia> typeof(x)
UInt32

julia> x = 0x123456789abcdef
0x0123456789abcdef

julia> typeof(x)
UInt64

julia> x = 0x11112222333344445555666677778888
0x11112222333344445555666677778888

julia> typeof(x)
UInt128

此行為基於一個觀察,當有人使用未簽名的十六進制字面值表示整數值時,通常會使用它們來表示固定的數字位元組序列,而非僅僅是一個整數值。

二進制和八進制字面值也受支援

julia> x = 0b10
0x02

julia> typeof(x)
UInt8

julia> x = 0o010
0x08

julia> typeof(x)
UInt8

julia> x = 0x00000000000000001111222233334444
0x00000000000000001111222233334444

julia> typeof(x)
UInt128

與十六進制字面值一樣,二進制和八進制字面值會產生未簽名的整數型別。二進制資料項的大小為所需的最小大小,如果字面值的開頭數字不是 0。在開頭為零的情況下,大小由字面值的所需最小大小決定,其長度相同但開頭數字為 1。這表示

  • 0x10x12UInt8 字面值,
  • 0x1230x1234UInt16 字面值,
  • 0x123450x12345678UInt32 字面值,
  • 0x1234567890x1234567890adcdefUInt64 字面值,依此類推。

即使有開頭的零數字不會對值造成影響,它們仍會計算在決定字面值的儲存大小中。因此 0x01UInt8,而 0x0001UInt16

這允許使用者控制大小。

對編碼成過大而無法表示為 UInt128 值的整數的未簽名字面值 (從 0x 開始) 將會建構 BigInt 值。這不是未簽名型別,但它是唯一內建的型別,夠大到足以表示如此大的整數值。

二進制、八進制和十六進制字面值可以由緊接在未簽名字面值之前的 - 符號簽署。它們會產生與未簽名字面值相同大小的未簽名整數,並具有值的二補數

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 迴圈字串內插,但對於具備一些程式設計經驗的使用者而言,應足夠容易理解。)

溢位行為

在 Julia 中,超過給定類型的最大可表示值會導致換行行為

julia> x = typemax(Int64)
9223372036854775807

julia> x + 1
-9223372036854775808

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

因此,Julia 整數的算術實際上是一種 模算術 形式。這反映了在現代電腦上實作的整數底層算術的特性。在可能發生溢位的應用程式中,必須明確檢查溢位產生的換行;否則,建議改用 任意精度算術 中的 BigInt 類型。

以下是溢位行為和如何潛在解決它的範例

julia> 10^19
-8446744073709551616

julia> big(10)^19
10000000000000000000

除法錯誤

整數除法(div 函數)有兩個例外情況:除以零,以及將最低負數 (typemin) 除以 -1。這兩個情況都會擲出 DivideError。當第二個參數為零時,餘數和模數函數(remmod)會擲出 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> x = 0.5f0
0.5f0

julia> typeof(x)
Float32

julia> 2.5f-4
0.00025f0

值可輕鬆轉換為Float32

julia> x = Float32(-1.5)
-1.5f0

julia> typeof(x)
Float32

十六進位浮點數文字也為有效值,但僅限於Float64 值,且 p 必須出現在基底 2 指數之前

julia> 0x1p0
1.0

julia> 0x1.8p3
12.0

julia> x = 0x.4p-1
0.125

julia> typeof(x)
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"

特殊浮點數值

有三個指定的標準浮點數值不對應到實數線上的任何一點

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

julia> NaN == NaN
false

julia> NaN != NaN
true

julia> NaN < NaN
false

julia> NaN > NaN
false

typemintypemax 函數也適用於浮點數類型

julia> (typemin(Float16),typemax(Float16))
(-Inf16, Inf16)

julia> (typemin(Float32),typemax(Float32))
(-Inf32, Inf32)

julia> (typemin(Float64),typemax(Float64))
(-Inf, Inf)

機器 epsilon

大多數實數無法以浮點數精確表示,因此對於許多目的而言,了解兩個相鄰可表示浮點數之間的距離非常重要,這通常稱為機器 epsilon

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

兩個相鄰可表示浮點數之間的距離並非恆定,而是對較小的值較小,對較大的值較大。換句話說,可表示浮點數在接近零的實數線上最密集,隨著遠離零的距離增加而呈指數級稀疏。根據定義,eps(1.0)eps(Float64) 相同,因為 1.0 是 64 位元浮點數值。

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 754 標準 中提供的捨入模式,可以視需要變更執行此捨入的方式。

使用的預設模式始終為 RoundNearest,它會捨入至最接近的可表示值,而捨入至最接近且最低有效位元為偶數的值。

背景與參考資料

浮點數運算包含許多細微差別,對於不熟悉低階實作細節的使用者而言,可能會感到驚訝。但是,大多數科學運算書籍以及下列參考資料都詳細說明了這些細微差別

任意精度運算

為了允許使用任意精度的整數和浮點數進行運算,Julia 封裝了 GNU 多重精度運算函式庫 (GMP)GNU MPFR 函式庫。Julia 中提供了 BigIntBigFloat 類型,分別用於任意精度的整數和浮點數。

建構函式用於從原始數值類型建立這些類型,而 字串文字 @big_strparse 可用於從 AbstractString 建立這些類型。當 BigInt 太大而無法使用其他內建整數類型時,也可以將其輸入為整數文字。請注意,由於 Base 中沒有無符號任意精度整數類型(在大部分情況下 BigInt 就已足夠),因此可以使用十六進位、八進位和二進位文字(除了十進位文字)。

建立後,它們會參與所有其他數值類型的算術運算,這要歸功於 Julia 的 類型提升和轉換機制

julia> BigInt(typemax(Int64)) + 1
9223372036854775808

julia> big"123456789012345678901234567890" + 1
123456789012345678901234567891

julia> parse(BigInt, "123456789012345678901234567890") + 1
123456789012345678901234567891

julia> string(big"2"^200, base=16)
"100000000000000000000000000000000000000000000000000"

julia> 0x100000000000000000000000000000000-1 == typemax(UInt128)
true

julia> 0x000000000000000000000000000000000
0

julia> typeof(ans)
BigInt

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

可以透過呼叫 setprecisionsetrounding 來變更 BigFloat 運算的預設精度(有效數字的位元數)和捨入模式,而後續的所有計算都會考量這些變更。或者,也可以只在特定程式碼區塊執行時變更精度或捨入,方法是使用具有 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

兩個表達式都會被解釋為函數應用:任何非數字文字的表達式,在緊接在括號之後時,會被解釋為對括號中值的函數應用(請參閱 函數 以進一步了解函數)。因此,在這些情況下,由於左邊的值不是函數,因此會發生錯誤。

上述語法增強功能大幅減少在撰寫常見數學公式時產生的視覺雜訊。請注意,數字文字係數與它所乘的識別碼或括號表達式之間不能有空白。

語法衝突

並置的字面係數語法可能會與某些數字字面語法產生衝突:十六進位、八進位和二進位整數字面,以及浮點字面中的工程表示法。以下是語法衝突發生的幾個情況

  • 十六進位整數字面表達式 0xff 可以解釋為數字字面 0 乘以變數 xff。八進位和二進位字面(例如 0o7770b01001010)也會產生類似的歧義。
  • 浮點字面表達式 1e10 可以解釋為數字字面 1 乘以變數 e10,對應的 E 形式也類似。
  • 32 位浮點字面表達式 1.5f22 可以解釋為數字字面 1.5 乘以變數 f22

在所有情況下,歧義都傾向於解釋為數字字面

  • 0x/0o/0b 開頭的表達式始終是十六進位/八進位/二進位字面。
  • 以數字字面後接 eE 開頭的表達式始終是浮點字面。
  • 以數字字面後接 f 開頭的表達式始終是 32 位浮點字面。

與基於歷史原因在數字字面中等於 eE 不同,F 只是一個字母,在數字字面中不會像 f 那樣運作。因此,以數字字面後接 F 開頭的表達式會被解釋為數字字面乘以一個變數,這表示例如 1.5F22 等於 1.5 * F22

字面零和一

Julia 提供函數,會傳回對應於指定類型或給定變數類型的字面 0 和 1。

函數說明
zero(x)型別 x 的字面零或變數 x 的型別
one(x)型別 x 或變數 x 的型別的字面一

這些函式在 數字比較 中很有用,可避免不必要的 型別轉換 所造成的負擔。

範例

julia> zero(Float32)
0.0f0

julia> zero(1.0)
0.0

julia> one(Int32)
1

julia> one(BigFloat)
1.0