整数和浮点数

整数和浮点数是算术和计算的基本组成部分。这些值的内置表示称为数字基元,而整数和浮点数在代码中作为立即值的表示称为数字字面量。例如,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 字面量,等等。

即使存在不影响值的先行零位数字,它们也会被计入确定字面量存储大小的范围。因此 0x01 是一个 UInt8,而 0x0001 是一个 UInt16

这允许用户控制大小。

编码大于 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)

机器ε

大多数实数不能用浮点数精确表示,因此,对于许多目的,了解两个相邻的可表示浮点数之间的距离很重要,这通常被称为 机器ε

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 库。该 BigIntBigFloat 类型在 Julia 中分别用于任意精度的整数和浮点数。

构造函数存在于从原始数值类型创建这些类型,并且该 字符串文字 @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

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^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 位浮点字面量。

E(由于历史原因,在数字字面量中等效于e)不同,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