整数和浮点数

    Julia 提供了很丰富的原始数值类型,并基于它们定义了一整套算术操作,还提供按位运算符以及一些标准数学函数。这些函数能够直接映射到现代计算机原生支持的数值类型及运算上,因此 Julia 可以充分地利用运算资源。此外,Julia 还为提供了软件支持,对于无法使用原生硬件表示的数值类型,Julia 也能够高效地处理其数值运算。当然,这需要相对的牺牲一些性能。

    以下是 Julia 的原始数值类型:

    • 整数类型:

    • 浮点类型:

    此外,对复数和有理数的完整支持是在这些原始数据类型之上建立起来的。多亏了 Julia 有一个很灵活的、用户可扩展的,所有的数值类型都无需显式转换就可以很自然地相互进行运算。

    整数字面量以标准形式表示:

    整型字面量的默认类型取决于目标系统是 32 位还是 64 位架构:

    1. # 32 位系统:
    2. julia> typeof(1)
    3. Int32
    4. # 64 位系统:
    5. julia> typeof(1)
    6. Int64

    Julia 的内置变量 Sys.WORD_SIZE 表明了目标系统是 32 位还是 64 位架构:

    1. # 32 位系统:
    2. julia> Sys.WORD_SIZE
    3. 32
    4. # 64 位系统:
    5. julia> Sys.WORD_SIZE
    6. 64

    Julia 也定义了 IntUInt 类型,它们分别是系统有符号和无符号的原生整数类型的别名。

    1. # 32 位系统:
    2. julia> Int
    3. Int32
    4. julia> UInt
    5. UInt32
    6. # 64 位系统:
    7. julia> Int
    8. Int64
    9. julia> UInt
    10. UInt64

    那些超过 32 位表示范围的大整数,如果能用 64 位表示,那么无论是什么系统都会用 64 位表示:

    1. # 32 位或 64 位系统:
    2. julia> typeof(3000000000)
    3. Int64

    无符号整数会通过 0x 前缀以及十六进制数 0-9a-f 来输入和输出(输入也可以使用大写的 A-F)。无符号值的位数取决于十六进制数字使用的数量:

    1. julia> 0x1
    2. 0x01
    3. julia> typeof(ans)
    4. UInt8
    5. julia> 0x123
    6. 0x0123
    7. julia> typeof(ans)
    8. UInt16
    9. julia> 0x1234567
    10. 0x01234567
    11. julia> typeof(ans)
    12. UInt32
    13. julia> 0x123456789abcdef
    14. 0x0123456789abcdef
    15. julia> typeof(ans)
    16. UInt64
    17. julia> 0x11112222333344445555666677778888
    18. 0x11112222333344445555666677778888
    19. julia> typeof(ans)
    20. UInt128

    采用这种做法是因为,当人们使用无符号十六进制字面量表示整数值的时候,通常会用它们来表示一个固定的数值字节序列,而不仅仅是个整数值。

    还记得这个 变量吗?它存着交互式会话中上一个表达式的运算结果,但以其他方式运行的 Julia 代码中没有这个变量。

    二进制和八进制字面量也是支持的:

    1. julia> 0b10
    2. 0x02
    3. julia> typeof(ans)
    4. UInt8
    5. julia> 0o010
    6. 0x08
    7. julia> typeof(ans)
    8. UInt8
    9. julia> 0x00000000000000001111222233334444
    10. 0x00000000000000001111222233334444
    11. julia> typeof(ans)
    12. UInt128

    二进制、八进制和十六进制的字面量都会产生无符号的整数类型。当字面量不是开头全是 0 时,它们二进制数据项的位数会是最少需要的位数。当开头都是 0 时,位数取决于一个字面量需要的最少位数,这里的字面量指的是一个有着同样长度但开头都为 1 的数。这样用户就可以控制位数了。那些无法使用 UInt128 类型存储下的值无法写成这样的字面量。

    二进制、八进制和十六进制的字面量可以在前面紧接着加一个负号 -,这样可以产生一个和原字面量有着同样位数而值为原数的补码的数(二补数):

    1. julia> -0x2
    2. 0xfe
    3. julia> -0x0002
    4. 0xfffe

    整型等原始数值类型的最小和最大可表示的值可用 typemin 和 函数得到:

    1. julia> (typemin(Int32), typemax(Int32))
    2. (-2147483648, 2147483647)
    3. julia> for T in [Int8,Int16,Int32,Int64,Int128,UInt8,UInt16,UInt32,UInt64,UInt128]
    4. println("$(lpad(T,7)): [$(typemin(T)),$(typemax(T))]")
    5. Int8: [-128,127]
    6. Int16: [-32768,32767]
    7. Int32: [-2147483648,2147483647]
    8. Int64: [-9223372036854775808,9223372036854775807]
    9. Int128: [-170141183460469231731687303715884105728,170141183460469231731687303715884105727]
    10. UInt8: [0,255]
    11. UInt16: [0,65535]
    12. UInt32: [0,4294967295]
    13. UInt64: [0,18446744073709551615]
    14. UInt128: [0,340282366920938463463374607431768211455]

    typemin 和 返回的值的类型总与所给参数的类型相同。(上面的表达式用了一些目前还没有介绍的功能,包括 for 循环、和插值,但对于已有一些编程经验的用户应该是很容易理解的。)

    Julia 中,超出一个类型可表示的最大值会导致循环行为:

    1. julia> x = typemax(Int64)
    2. 9223372036854775807
    3. julia> x + 1
    4. -9223372036854775808
    5. julia> x + 1 == typemin(Int64)

    除法错误

    div 函数的整数除法有两种异常情况:除以零,以及使用 -1 去除最小的负数()。 这两种情况都会抛出一个 DivideError 错误。 rem 取余函数和 mod 取模函数在除零时抛出 错误。

    浮点数字面量也使用标准格式表示,必要时可使用 E-表示法

    上面的结果都是 值。使用 f 替代 e 可以得到 Float32 的字面量:

    1. julia> 0.5f0
    2. 0.5f0
    3. julia> typeof(ans)
    4. Float32
    5. julia> 2.5f-4
    6. 0.00025f0

    数值容易就能转换成 :

    1. julia> Float32(-1.5)
    2. -1.5f0
    3. julia> typeof(ans)
    4. Float32

    也存在十六进制的浮点数字面量,但只适用于 Float64 值。一般使用 p 前缀及以 2 为底的指数来表示:

    1. julia> 0x1p0
    2. 1.0
    3. julia> 0x1.8p3
    4. 12.0
    5. julia> 0x.4p-1
    6. 0.125
    7. julia> typeof(ans)
    8. Float64

    Julia 也支持半精度浮点数(),但它们是使用 Float32 进行模拟实现的。

    1. julia> sizeof(Float16(4.))
    2. 2
    3. julia> 2*Float16(4.)
    4. Float16(8.0)

    下划线 _ 可用作数字分隔符:

    1. julia> 10_000, 0.000_000_005, 0xdead_beef, 0b1011_0010
    2. (10000, 5.0e-9, 0xdeadbeef, 0xb2)

    浮点数有,正零和负零。它们相互相等但有着不同的二进制表示,可以使用 bitstring 函数来查看:

    1. julia> 0.0 == -0.0
    2. true
    3. julia> bitstring(0.0)
    4. "0000000000000000000000000000000000000000000000000000000000000000"
    5. julia> bitstring(-0.0)
    6. "1000000000000000000000000000000000000000000000000000000000000000"

    特殊的浮点值

    有三种特定的标准浮点值不和实数轴上任何一点对应:

    对于这些非有限浮点值相互之间以及关于其它浮点值的顺序的更多讨论,请参见。根据 IEEE 754 标准,这些浮点值是某些算术运算的结果:

    1. julia> 1/Inf
    2. 0.0
    3. julia> 1/0
    4. Inf
    5. julia> -5/0
    6. -Inf
    7. julia> 0.000001/0
    8. Inf
    9. julia> 0/0
    10. NaN
    11. julia> 500 + Inf
    12. Inf
    13. julia> 500 - Inf
    14. -Inf
    15. julia> Inf + Inf
    16. Inf
    17. julia> Inf - Inf
    18. NaN
    19. julia> Inf * Inf
    20. Inf
    21. julia> Inf / Inf
    22. NaN
    23. julia> 0 * Inf
    24. NaN

    typemax 函数同样适用于浮点类型:

    1. julia> (typemin(Float16),typemax(Float16))
    2. (-Inf16, Inf16)
    3. julia> (typemin(Float32),typemax(Float32))
    4. (-Inf32, Inf32)
    5. julia> (typemin(Float64),typemax(Float64))

    大多数实数都无法用浮点数准确地表示,因此有必要知道两个相邻可表示的浮点数间的距离。它通常被叫做。

    Julia 提供了 eps 函数,它可以给出 1.0 与下一个 Julia 能表示的浮点数之间的差值:

    1. julia> eps(Float32)
    2. 1.1920929f-7
    3. julia> eps(Float64)
    4. 2.220446049250313e-16
    5. julia> eps() # 与 eps(Float64) 相同
    6. 2.220446049250313e-16

    这些值分别是 中的 2.0^-23Float64 中的 2.0^-52。 函数也可以接受一个浮点值作为参数,然后给出这个值与下一个可表示的值直接的绝对差。也就是说,eps(x) 产生一个和 x 类型相同的值使得 x + eps(x) 是比 x 更大的下一个可表示的浮点值:

    两个相邻可表示的浮点数之间的距离并不是常数,数值越小,间距越小,数值越大,间距越大。换句话说,可表示的浮点数在实数轴上的零点附近最稠密,并沿着远离零点的方向以指数型的速度变得越来越稀疏。根据定义, 与 eps(Float64) 相等,因为 1.0 是个 64 位浮点值。

    Julia 也提供了 nextfloat 和 两个函数分别返回基于参数的下一个更大或更小的可表示的浮点数:

    1. julia> x = 1.25f0
    2. 1.25f0
    3. julia> nextfloat(x)
    4. 1.2500001f0
    5. julia> prevfloat(x)
    6. 1.2499999f0
    7. julia> bitstring(prevfloat(x))
    8. "00111111100111111111111111111111"
    9. julia> bitstring(x)
    10. "00111111101000000000000000000000"
    11. julia> bitstring(nextfloat(x))
    12. "00111111101000000000000000000001"

    这个例子体现了一般原则,即相邻可表示的浮点数也有着相邻的二进制整数表示。

    舍入模式

    一个数如果没有精确的浮点表示,就必须被舍入到一个合适的可表示的值。然而,如果想的话,可以根据舍入模式改变舍入的方式,如 IEEE 754 标准 所述。

    Julia 所使用的默认模式总是 ,指舍入到最接近的可表示的值,这个被舍入的值会使用尽量少的有效位数。

    • 浮点数算术的权威指南是 IEEE 754-2008 标准; 然而这篇标准在网上无法免费获得。
    • 关于浮点数是如何表示的,想要一个简单而明白的介绍的话,可以看 John D. Cook 的以及他关于从这种表示与实数理想的抽象化的差别中产生的一些问题的介绍
    • 同样推荐 Bruce Dawson 的。
    • 想要一个对浮点数和使用浮点数计算时产生的数值精度问题的极好的、有深度的讨论,可以参见 David Goldberg 的文章 What Every Computer Scientist Should Know About Floating-Point Arithmetic
    • 更多延伸文档,包括浮点数的历史、基础理论、问题以及数值计算中很多其它主题的讨论,可以参见 的写作集。他以“浮点数之父”闻名。特别感兴趣的话可以看 。

    为了允许使用任意精度的整数与浮点数,Julia 分别包装了 GNU Multiple Precision Arithmetic Library (GMP) 以及 。Julia 中的 BigInt 与 两种类型分别提供了任意精度的整数和浮点数。

    存在从原始数字类型创建它们的构造器,也可以使用 parseAbstractString 来构造它们。一旦被创建,它们就可以像所有其它数值类型一样参与算术(也是多亏了 Julia 的)。

    1. julia> BigInt(typemax(Int64)) + 1
    2. 9223372036854775808
    3. julia> parse(BigInt, "123456789012345678901234567890") + 1
    4. 123456789012345678901234567891
    5. julia> parse(BigFloat, "1.23456789012345678901")
    6. 1.234567890123456789010000000000000000000000000000000000000000000000000000000004
    7. julia> BigFloat(2.0^66) / 3
    8. 2.459565876494606882133333333333333333333333333333333333333333333333333333333344e+19
    9. julia> factorial(BigInt(40))
    10. 815915283247897734345611269596115894272000000000

    然而,上面的原始类型与 BigInt/ 之间的类型提升并不是自动的,需要明确地指定:

    1. julia> x = typemin(Int64)
    2. -9223372036854775808
    3. julia> x = x - 1
    4. 9223372036854775807
    5. julia> typeof(x)
    6. Int64
    7. julia> y = BigInt(typemin(Int64))
    8. -9223372036854775808
    9. julia> y = y - 1
    10. -9223372036854775809
    11. julia> typeof(y)
    12. BigInt

    BigFloat 的默认精度(有效数字的位数)和舍入模式可以通过调用 和 setrounding 来全局地改变,所有之后的计算都会根据这些改变进行。还有一种方法,可以使用同样的函数以及 do-block 来只在运行一个特定代码块时改变精度和舍入模式:

    1. julia> setrounding(BigFloat, RoundUp) do
    2. BigFloat(1) + parse(BigFloat, "0.1")
    3. end
    4. 1.100000000000000000000000000000000000000000000000000000000000000000000000000003
    5. julia> setrounding(BigFloat, RoundDown) do
    6. BigFloat(1) + parse(BigFloat, "0.1")
    7. end
    8. 1.099999999999999999999999999999999999999999999999999999999999999999999999999986
    9. julia> setprecision(40) do
    10. BigFloat(1) + parse(BigFloat, "0.1")
    11. end
    12. 1.1000000000004

    为了让常见的数值公式和表达式更清楚,Julia 允许变量直接跟在一个数值字面量后,暗指乘法。这可以让写多项式变得很清楚:

    1. julia> x = 3
    2. 3
    3. julia> 2x^2 - 3x + 1
    4. 10
    5. julia> 1.5x^2 - .5x + 1
    6. 13.0

    也会让写指数函数变得更加优雅:

    1. julia> 2^2x
    2. 64

    数值字面量系数的优先级跟一元运算符相同,比如说取相反数。所以 2^3x 会被解析成 2^(3x),而 2x^3 会被解析成 2*(x^3)

    数值字面量也能作为被括号表达式的系数:

    1. julia> 2(x-1)^2 - 3(x-1) + 1
    2. 3

    Note

    用于隐式乘法的数值字面量系数的优先级高于其它的二元运算符,例如乘法(*)和除法(/\ 以及 //)。这意味着,比如说,1 / 2im 等于 -0.5im 以及 6 // 2(2+1) 等于 1 // 1

    此外,括号表达式可以被用作变量的系数,暗指表达式与变量相乘:

    1. julia> (x-1)x
    2. 6

    但是,无论是把两个括号表达式并列,还是把变量放在括号表达式之前,都不会被用作暗指乘法:

    1. julia> (x-1)(x+1)
    2. ERROR: MethodError: objects of type Int64 are not callable
    3. julia> x(x+1)
    4. ERROR: MethodError: objects of type Int64 are not callable

    这两种表达式都会被解释成函数调用:所有不是数值字面量的表达式,后面紧跟一个括号,就会被解释成使用括号内的值来调用函数(更多关于函数的信息请参见)。因此,在这两种情况中,都会因为左手边的值并不是函数而产生错误。

    上述的语法糖显著地降低了在写普通数学公式时的视觉干扰。注意数值字面量系数和后面用来相乘的标识符或括号表达式之间不能有空格。

    语法冲突

    并列的字面量系数语法可能和两种数值字面量语法产生冲突:十六进制整数字面量以及浮点字面量的工程表示法。下面是几种会产生语法冲突的情况:

    • 十六进制整数字面量 0xff 可能被解释成数值字面量 0 乘以变量 xff
    • 浮点字面量表达式 1e10 可以被解释成数值字面量 1 乘以变量 e10,与之等价的 E-表示法也存在类似的情况。
    • 32-bit 的浮点数字面量 1.5f22 被解释成数值字面量 1.5 乘以变量 f22。在这些所有的情况中,都使用这样的解释方式来解决歧义:

    • 0x 开头的表达式总是十六进制字面量。

    • 数值开头跟着 eE 的表达式总是浮点字面量。

    Julia 提供了 0 和 1 的字面量函数,可以返回特定类型或所给变量的类型。

    这些函数在数值比较中可以用来避免不必要的带来的开销。