Julia 数据类型
在编程语言中,都有基本的数学运算和科学计算,它们常用的数据类型为整数和浮点数。
另外还有一个"字面量"的术语,字面量(literal)用于表达源代码中一个固定值的表示法(notation),整数、浮点数以及字符串等等都是字面量。
例如:
a = 1 # a 是变量,1 是整型字面量 b = 1.0 # b 是变量,1.0 是浮点型字面量
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) |
整数字面量形式:
实例
1
julia> 1234
1234
整型字面量的默认类型取决于目标系统是 32 位还是 64 位架构(目前大部分系统都是 64 位):
实例
julia> typeof(1)
Int64
julia> Sys.WORD_SIZE
64
Julia 也定义了 Int 与 UInt 类型,它们分别是系统有符号和无符号的原生整数类型的别名,在 64 位系统上 Int 就是 Int64。
不同进制的字面量
Julia 支持16 进制、2 进制和 8 进制字面量,它们都以无符号整数表示,类型由字面量的位数决定:
实例
julia> 0x1
0x01
julia> typeof(0x1) # 1 位 16 进制数对应 UInt8
UInt8
julia> typeof(0x123) # 3 位 16 进制数对应 UInt16
UInt16
# 0b 前缀表示 2 进制
julia> 0b10
0x02
# 0o 前缀表示 8 进制
julia> 0o010
0x08
# 下划线可以用作分隔符提高可读性
julia> 0xdead_beef
0xdeadbeef
超出 128 位的 16 进制字面量会自动创建 BigInt 类型。
typemin 与 typemax
typemin() 和 typemax() 函数可以查询一个整数类型的最小值和最大值:
实例
(-2147483648, 2147483647)
julia> typemax(Int64)
9223372036854775807
溢出行为
在 Julia 里,超出一个类型可表示的最大值会导致环绕 (wraparound) 行为:
实例
9223372036854775807
julia> x + 1
-9223372036854775808
julia> x + 1 == typemin(Int64)
true
因此,Julia 的整数算术实际上是模算数的一种形式,它反映了现代计算机实现底层算术的特点。在可能有溢出产生的程序中,对最值边界出现的循环进行显式检查是必要的。
任意精度算术(BigInt)
如果需要无限范围的整数,可以使用 BigInt 类型,它通过 big() 函数创建,不会溢出:
实例
julia> 10^19
-8446744073709551616
# 用 big() 转换为 BigInt 后结果正确
julia> big(10)^19
10000000000000000000
# BigInt 也可以直接用 big"..." 字符串字面量创建
julia> big"123456789012345678901234567890"
123456789012345678901234567890
注意:BigInt 运算比原生整数慢得多,只在确实需要超大整数时使用。
除法错误
整数除法(div 函数)在以下两种例外情况下会触发 DivideError 错误:
- 除以零
- 最小的负数除以 -1(因为结果超出该类型的表示范围)
rem 取余函数和 mod 取模函数在除零时抛出 DivideError 错误,实例如下:
实例
ERROR: DivideError: integer division error
Stacktrace:
[...]
julia> rem(1, 0)
ERROR: DivideError: integer division error
Stacktrace:
[...]
浮点类型
下表列出了 Julia 支持的浮点类型:
| 类型 | 精度 | 比特数 |
|---|---|---|
Float16 | 半精度 | 16 |
Float32 | 单精度 | 32 |
Float64 | 双精度 | 64 |
此外,对复数和有理数的完整支持是在这些原始数据类型之上建立起来的。
浮点数字面量格式表示如下,必要时可使用 E 记数法:
实例
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
注:在科学计数法中,为了使公式简便,可以用带 E 的格式表示。例如 1.03 乘 10 的 8 次方,可简写为 "1.03E+08" 的形式,其中 "E" 是 exponent(指数)的缩写。
上面的结果都是 Float64 类型的值。使用 f 替代 e 可以得到 Float32 类型的字面量:
实例
0.5f0
julia> typeof(x)
Float32
julia> 2.5f-4
0.00025f0
数值可以很容易地通过构造函数转换为 Float32 类型:
实例
-1.5f0
julia> typeof(x)
Float32
也存在十六进制的浮点数字面量,但只适用于 Float64 类型的值,用 p 表示以 2 为底的指数:
实例
1.0
julia> 0x1.8p3
12.0
julia> x = 0x.4p-1
0.125
julia> typeof(x)
Float64
Julia 也支持半精度浮点数(Float16),在不支持半精度指令的硬件上会使用 Float32 进行软件模拟实现:
实例
2
julia> 2*Float16(4.)
Float16(8.0)
下划线 _ 可用作数字分隔符:
实例
(10000, 5.0e-9, 0xdeadbeef, 0xb2)
浮点数中的零
浮点数有两种零,正零和负零。它们相互相等但有着不同的二进制表示,可以使用 bitstring 函数来查看:
实例
true
julia> bitstring(0.0)
"0000000000000000000000000000000000000000000000000000000000000000"
julia> bitstring(-0.0)
"1000000000000000000000000000000000000000000000000000000000000000"
特殊的浮点值
有三种特定的标准浮点值不和实数轴上任何一点对应:
| Float16 | Float32 | Float64 | 名称 | 描述 |
|---|---|---|---|---|
| Inf16 | Inf32 | Inf | 正无穷 | 一个大于所有有限浮点数的数 |
| -Inf16 | -Inf32 | -Inf | 负无穷 | 一个小于所有有限浮点数的数 |
| NaN16 | NaN32 | NaN | 不是一个数 | 一个不和任何浮点值(包括自己)相等(==)的值 |
以下列举了一些浮点数的运算实例:
实例
0.0
julia> 1/0
Inf
julia> -5/0
-Inf
julia> 0/0
NaN
julia> 500 + 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
我们还可以使用 typemin 和 typemax 函数查询浮点类型的边界,浮点类型的最小值是负无穷,最大值是正无穷:
实例
(-Inf16, Inf16)
julia> (typemin(Float32), typemax(Float32))
(-Inf32, Inf32)
julia> (typemin(Float64), typemax(Float64))
(-Inf, Inf)
机器精度
大多数实数都无法用浮点数准确地表示,因此有必要知道两个相邻可表示的浮点数间的距离,它通常被叫做机器精度。
Julia 提供了 eps 函数,它可以给出 1.0 与下一个 Julia 能表示的浮点数之间的差值:
实例
1.1920929f-7
julia> eps(Float64)
2.220446049250313e-16
julia> eps() # 与 eps(Float64) 相同
2.220446049250313e-16
这些值分别是 Float32 中的 2.0^-23 和 Float64 中的 2.0^-52。eps 函数也可以接受一个浮点值作为参数,然后给出这个值与下一个可表示的浮点数值之间的绝对差:
实例
2.220446049250313e-16
julia> eps(1000.)
1.1368683772161603e-13
julia> eps(0.0)
5.0e-324
两个相邻可表示的浮点数之间的距离并不是常数,数值越小,间距越小,数值越大,间距越大。换句话说,可表示的浮点数在实数轴上的零点附近最稠密,并沿着远离零点的方向以指数型的速度变得越来越稀疏。
Julia 也提供了 nextfloat 和 prevfloat 两个函数分别返回基于参数的下一个更大或更小的可表示的浮点数:
实例
1.25f0
julia> nextfloat(x)
1.2500001f0
julia> prevfloat(x)
1.2499999f0
任意精度浮点数(BigFloat)
与 BigInt 对应,BigFloat 提供任意精度的浮点运算,默认精度为 256 位:
实例
2.459565876494606882133333333333333333333333333333333333333333333333333333333344e19
# 可以用 setprecision 指定精度(有效位数)
julia> setprecision(40) do
BigFloat(1) + parse(BigFloat, "0.1")
end
1.1000000000004
舍入模式
一个数如果没有精确的浮点表示,就必须被舍入到一个合适的可表示的值。
Julia 所使用的默认模式总是 RoundNearest,指舍入到最接近的可表示的值;当距离相等(正好在中间)时,向有效位为偶数的一侧舍入,这种策略也叫"银行家舍入":
实例
1.5
julia> BigFloat("1.550564889", 2, RoundNearest)
1.5
julia> BigFloat("1.560564889", 2, RoundNearest)
1.5
0 和 1 的字面量
Julia 提供了 0 和 1 的字面量函数,可以返回特定类型或所给变量的类型对应的 0 和 1:
| 函数 | 描述 |
|---|---|
zero(x) | x 类型或变量 x 的类型的零字面量 |
one(x) | x 类型或变量 x 的类型的一字面量 |
这些函数在数值比较和泛型代码中可以用来避免不必要的类型转换带来的开销:
实例
0.0f0
julia> zero(1.0)
0.0
julia> one(Int32)
1
julia> one(BigFloat)
1.0
数值字面量系数
为了让数学公式写起来更自然,Julia 允许在变量前直接放一个数字字面量表示乘法:
实例
3
# 2x^2 - 3x + 1 等价于 2*x^2 - 3*x + 1
julia> 2x^2 - 3x + 1
10
# 数字字面量也可以乘以括号表达式
julia> 2(x - 1)^2 - 3(x - 1) + 1
3
注意:数字字面量系数的优先级低于一元取负但高于其他乘除运算:
-2x解析为(-2)*x,而2^3x解析为2^(3x)。此外两个括号表达式相邻(如(x-1)(x+1))不表示乘法,会被当作函数调用而报错。
类型转换
类型转换是把变量从一种类型转换为另一种数据类型。Julia 支持三种数值转换,它们在处理不精确转换上有所不同。
第一种:T(x) 或 convert(T, x)
以上写法都会把 x 转换为 T 类型:
- 如果 T 是浮点类型,转换的结果就是最近的可表示值,可能会是正负无穷大。
- 如果 T 为整数类型,当 x 不能由 T 类型精确表示时,会抛出 InexactError。
第二种:x % T
x % T 将整数 x 转换为整型 T,结果与 x 模 2^n 一致(n 是 T 的位数),即截取二进制的低 n 位,不会报错:
第三种:舍入函数
舍入函数接收一个 T 类型的可选参数,比如 round(Int, x) 是 Int(round(x)) 的简写形式。
实例
127
# 128 超出 Int8 的表示范围,抛出 InexactError
julia> Int8(128)
ERROR: InexactError: trunc(Int8, 128)
Stacktrace:
[...]
# 3.14 无法精确表示为整数,同样抛出 InexactError
julia> Int8(3.14)
ERROR: InexactError: Int8(3.14)
Stacktrace:
[...]
# % 转换不报错,128 模 256 得到 -128
julia> 127 % Int8
127
julia> 128 % Int8
-128
julia> round(Int8, 127.4)
127
# 127.6 四舍五入到 128,超出 Int8 范围,抛出 InexactError
julia> round(Int8, 127.6)
ERROR: InexactError: Int8(128.0)
Stacktrace:
[...]
