数学运算和初等函数

数学运算和初等函数

Julia 为它所有的基础数值类型,提供了整套的基础算术和位运算,也提供了一套高效、可移植的标准数学函数。

算术运算符

以下算术运算符支持所有的原始数值类型:

表达式名称描述
+x一元加法运算符全等操作
-x一元减法运算符将值变为其相反数
x + y二元加法运算符执行加法
x - y二元减法运算符执行减法
x * y乘法运算符执行乘法
x / y除法运算符执行除法
x ÷ y整除取 x / y 的整数部分
x \ y反向除法等价于 y / x
x ^ y幂操作符xy 次幂
x % y取余等价于 rem(x,y)

以及对 Bool 类型的否定:

表达式名称描述
!x否定truefalse 互换

Julia 的类型提升系统使得混合参数类型上的代数运算也能顺其自然的工作,请参考类型提升系统来了解更多内容。

这里是使用算术运算符的一些简单例子:

julia> 1 + 2 + 3
6

julia> 1 - 2
-1

julia> 3*2/12
0.5

习惯上我们会把优先运算的操作符紧邻操作数,比如 -x + 2 表示先要给 x 取反,然后再加 2

位运算符

所有原始整数类型都支持以下位运算符

表达式名称
~x按位取反
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> ~UInt32(123)
0xffffff84

julia> ~UInt8(123)
0x84

复合赋值操作符

每一个二元运算符和位运算符都可以给左操作数复合赋值:方法是把 = 直接放在二元运算符后面。比如,x += 3 等价于 x = x + 3

julia> x = 1
1

julia> x += 3
4

julia> x
4

二元运算和位运算的复合赋值操作符有下面几种:

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

复合赋值后会把变量重新绑定到左操作数上,所以变量的类型可能会改变。

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

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

julia> typeof(x)
Int64

向量化 dot 运算符

Julia 中,每个二元运算符都有一个 dot 运算符与之对应,例如 ^ 就有对应的 .^ 存在。这个对应的 .^ 被 Julia 自动地定义为逐元素地执行 ^ 运算。比如 [1,2,3] ^ 3 是非法的,因为数学上没有给(长宽不一样的)数组的立方下过定义。但是 [1,2,3] .^ 3 在 Julia 里是合法的,它会逐元素地执行 ^ 运算(或称向量化运算),得到 [1^3, 2^3, 3^3]。类似地,! 这样的一元运算符,也都有一个对应的 .√ 用于执行逐元素运算。

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

具体来说,a .^ b 被解析为 dot 调用 (^).(a,b),这会执行 broadcast 操作:该操作能结合数组和标量、相同大小的数组(元素之间的运算)、甚至不同形状的数组(例如行、列向量结合生成矩阵)。更进一步,就像所有向量化的 dot 调用一样,这些 dot 运算符是融合的(fused)。例如,在计算表达式 2 .* A.^2 .+ sin.(A) 时,Julia 只对 A 进行做一次循环,遍历 A 中的每个元素 a 并计算 2a^2 + sin(a)。上诉表达式也可以用@. 宏简写为 @. 2A^2 + sin(A)。特别的,类似 f.(g.(x)) 的嵌套 dot 调用也是融合的,并且“相邻的”二元运算符表达式 x .+ 3 .* x.^2 可以等价转换为嵌套 dot 调用:(+).(x, (*).(3, (^).(x, 2)))

除了 dot 运算符,我们还有 dot 复合赋值运算符,类似 a .+= b(或者 @. a += b)会被解析成 a .= a .+ b,这里的 .= 是一个融合的 in-place 运算,更多信息请查看 dot 文档)。

这个加点的语法,也能用在用户自定义的运算符上。For example, if you define ⊗(A,B) = kron(A,B) to give a convenient infix syntax A ⊗ B for Kronecker products (kron), then [A,B] .⊗ [C,D] will compute [A⊗C, B⊗D] with no additional coding.

将点运算符用于数值字面量可能会导致歧义。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 标准

NaN 的“不可比性”可能有点奇特,这里举例说明一下:

julia> NaN == NaN
false

julia> NaN != NaN
true

julia> NaN < NaN
false

julia> NaN > NaN
false

在做数组比较时,NaN 的存在,会使比较结果变得很奇怪:

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

为此,Julia 给这些 非有限数 提供了下面几个额外的测试函数。这些函数在有些情况下很有用处,比如在做 hash 比较时。

函数测试是否满足如下性质
isequal(x, y)xy 是完全相同的
isfinite(x)x 是有限大的数字
isinf(x)x 是(正/负)无穷大
isnan(x)xNaN

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)

链式比较

与其他多数语言不同,就像 notable exception of Python 一样,Julia 允许链式比较:

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

链式比较在数值代码中特别方便。链式比较使用 && 运算符比较标量,使用 & 比较元素类型, 也就是数组的元素。比如,0 .< A .< 1 得到一个 boolean 数组,如果 A 的元素都在 0 和 1 之间则数组元素就都是 true。

注意链式比较的执行顺序:

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) 是计算了两次的。然而,链式比较中的顺序是不确定的。强烈建议不要在表达式中使用有副作用(比如 printing)的函数。如果的确需要,请使用短路运算符 &&(参考 Short-Circuit Evaluation

初等函数

Julia 提供了强大的数学函数和运算符集合。这些数学运算定义在各种合理的数值上,包括整型、浮点数、分数、复数,只要有定义就行。

而且,这些函数(和其他 Julia 函数一样)能通过 点语法 f.(A) 以“向量式”方式用于数组和其他集合上。 比如,sin.(A) 会计算 A 中每个元素的 sin 值。

运算符的优先级与结合性

从高到低,Julia 运算符的优先级与结合性为:

分类运算符结合性
语法. followed by ::左结合
幂运算^右结合
一元运算符+ - √右结合[1]
移位运算<< >> >>>左结合
除法//左结合
乘法* / % & \ ÷左结合[2]
加法+ - | ⊻左结合[2]
语法: ..左结合
语法|>左结合
语法<|右结合
比较> < >= <= == === != !== <:无结合性
控制流程&& followed by || followed by ?右结合
Pair 操作=>右结合
赋值= += -= *= /= //= \= ^= ÷= %= |= &= ⊻= <<= >>= >>>=右结合
[1]

一元运算符 +- 需要给它们的参数显式使用括号以免和 ++等运算符混淆。其他一元运算符的混合使用都被解析为右结合的,比如√√-a解析为√(√(-a))`。

[2]

The operators +, ++ and * are non-associative. a + b + c is parsed as +(a, b, c) not +(+(a, b), c). However, the fallback methods for +(a, b, c, d...) and *(a, b, c, d...) both default to left-associative evaluation.

要看全部 Julia 运算符的优先级关系,可以看这个文件的最上面部分: src/julia-parser.scm

你也可以通过内置函数 Base.operator_precedence 查看任何给定运算符的数值优先级,数值越大优先级越高:

julia> Base.operator_precedence(:+), Base.operator_precedence(:*), Base.operator_precedence(:.)
(11, 13, 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

数值转换

Julia 支持三种数值转换,它们在处理不精确转换上有所不同。

下面的例子展示了不同的形式

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(Int8, 3.14)
Stacktrace:
[...]

julia> Int8(128.0)
ERROR: InexactError: Int8(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)round x towards +Inftypeof(x)
ceil(T, x)round x towards +InfT
trunc(x)round x towards zerotypeof(x)
trunc(T, x)round x towards zeroT

除法函数

函数描述
div(x,y), x÷y截断除法;商向零近似
fld(x,y)向下取整除法;商向 -Inf 近似
cld(x,y)向上取整除法;商向 +Inf 近似
rem(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)偏移 1 的 mod;若 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...)x, y,... 的最大公约数
lcm(x,y...)x, y,... 的最小公倍数

符号和绝对值函数

函数描述
abs(x)x 的模
abs2(x)x 的模的平方
sign(x)表示 x 的符号,返回 -1,0,或 +1
signbit(x)表示符号位是开启的(true)或关闭的(false)
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 接近 0 时的 exp(x)-1 的精确值
ldexp(x,n)x*2^n 的高效算法,n 为整数
log(x)x 的自然对数
log(b,x)b 为底 x 的对数
log2(x)以 2 为底 x 的对数
log10(x)以 10 为底 x 的对数
log1p(x)x接近 0 时的 log(1+x) 的精确值
exponent(x)binary exponent of x
significand(x)binary significand (a.k.a. mantissa) of a floating-point number x

For an overview of why functions like hypot, expm1, and log1p are necessary and useful, see John D. Cook's excellent pair of blog posts on the subject: expm1, log1p, erfc, and hypot.

三角和双曲函数

All the standard trigonometric and hyperbolic functions are also defined:

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

These are all single-argument functions, with atan also accepting two arguments corresponding to a traditional atan2 function.

Additionally, sinpi(x) and cospi(x) are provided for more accurate computations of sin(pi*x) and cos(pi*x) respectively.

In order to compute trigonometric functions with degrees instead of radians, suffix the function with d. For example, sind(x) computes the sine of x where x is specified in degrees. The complete list of trigonometric functions with degree variants is:

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

特殊函数

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