Skip to content

Latest commit

 

History

History
645 lines (490 loc) · 16.9 KB

算术操作符和基本函数.md

File metadata and controls

645 lines (490 loc) · 16.9 KB

Julia给全部原始数值类型提供了完整的基本算术和位操作集,也提供了可移植的、高效的标准数学函数综合集。

算术操作

下面列出Julia全部原始数值类型都支持的算术操作

表达式 名称 描述
+x 一元加 恒等算子(the identity operation)
-x 一元减 将数值映射为加的逆运算
x + y 二元加 执行加法
x - y 二元减 执行减法
x * y 执行乘法
x / y 执行除法
x ÷ y 整除 相当于把x/y的结果截取整数部分
x \ y 反转除 等价于y/x
x ^ y 将x自乘y次
x % y 求余 等价于rem(x,y)

还有Bool类型的取反操作。

表达式 名称 描述
!x 就是true和false的相互逆操作

插一句:

# 尽管Bool假数值上等于整型0
julia> false == 0
true

# 但是Bool类型的取反操作只能应用在该类型
julia> !0
ERROR: MethodError: no method matching !(::Int64)
Closest candidates are:
  !(::Missing) at missing.jl:79
  !(::Bool) at bool.jl:35
  !(::Function) at operators.jl:853
Stacktrace:
 [1] top-level scope at none:0

Julia的类型【转换和提升系统】使得混合类型参数算术操作自然自动地进行。

体会下面简单示例:

julia> 1 + 2 + 3
6

julia> 1 - 2
-1

julia> 3*2/12
0.5

按照惯例,猪吏偏好将单目运算符更贴近被操作标识符,比如:

julia> x = 9527
9527

# 一元减(-)紧挨x
# 二元加(+)两侧有空格
# 明确表示先给x取负然后和1314相加
julia> -x + 1314
-8213

位操作

下面列出的位操作,Julia的全部原始整型类型都支持。

表达式 名称
~x 按位取反
x & y 按位与
x y
x ⊻ y 按位异或
x >>> y 逻辑右移
x >> y 算术右移
x << y 逻辑或算术左移(若溢出则按逻辑左移

体会下面简单示例:

julia> ~123
-124

julia> 0b1010 & 0b0101
0x00

julia> 0b1010 | 0b0101
0x0f

julia> 0b1010 ⊻ 0b0101
0x0f

julia> 0b1010 ⊻ 0b1010
0x00

julia> xor(0b1010, 0b0101)
0x0f

julia> xor(0b1010, 0b1010)
0x00

julia> x = UInt8(64)
0x40

julia> bitstring(x)
"01000000"

julia> bitstring(x >>> 2)
"00010000"

julia> bitstring(x >> 2)
"00010000"

julia> bitstring(x << 1)
"10000000"

julia> bitstring(x << 2)
"00000000"

julia> y = Int8(-64)
-64

julia> bitstring(y)
"11000000"

julia> bitstring(y >>> 2)
"00110000"

julia> bitstring(y >> 2)
"11110000"

julia> bitstring(y << 1)
"10000000"

# 负数做逻辑或算术左移可能成为正数
# 这和Python的实现不同
julia> bitstring(y << 2)
"00000000"

julia> y << 2
0

更新操作

每个二元算术操作和位操作都有“更新版本”操作,即将操作结果返回给左边的被操作标识符; 各自对应的更新操作就是在二元算术操作或位操作之后紧跟“=”,举栗子:

julia> x = 1
1

julia> x += 3
4

# 位操作交换两个变量的值
julia> x = 0b10101111
0xaf

julia> y = 0b11110101
0xf5

julia> x ⊻= y
0x5a

julia> y ⊻= x
0xaf

julia> x ⊻= y
0xf5

全部二元算术操作和位操作的更新版本操作是:【+= -= *= /= \= ÷= %= ^= &= |= ⊻= >>>= >>= <<=】。

要注意的是:更新操作重新绑定左侧被操作标识符,可能导致变量类型变化

julia> x = 0x01
0x01

julia> typeof(x)
UInt8

julia> x *= 2.0
2.0

julia> typeof(x)
Float64

向量化点操作

全部二元操作和单目操作,都有对应的点操作。 不磨叽,直接上示例代码。

julia> [0,1,2,3,4] .^ 2
5-element Array{Int64,1}:
  0
  1
  4
  9
 16

julia> .√[0,1,2,3,4]
5-element Array{Float64,1}:
 0.0
 1.0
 1.4142135623730951
 1.7320508075688772
 2.0

julia> .! [true,false,false,true,true]
5-element BitArray{1}:
 false
  true
  true
 false
 false

julia> .- [true,false,false,true,true]
5-element Array{Int64,1}:
 -1
  0
  0
 -1
 -1

julia> .+ [true,false,false,true,true]
5-element Array{Int64,1}:
 1
 0
 0
 1
 1

更具体地讲(more specifically),“a .^ b”解析为【点调用幂】,这会执行【广播操作】:能够结合数组和标量,数组的维度相同(执行矩阵元素操作)、数组的形状不同(行列向量联合生成一个矩阵)。

此外,像所有向量化“点调用”一样,这些点操作是融合的:例如计算数组A的“2 .* A.^2 .+ sin.(A)”(或等价的“@. 2A^2 + sin(A)——运用‘@.’宏”),将在A上执行单循环、计算A的每个元素“2a^2 + sin(a)”。特别是嵌套点调用(f.(g.(x)))是混合的,且临近的二元操作如“x .+ 3 .* x.^2”等价于嵌套点调用“(+).(xm (*).(3, (^).(x, 2)))”。

而且,点更新操作,如“a .+ b”解析为“a .= a .+ b”,这里的“.=”混合内联(in-place)赋值操作,具体参考【函数】。

注意,点操作语法也适用于群众自定义的操作,举个栗子,如果秀才定义“⊗(A,B) = kron(A,B)”为【克罗内克积(kron)】的便捷中缀(别名),则有“[A,B] .⊗ [C,D]”等于“[A⊗C, B⊗D]”,而不需要额外代码。

点操作结合数值字面则会引起歧义。 例如“1.+x”就搞不清是“1. + x”还是“1 .+ x”,因此不允许这种语法,必须在操作符两侧根据实际意图留下空白。

julia> x = 2
2

julia> 1 .+ x
3

julia> 1. + x
3.0

数值比较

Julia的所有原始数值类型都支持标准比较操作:

操作 名称
== 等于
!=或≠ 不等于
< 小于
<=或≤ 小于等于
> 大于
>=或≥ 大于等于

体会下面简单示例:

julia> 9527 == 1314
false

julia> 9527 != 1314
true

julia> 9527 ≠ 1314
true

julia> 9527 > 1314
true

julia> 9527 ≥ 1314
true

julia> 9527 < 1314
false

julia> 9527 ≤ 1314
false

julia> 1.0 == 1
true

julia> +0 == -0
true

整型数值比较按照习惯姿势(比较比特)。

浮点数值比较根据IEEE 754标准:

  • 有限数字按惯例排序;
  • 正零等于负零,并不是正零就比负零大;
  • Inf和自身相等,大于除NaN之外的任意数字;
  • -Inf和自身相等,小于除NaN之外的任意数字;
  • NaN不等于、不小于、不大于任何数字,包括本身!

最后一点说点可能令人惊奇的,但没意义的东西:

julia> NaN == NaN
false

# NaN不等于本身
julia> NaN != NaN
true

julia> NaN < NaN
false

julia> NaN > NaN
false

数组中出现NaN会引起特殊的头痛:

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

对于特殊数值,Julia提供了别的函数来测试,在hash键比较中很有用:

函数 测试
isequal(x, y) x和y相等否
isfinite(x) x有限否
ifinf(x) x无限否
isnan(x) x非数字否
  • isequal认为NaN和自身相等
julia> isequal(NaN, NaN)
true

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

julia> isequal(NaN, NaN32)
true
  • isequal还可以区分正负零
julia> isequal(+0.0, -0.0)
false

julia> +0.0 == -0.0
true

有符号整型、无符号整型、浮点类型的混合类型比较必须留心(tricky)。 Julia小心呵护以保证正确运算。

别的类型,isequal默认调用“==”操作,因此群众想自定义相等操作,只需要添加“==”方法。 如果秀才自定义自己的相等函数,或许应该同时自定义相应hash方法以确保isequal(x, y)暗含“hash(x) == hash(y)”。

链式比较

不同于大多数编程语言(注意Python除外),比较操作可以果断链接。

julia> 0 < 1 <= 2 == 2 > 1.0 == 1 > 0.0 != 9527
true

链式比较通常在数值代码中十分便捷。 链式比较采用“&&”操作符链接标量比较、采用“&”操作符链接矩阵元素比较(允许作用于数组)。 举例说明:“0 .< A .< 1”返回一个Bool元素组成的数组,某位置的元素是true说明A中对应位置的元素介于0和1之间。

注意链式比较的求值做法:

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

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

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

中间的表达式仅计算一次,而不是像写成“v(1) < v(2) && v(2) <= v(3)”这样导致中间表达式计算两次。

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

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

然而,链式比较中表达式的求值顺序并没有定义。

强烈推荐不要在链式比较中用有副作用的表达式(如打印等);如果副作用表达式是必须的,【短路操作符(&&)】应当明确给出。

基本函数

猪吏提供了广泛的数学函数和操作符集。 这些数学操作符横跨众多允许合理定义的数值类,包括整型、浮点类型、有理数、复数,只要这些定义有意义。 这些数学函数,和任何别的Julia函数一样,可以应用向量化样式到数组和别的集合,就是【点语法】,例如“sin.(A)”将计算数组A中每个元素的正弦值。

操作符的优先级和结合性

下表优先级从高到低地列出Julia操作符的结合性:

类别 操作符 结合性
语法 紧跟双冒号(::)的点(.)
指数 ^
单目(一元) + - √ 右:“+”和“-”必须明确用圆括号包括参数以消除“++”和“--”等情况;“√”总是解析为给右边参数开方(“√√-a”理解为“√(√(-a))”)
位移 << >> >>>
分数 //
泛乘 * / % & \ ÷ 左:“+”、“++”和“”是非结合的,即“a + b + c”解析为“+(a, b, c)”而非“+(+(a, b), c)”;然而“+(a, b, c, d...)”和“(a, b, c, d...)”的回降方法(fallback method)都是左结合的
泛加 + - | ⊻ 左:同上
语法 : ..
语法 |>
语法 <|
比较 > < >= <= == === != ≠ !== <: 非结合的
控制流 && 紧跟 || 紧跟 ?
组对 =>
赋值 = += -= 8= /= //= \= ^= ÷= %= |= &= ⊻= <<= >>= >>>=

欲知Julia完整操作符的数值优先级,查看“src/julia-parser.scm”文件。

  • 可以通过内建的Base.operator_precedence函数获取任何给定操作符的数值优先级,返回的数字越大越优先。
julia> Base.operator_precedence(:+)
11

julia> Base.operator_precedence(:*)
13

julia> Base.operator_precedence(:.)
17

julia> Base.operator_precedence(:+=)
1

julia> Base.operator_precedence(:(=))
1

julia> Base.operator_precedence(:sin)
0

# 并非所有函数都可以通过Base.operator_precedence函数获取数值优先级
julia> Base.operator_precedence(:Base.operator_precedence)
ERROR: type Symbol has no field operator_precedence
Stacktrace:
 [1] getproperty(::Any, ::Symbol) at .\sysimg.jl:18
 [2] top-level scope at none:0
  • 可以通过内建的Base.operator_associativity函数获取操作符结合性的符号表示。
julia> Base.operator_associativity(:->)
:none

julia> Base.operator_associativity(:⊗)
:left

julia> Base.operator_associativity(:sin)
:none

# 并非所有函数都可以通过Base.operator_associativity函数获取结合性
julia> Base.operator_associativity(:Base.operator_associativity)
ERROR: type Symbol has no field operator_associativity
Stacktrace:
 [1] getproperty(::Any, ::Symbol) at .\sysimg.jl:18
 [2] top-level scope at none:0

注意:像“sin”这种符号返回的0表示无效操作符,并非最低优先级的操作符。同理,这类操作符的结合性是“none”

数值转换

Julia支持三种数值转换的形式,不同之处在于处理精确转换的细节。

  • 注解“T(x)”或“convert(T, x)”将x转换为类型T。
    • 如果是浮点类型,结果是最邻近可表达数值,可能是正无穷或负无穷;
    • 如果是整型,如果类型T无法表达x则抛出InexactError错误。
  • “x % T”将整型x转换为类型T全等于(congruent to)x以2^n取模,其中n是T的比特数;换句话说,二进制截断适配。
  • 【舍入函数】把类型T当作可选参数,例如round(Int, x)是Int(round(x))的速记法。

下面给出不同形式的样例:

julia> Int8(127)
127

julia> Int8(128)
ERROR: InexactError: trunc(Int8, 128)
Stacktrace:
 [1] throw_inexacterror(::Symbol, ::Any, ::Int64) at .\boot.jl:567
 [2] checked_trunc_sint at .\boot.jl:589 [inlined]
 [3] toInt8 at .\boot.jl:604 [inlined]
 [4] Int8(::Int64) at .\boot.jl:714
 [5] top-level scope at none:0

# 浮点数通过IntN强转小数部分必须为零
julia> Int8(127.0)
127

julia> Int8(95.27)
ERROR: InexactError: Int8(Int8, 95.27)
Stacktrace:
 [1] Int8(::Float64) at .\float.jl:679
 [2] top-level scope at none:0

julia> 127 % Int8
127

julia> 128 % Int8
-128

julia> 129 % Int8
-127

julia> round(Int8, 95.27)
95

julia> round(Int8, 128)
ERROR: InexactError: trunc(Int8, 128)
Stacktrace:
 [1] throw_inexacterror(::Symbol, ::Any, ::Int64) at .\boot.jl:567
 [2] checked_trunc_sint at .\boot.jl:589 [inlined]
 [3] toInt8 at .\boot.jl:604 [inlined]
 [4] Type at .\boot.jl:714 [inlined]
 [5] convert at .\number.jl:7 [inlined]
 [6] round(::Type{Int8}, ::Int64) at .\int.jl:549
 [7] top-level scope at none:0

查看【转换和提升】了解如何定义客户化转换和提升。

舍入函数

函数 描述 返回类型
round(x) 取x的最邻近整数 typeof(x)
round(T, x) 取x的最邻近整数 T
floor(x) 朝无穷小方向取整 typeof(x)
floor(T, x) 朝无穷小方向取整 T
ceil(x) 朝无穷大方向取整 typeof(x)
ceil(T, x) 朝无穷大方向取整 T
trunc(x) 朝最小绝对值方向取整 typeof(x)
trunc(T, x) 朝最小绝对值方向取整 T

除法

函数 描述
div(x, y) x÷y 截断除法,商向零取整。
fld(x, y) 地板除法,商向无穷小取整。
cld(x, y) 进一除法,商向无穷大取整。
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) 带偏置一取模,返回:r ∈ (0, y],y>0;r ∈ [y, 0),y<0,mod(r, y) == mod(x, y)
mod2pi(x) 按2pi取模,即“mod2pi(x) ∈ [0, 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) 绝对值
abs2(x) 绝对值平方
sign(x) 表示x的符号(“-1”、“0”或“+1”)
signbit(x) 表示符号位开启(true)或关闭(false)
copysign(x, y) 产生符号同y值同x的值
flipsign(x, y) 产生符号同x*y值同x的值

幂对及根

函数 描述
sqrt(x) √x 开平方
cbrt(x) ∛x 开立方
hypot(x, y) 计算直角边长度分别是x和y的斜边长
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的二进制有效数字(mantissa: 尾数)

为何像hypot、expm1、log1p等函数必要且有用,阅读John D Cook关于这些主题的卓著博客

三角函数和双曲线函数

全部标准三角函数和双曲线函数也已备好:

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

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

此外,sinpi(x)和cospi(x)分别提供了sin(pix)和cos(pix)的更精确计算。

为了用角度替代弧度计算三角函数,给函数名后转“d”即可。 例如sind(x)就是计算已角度为单位的x的正弦值。 这里给出全部以角度为单位的三角函数:

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

特殊函数

其余需多特殊数学函数由SpecialFunctions.jl包提供。