Julia 是一種通用程式語言,可以用來處理大多數的程式設計工作。 不過實際上,它最主要的應用場景多半落在工程與科學領域。 快速、靈活且精密的數值運算,是它設計上的核心。
整數是沒有小數點的「整」數。
在 Basics 概念裡,我們看到可以把整數值指定給變數,而不需要指定型別。
為了方便閱讀,可以用底線當作數字分隔符號。 編譯器會忽略它們。
julia> x = 3
3
julia> typeof(x)
Int64
julia> large_number = 1_234_567_890
1234567890
在內部,編譯器會選用對你的 CPU 最合適的有號整數型別。
在現代 PC 上通常會是 Int64,對大多數工作來說已經十分足夠。
型別會在之後的概念裡有更完整的討論。 現在,_數值_型別應該夠直覺,足以讓這份文件讀得通。
我們會在之後的概念看到,Julia 原生支援非常巨大的多維陣列。 這會同時考驗可用記憶體的容量,以及資料傳輸的頻寬。
為了讓程式設計師有更細緻的控制,指定想要的有號或無號整數型別不但可行,也相當常見。 許多先學過 Python 才來學 Julia 的人,可能會想把它拿來跟 NumPy 比較,而不是跟原生 Python 比。
julia> y = Int8(42)
42
julia> typeof(y)
Int8
julia> z = UInt32(1024)
0x00000400
注意 Julia 預設會以十六進位格式顯示無號整數,因為無號型別常常用於低階的位元運算。
整數也可以用二進位、八進位或十六進位輸入,前綴分別是 0b、0o 和 0x。
julia> a = 0x10
0x10
julia> Int(a)
16
julia> b = 0b1010 # displays as hexadecimal
0x0a
BigInt
每種整數型別都有它能儲存的最大值與最小值:
julia> typemax(Int8)
127
julia> typemin(Int8)
-128
超出這個有效範圍就會造成整數溢位,結果看起來會非常奇怪。
julia> c = Int8(126)
126
julia> c * c
4
我們大概都會同意,126 * 126 應該_不_等於 4!
要詳細解釋,需要了解 CPU 暫存器如何儲存整數,這超出了本概念的範疇。
使用「更寬」的型別可以把問題降到最低,最大可以到 Int128 或 UInt128(「更寬」的意思會在下面討論)。 不過,它們在遇到溢位問題之前,都各有硬性上限。
其中一個解法是使用BigInt型別,它只受限於你電腦的記憶體。
這是 Julia 對任意精度算術的實作。
julia> 2 ^ 70
0
julia> big = BigInt(2) ^ 70
1180591620717411303424
julia> UInt128(big)
0x00000000000000400000000000000000
因為 2 的 70 次方放不進 Int64,上面例子中的第一個計算會失敗:不過要注意,並不會出現任何錯誤訊息。
寫程式的人請小心!
把 2 轉型成BigInt就會得到正確答案,用十六進位顯示也更能看出這是一個 2 的次方。
不出意料,浮點數可以有小數點,以及小數點後的小數部分。
julia> f = 3.45
3.45
julia> typeof(f)
Float64
當然,也支援科學記號。
julia> avogadro = 6.02e23
6.02e23
和整數一樣,預設型別對大多數用途都沒問題,但還有其他有號型別可用。 並沒有_無號_浮點型別。
有個捷徑:加上 f0 後綴就能建立 Float32 值。
julia> f32 = 4.56f0
4.56f0
julia> typeof(f32)
Float32
其他大小的浮點數則需要明確轉型,和整數一樣。
最大值和最小值可能會讓你嚇一跳:
julia> typemax(Float64)
Inf
julia> typemin(Float64)
-Inf
無限大是一個合法的值!
不過,浮點數的可用範圍有限,數值太大就直接指定為 Inf,太小則捨入成 0.0。
我們可以用另一組函式來看看這些極限:大約是 ± 10 ^ 308。
julia> floatmax(Float64)
1.7976931348623157e308
julia> floatmin(Float64)
2.2250738585072014e-308
此外,精度也有限,能表示的有效位數雖然多但仍是有限個(Float64 大約是 15 位)。
如同 Basics 概念中所討論的,算術運算子大多和我們小時候學的標準算術一樣。
注意,指數運算用的是^,不是**(兩者在其他語言裡都很常見)。
2 + 3 # 5 (addition)
2 - 3 # -1 (subtraction)
2 * 3 # 6 (multiplication)
8 / 2 # 4.0 (division)
8 % 3 # 2 (remainder)
2 ^ 3 # 8 (exponentiation)
不過,有幾個 Julia 特有的細節值得討論。
julia> x = 4.2
4.2
julia> 2 * x
8.4
julia> 2x
8.4
julia> 2.4x
10.08
這可能有點出乎意料。
和大多數其他程式語言一樣,你隨時可以用*作為中綴運算子。
不過,Julia 的設計者相信,程式碼應該盡可能看起來像數學算式。
因為變數名稱必須以字母開頭,所以在名稱前面加上數字(整數或浮點數)會被視為隱含的乘法。
舉例來說,如果我們想要球體的表面積,除了 4 * pi * r * r,也可以這樣寫:
julia> surface(r) = 4π * r^2
surface (generic function with 1 method)
julia> surface(3)
113.09733552923255
雖然 π 是內建的常數,它同時也是一個(希臘)字母。
所以剖析器仍然需要一個明確的*,把π和r分開。
用/作為中綴運算子,永遠會得到浮點數結果,即使輸入是整數也一樣。
整數除法則有更多選擇:
julia> 10 / 3
3.3333333333333335
julia> div(10, 3)
3
julia> 10 ÷ 3
3
julia> 10 // 3
10//3
div()函式用於整數除法,結果會朝零截斷:正數向下、負數向上。
作為同義寫法,我們可以用中綴運算子÷,同樣是為了讓它看起來更像數學。
如果你用的編輯器看得懂 Julia,輸入 \div 然後按 <Tab> 鍵即可。
//運算子之後在課程大綱裡會有專門的一個概念。
現在我們只要說,//的結果是「有理數」,這是大多數人稱為分數的東西的正式名稱。
分子和分母的公因數會被約掉,得出兩個整數的比值,也就是所謂的「最簡分數」。
julia> rationalnum = 22 // 6
11//3
julia> typeof(rationalnum)
Rational{Int64}
我們有了有理數。 那「無理」數呢?
julia> π
π = 3.1415926535897...
julia> typeof(π)
Irrational{:π}
無理數是無法化為整數比值的數。
常見的例子包括π、e(歐拉數),以及許多根號,例如√2(2 的平方根)。
Julia 試著_好好地_做數學。
這常常會自動發生:
julia> x = 2 + 3.5
5.5
julia> typeof(x)
Float64
我們把 Int64 加上 Float64,得到 Float64 的結果。
事實上,整數在做加法之前,就已經悄悄被轉成 Float64 了。
Julia 有個數值型別「寬度」的概念。
Int64 比 Int16 更寬。如果一個運算式裡混用了多種型別,每個值都會視需要被「提升」到所用到的最寬型別。
要強制轉換,我們可以把整數轉型成特定型別,例如Float64(5)。
或者直接用float(5),讓編譯器挑一個合適的型別。
浮點數轉整數的轉換無可避免地比較複雜。 小數點後面的部分你想怎麼處理?
round()函式會轉成最接近的整數,遇到像 4.5 這種剛好在中間的情況,會捨入到最接近的_偶數_。floor()向下捨入,ceil()向上捨入,trunc()朝零捨入。Int32(),會失敗並丟出 InexactError。不過,這些函式預設不會回傳你可能想要的整數型別。 你可以指定想要的輸出型別。
julia> round(4.5)
4.0
julia> round(Int64, 4.5)
4
julia> round(Int, 4.5) # => default integer type
4
julia> ceil(Int16, 4.3)
5
也可以用digits關鍵字,指定要捨入到小數點後幾位。
julia> round(π, digits=10)
3.1415926536
詳情請參閱手冊。
這想必只會丟出錯誤吧? 事實上,情況沒那麼單純。
用÷或//做整數除法,會如你所料產生錯誤。
用/做浮點除法,走的則是比較像工程的做法,而不是標準的資訊科學做法:
julia> 2 / 0
Inf
julia> 0 / 0
NaN
如前一節所討論的,無限大在 Julia 裡是合法的浮點數,以Inf表示。
當分子也是零時,結果在數學上是未定義的。
Julia 會把它當成「非數值」,以NaN表示。
如果這看起來很奇怪,想想你是在處理大量真實世界(因此常常相當雜亂)的資料陣列。 為了繼續推進,最好的做法就是把有問題的值標記起來,然後繼續往下走。
無止盡地手動檢查每個值,寫起來很煩人,也肯定會拖累執行時的效能。
只要有一點小狀況就停下來丟錯誤訊息,會讓你的程式_非常_不受使用者歡迎!
如 Conditionals概念所述,相等性測試通常用==運算子來做。
這對整數、字元、字串等都運作得很好。
不過,浮點數值的精度有限,用不同方式算出同一個結果,也可能得到略有差異的值。
對 Float64 來說,差異通常會落在第 15 位有效數字附近:差距很小,但並不「相等」。
傳統上給程式設計師的建議是:_千萬不要_對浮點數值使用==,因為結果無法預測。
一個廣為使用的替代做法,是拿兩者差值的絕對值去和某個容許誤差(通常叫 epsilon 或 ϵ)比較。
所以不要用 a == b,改用 abs(a - b) < epsilon。
Julia 用 isapprox()函式提供了更乾淨的替代方案。
要用絕對容差,語法是isapprox(a, b, atol=epsilon)。
這種情況下一定要用atol關鍵字。
相對容差通常更有用。
這是預設值,所以isapprox(a, b)會試著為rtol挑一個合理的值(規則相當複雜)。
相對容差也可以指定,用被比較數值的比例來表示。
所以isapprox(a, b, rtol=0.01會測試兩個值是否在彼此的 1% 以內。
依照 Julia 慣例,有個數學運算子等同於預設情況:a ≈ b(這個運算子輸入方式是 \approx 再按 <tab>)。
除了有理數之外,課程大綱後面的部分還會討論: