数学运算和基本函数

Julia 提供了所有数字基本类型的基本算术和按位运算符的完整集合,并提供了全面的标准数学函数的便携式、高效实现。

算术运算符

以下 算术运算符 在所有基本数字类型上都受支持

表达式名称描述
+x一元加恒等运算
-x一元减将值映射到其加法逆元
x + y二元加执行加法
x - y二元减执行减法
x * y执行乘法
x / y执行除法
x ÷ y整数除x / y,截断为整数
x \ y逆除等效于 y / x
x ^ yx 提升到 y 次方
x % y余数等效于 rem(x,y)

直接放在标识符或括号之前的数字字面量,例如 2x2(x+y),被视为乘法,但优先级高于其他二元运算。有关详细信息,请参阅 数字字面量系数

Julia 的提升系统使混合参数类型的算术运算能够自然而然地自动“生效”。有关提升系统的详细信息,请参阅 转换和提升

可以通过在 REPL 或 Julia IDE 中输入 \div<tab> 方便地键入 ÷ 符号。有关更多信息,请参阅 Unicode 输入手册部分

以下是一些使用算术运算符的简单示例

julia> 1 + 2 + 3
6

julia> 1 - 2
-1

julia> 3*2/12
0.5

(按照惯例,如果运算符在其他附近的运算符之前应用,我们倾向于将运算符更紧密地间隔。例如,我们通常会写 -x + 2 来反映首先对 x 进行取反,然后将 2 添加到该结果。)

在乘法中使用时,false 充当强零

julia> NaN * false
0.0

julia> false * Inf
0.0

这对于防止已知为零的量中 NaN 值的传播很有用。有关动机,请参阅 Knuth (1992)

布尔运算符

以下 布尔运算符Bool 类型上受支持

表达式名称
!x否定
x && y短路与
x || y短路或

否定将 true 更改为 false,反之亦然。短路运算在链接页面上解释。

请注意,Bool 是一个整数类型,所有通常的提升规则和数字运算符也都在其上定义。

按位运算符

以下 按位运算符 在所有基本整数类型上都受支持

表达式名称
~x按位非
x & y按位与
x | y按位或
x ⊻ y按位异或(异或)
x ⊼ y按位与非(非与)
x ⊽ y按位或非(非或)
x >>> y逻辑右移
x >> y算术右移
x << y逻辑/算术左移

以下是一些使用按位运算符的示例

julia> ~123
-124

julia> 123 & 234
106

julia> 123 | 234
251

julia> 123 ⊻ 234
145

julia> xor(123, 234)
145

julia> nand(123, 123)
-124

julia> 123 ⊼ 123
-124

julia> nor(123, 124)
-128

julia> 123 ⊽ 124
-128

julia> ~UInt32(123)
0xffffff84

julia> ~UInt8(123)
0x84

更新运算符

每个二元算术和按位运算符也都有一个更新版本,该版本将运算的结果赋值回其左操作数。二元运算符的更新版本是通过在运算符后立即放置一个 = 来形成的。例如,写 x += 3 等效于写 x = x + 3

julia> x = 1
1

julia> x += 3
4

julia> x
4

所有二元算术和按位运算符的更新版本为

+=  -=  *=  /=  \=  ÷=  %=  ^=  &=  |=  ⊻=  >>>=  >>=  <<=
注意

更新运算符会重新绑定左侧的变量。因此,变量的类型可能会改变。

julia> x = 0x01; typeof(x)
UInt8

julia> x *= 2 # Same as x = x * 2
2

julia> typeof(x)
Int64

向量化的“点”运算符

对于每个^ 这样的二元运算,都有一个相应的“点”运算 .^,它被自动定义为对数组执行逐元素的 ^。例如,[1,2,3] ^ 3 未定义,因为对(非方阵)数组进行“立方”没有标准的数学意义,但 [1,2,3] .^ 3 被定义为计算逐元素(或“向量化”)结果 [1^3, 2^3, 3^3]。类似地,对于像 ! 这样的 一元运算符,有一个相应的 .√,它逐元素应用运算符。

julia> [1,2,3] .^ 3
3-element Vector{Int64}:
  1
  8
 27

更准确地说,a .^ b 被解析为 “点”调用 (^).(a,b),它执行 广播 运算:它可以组合数组和标量、相同大小的数组(逐元素执行运算),甚至不同形状的数组(例如,组合行向量和列向量以生成矩阵)。此外,与所有向量化的“点调用”一样,这些“点运算符”是融合的。例如,如果您计算 2 .* A.^2 .+ sin.(A)(或等效地 @. 2A^2 + sin(A),使用 @. 宏)对于数组 A,它执行对 A循环,计算 A 每个元素 a2a^2 + sin(a)。特别是,嵌套的点调用,如 f.(g.(x)),是融合的,“相邻”的二元运算符,如 x .+ 3 .* x.^2,等效于嵌套的点调用 (+).(x, (*).(3, (^).(x, 2)))

此外,“点式”更新运算符,如 a .+= b(或 @. a += b),被解析为 a .= a .+ b,其中 .= 是一个融合的就地赋值运算(请参阅 “点”语法文档)。

请注意,点语法也适用于用户定义的运算符。例如,如果您定义 ⊗(A,B) = kron(A,B),为 Kronecker 积提供方便的中缀语法 A ⊗ B (kron),则 [A,B] .⊗ [C,D] 将计算 [A⊗C, B⊗D],无需任何额外的编码。

将点运算符与数字字面量结合使用可能会造成歧义。例如,不清楚1.+x表示1. + x还是1 .+ x。因此,这种语法是不允许的,在这些情况下,运算符周围必须使用空格。

数字比较

所有原始数字类型都定义了标准比较操作

运算符名称
==相等
!=, 不相等
<小于
<=, 小于或等于
>大于
>=, 大于或等于

以下是一些简单的示例

julia> 1 == 1
true

julia> 1 == 2
false

julia> 1 != 2
true

julia> 1 == 1.0
true

julia> 1 < 2
true

julia> 1.0 > 3
false

julia> 1 >= 1.0
true

julia> -1 <= 1
true

julia> -1 <= -1
true

julia> -1 <= -2
false

julia> 3 < -0.5
false

整数按标准方式比较 - 通过比较位。浮点数根据IEEE 754 标准进行比较

  • 有限数按通常的方式排序。
  • 正零相等,但不大于负零。
  • Inf 等于自身,并且大于除 NaN 之外的所有其他内容。
  • -Inf 等于自身,并且小于除 NaN 之外的所有其他内容。
  • NaN 不等于、小于或大于任何事物,包括自身。

最后一点可能令人惊讶,因此值得注意

julia> NaN == NaN
false

julia> NaN != NaN
true

julia> NaN < NaN
false

julia> NaN > NaN
false

在使用数组时可能会导致头痛

julia> [1 NaN] == [1 NaN]
false

Julia 提供了额外的函数来测试数字的特殊值,这在哈希键比较等情况下很有用

函数测试是否
isequal(x, y)xy 相同
isfinite(x)x 是一个有限数
isinf(x)x 是无限的
isnan(x)x 不是一个数字

isequal 认为 NaN 相互相等

julia> isequal(NaN, NaN)
true

julia> isequal([1 NaN], [1 NaN])
true

julia> isequal(NaN, NaN32)
true

isequal 也可以用于区分有符号零

julia> -0.0 == 0.0
true

julia> isequal(-0.0, 0.0)
false

有符号整数、无符号整数和浮点数之间的混合类型比较可能很棘手。Julia 已经采取了大量措施来确保它们能够正确进行。

对于其他类型,isequal 默认调用 ==,因此,如果您想为自己的类型定义相等性,那么您只需要添加一个 == 方法。如果您定义了自己的相等性函数,那么您可能应该定义一个相应的 hash 方法来确保 isequal(x,y) 意味着 hash(x) == hash(y)

链接比较

与大多数语言不同,除了 Python 之外,比较可以任意链接

julia> 1 < 2 <= 2 < 3 == 3 > 2 >= 1 == 1 < 3 != 5
true

链接比较在数值代码中通常非常方便。链接比较对标量比较使用 && 运算符,对逐元素比较使用 & 运算符,这使它们能够在数组上工作。例如,0 .< A .< 1 会产生一个布尔数组,其条目在 A 的对应元素介于 0 和 1 之间时为真。

请注意链接比较的评估行为

julia> v(x) = (println(x); x)
v (generic function with 1 method)

julia> v(1) < v(2) <= v(3)
2
1
3
true

julia> v(1) > v(2) <= v(3)
2
1
false

中间表达式仅评估一次,而不是两次,如果表达式写成 v(1) < v(2) && v(2) <= v(3)。但是,链接比较中的评估顺序是未定义的。强烈建议不要在链接比较中使用带有副作用的表达式(如打印)。如果需要副作用,则应显式使用短路 && 运算符(参见 短路评估)。

基本函数

Julia 提供了全面的数学函数和运算符集合。这些数学运算定义在尽可能广泛的数值类型上,只要定义有意义,包括整数、浮点数、有理数和复数,无论这些定义是否合理。

此外,这些函数(就像任何 Julia 函数一样)可以以“向量化”方式应用于数组和其他集合,使用 点语法 f.(A),例如 sin.(A) 将计算数组 A 的每个元素的正弦。

运算符优先级和结合性

Julia 应用以下操作顺序和结合性,从最高优先级到最低优先级

类别运算符结合性
语法. 后跟 ::
指数^
一元+ - √[1]
位移<< >> >>>
分数//
乘法* / % & \ ÷[2]
加法+ - | ⊻[2]
语法: ..
语法|>
语法<|
比较> < >= <= == === != !== <:非结合性
控制流&& 后跟 || 后跟 ?
配对=>
赋值= += -= *= /= //= \= ^= ÷= %= |= &= ⊻= <<= >>= >>>=

有关所有 Julia 运算符优先级的完整列表,请参见此文件顶部:src/julia-parser.scm。请注意,其中一些运算符未在 Base 模块中定义,但可能会由标准库、包或用户代码给出定义。

您也可以通过内置函数 Base.operator_precedence 找到任何给定运算符的数值优先级,其中数字越大,优先级越高

julia> Base.operator_precedence(:+), Base.operator_precedence(:*), Base.operator_precedence(:.)
(11, 12, 17)

julia> Base.operator_precedence(:sin), Base.operator_precedence(:+=), Base.operator_precedence(:(=))  # (Note the necessary parens on `:(=)`)
(0, 1, 1)

也可以通过调用内置函数 Base.operator_associativity 找到表示运算符结合性的符号

julia> Base.operator_associativity(:-), Base.operator_associativity(:+), Base.operator_associativity(:^)
(:left, :none, :right)

julia> Base.operator_associativity(:⊗), Base.operator_associativity(:sin), Base.operator_associativity(:→)
(:left, :none, :right)

请注意,诸如 :sin 之类的符号返回优先级 0。此值表示无效运算符,而不是最低优先级的运算符。类似地,这些运算符被分配结合性 :none

数字字面量系数,例如 2x,被视为乘法,其优先级高于任何其他二元运算,除了 ^,在其中它们只有作为指数时才具有更高的优先级。

julia> x = 3; 2x^2
18

julia> x = 3; 2^2x
64

并置解析为一元运算符,它在指数周围具有相同的自然不对称:-x^y2x^y 解析为 -(x^y)2(x^y),而 x^-yx^2y 解析为 x^(-y)x^(2y)

数值转换

Julia 支持三种形式的数值转换,它们在处理不精确转换方面有所不同。

  • T(x)convert(T,x) 表示法将 x 转换为类型为 T 的值。

    • 如果 T 是一个浮点类型,则结果是最接近的可表示值,可以是正无穷大或负无穷大。
    • 如果 T 是一个整数类型,则如果 x 不能被 T 表示,则会引发 InexactError
  • x % T 将整数 x 转换为类型为 T 的整数类型值,该值与 x2^n 同余,其中 nT 中的位数。换句话说,二进制表示被截断以适合。

  • 舍入函数 以类型 T 作为可选参数。例如,round(Int,x)Int(round(x)) 的简写。

以下示例显示了不同的形式。

julia> Int8(127)
127

julia> Int8(128)
ERROR: InexactError: trunc(Int8, 128)
Stacktrace:
[...]

julia> Int8(127.0)
127

julia> Int8(3.14)
ERROR: InexactError: Int8(3.14)
Stacktrace:
[...]

julia> Int8(128.0)
ERROR: InexactError: Int8(128.0)
Stacktrace:
[...]

julia> 127 % Int8
127

julia> 128 % Int8
-128

julia> round(Int8,127.4)
127

julia> round(Int8,127.6)
ERROR: InexactError: trunc(Int8, 128.0)
Stacktrace:
[...]

有关如何定义自己的转换和提升的信息,请参见 转换和提升

舍入函数

函数描述返回类型
round(x)x 舍入到最接近的整数typeof(x)
round(T, x)x 舍入到最接近的整数T
floor(x)x 舍入到 -Inftypeof(x)
floor(T, x)x 舍入到 -InfT
ceil(x)x 舍入到 +Inftypeof(x)
ceil(T, x)x 舍入到 +InfT
trunc(x)x 舍入到零typeof(x)
trunc(T, x)x 舍入到零T

除法函数

函数描述
div(x,y), x÷y截断除法;商舍入到零
fld(x,y)向下取整除法;商舍入到 -Inf
cld(x,y)向上取整除法;商舍入到 +Inf
rem(x,y), x%y余数;满足 x == div(x,y)*y + rem(x,y);符号与 x 相同
mod(x,y)模数;满足 x == fld(x,y)*y + mod(x,y);符号与 y 相同
mod1(x,y)mod 偏移 1;对于 y>0 返回 r∈(0,y],对于 y<0 返回 r∈[y,0),其中 mod(r, y) == mod(x, y)
mod2pi(x)关于 2pi 的模数;0 <= mod2pi(x) < 2pi
divrem(x,y)返回 (div(x,y),rem(x,y))
fldmod(x,y)返回 (fld(x,y),mod(x,y))
gcd(x,y...)xy、... 的最大公约数
lcm(x,y...)xy、... 的最小公倍数

符号和绝对值函数

函数描述
abs(x)一个具有 x 大小的正值
abs2(x)x 的平方大小
sign(x)指示 x 的符号,返回 -1、0 或 +1
signbit(x)指示符号位是否打开(真)或关闭(假)
copysign(x,y)一个具有 x 大小和 y 符号的值
flipsign(x,y)一个具有 x 大小和 x*y 符号的值

幂、对数和根

函数描述
sqrt(x), √xx 的平方根
cbrt(x), ∛xx 的立方根
hypot(x,y)直角三角形的斜边,其他边的长度为 xy
exp(x)x 处的自然指数函数
expm1(x)对于接近零的 x,精确的 exp(x)-1
ldexp(x,n)x*2^n,对于 n 的整数值,以有效的方式计算
log(x)x 的自然对数
log(b,x)x 的以 b 为底的对数
log2(x)x 的以 2 为底的对数
log10(x)x 的以 10 为底的对数
log1p(x)对于接近零的 x,精确的 log(1+x)
exponent(x)x 的二进制指数
significand(x)浮点数 x 的二进制有效数(也称为尾数)

有关为什么像 hypotexpm1log1p 这样的函数是必要且有用的概述,请参阅 John D. Cook 关于此主题的出色博文对:expm1、log1p、erfchypot

三角函数和双曲函数

所有标准的三角函数和双曲函数也被定义。

sin    cos    tan    cot    sec    csc
sinh   cosh   tanh   coth   sech   csch
asin   acos   atan   acot   asec   acsc
asinh  acosh  atanh  acoth  asech  acsch
sinc   cosc

这些都是单参数函数,atan 也接受两个参数,对应于传统的 atan2 函数。

此外,还提供了 sinpi(x)cospi(x) 以更准确地计算 sin(pi*x)cos(pi*x)

为了使用度数而不是弧度计算三角函数,请在函数末尾添加 d。例如,sind(x) 计算 x 的正弦,其中 x 以度数指定。使用度数变体的三角函数的完整列表如下:

sind   cosd   tand   cotd   secd   cscd
asind  acosd  atand  acotd  asecd  acscd

特殊函数

SpecialFunctions.jl 提供了许多其他特殊数学函数。

  • 1一元运算符 +- 需要在其参数周围显式使用括号以区分它们与运算符 ++ 等。一元运算符的其他组合使用右结合性解析,例如,√√-a√(√(-a))
  • 2运算符 +++* 不是结合性的。a + b + c 被解析为 +(a, b, c) 而不是 +(+(a, b), c)。但是,+(a, b, c, d...)*(a, b, c, d...) 的回退方法都默认使用左结合性求值。