4.4 比较运算符
数值类型对象之间的对比,也是常常遇到的问题。比较运算符便是对两个操作数的关系进行比较,在大于小于、是否相等或者是否完全相同等条件下给出逻辑判断,并返回一个布尔型的结果(Bool值):在给定条件为真时返回true,为假时则返回false值。因为涉及两个值,所以比较运算符都是二元的。Julia提供的比较运算符如表4-3所示。
表4-3 比较运算符
其中有些是非传统的Unicode符号,例如≠、≤及≥等,这些是在数学表述中常见的符号,但在编程语言中却是不常见的,这是Julia的特色。
1.大小比较
我们先看小于(<)及大于(>)这两个比较运算符,仍是从例子开始:
julia> a < b # Int64(50) < Int32(2) false julia> a > b # Int64(50) > Int32(2) true julia> c < d # Float32(2.5) < Float64(3.0) true julia> a > c # Int64(50) > Float32(2.5) false julia> a < y # Int64(50) < Rational{Int64}(15//8) false julia> c > y # Float32(2.5) > Rational{Int64}(15//8) true
可见,比较运算符也可以对不同的操作数类型进行操作。
不过需要注意的是,大小的比较对两个复数是不适用的,例如:
julia> 3+2im < 3+1im ERROR: MethodError: no method matching isless(::Complex{Int64}, ::Complex{Int64})
实际上,这种比较在数学上也是无意义的。
2.相等或相同
“小于”及“大于”这两种运算符是互斥的,不可能同时成立,但也有可能都不成立,因为还有第三种情况,即两者相等。我们可以使用等于(==)或者不等于(!=或≠)判断。
同样,“不等于”或“等于”这两者也是互斥的,不可能同时成立。例如:
julia> Int32(10) == Int64(10) # 类型不同 true julia> UInt8(5) == Int64(5) # 类型不同 true julia> 5 != 3 true julia> Int32(15) != Int8(15) false julia> Int32(1) == Float64(1.0) # 浮点型与整型对比时,可以获得正确的结论 true
可见,是否相等的比较会忽略值的具体类型,而直接对数值本身进行比较。
另外,与大小比较不同的是,是否相等对于有理数以及复数类型是适用的,例如:
julia> 15//8 == 3 false julia> 15//8 != 1.875 # 有理数15//8的实际值为1.875 false julia> 3+1im == 3+im true julia> 3+2im == 1.875 false julia> 15//8 != 3+2im true
必要时,如果判断是否相等的同时需要考虑值的类型,则可用===这个“完全相同”运算符,例如:
julia> Int32(10) === Int64(10) # 类型不同 false julia> Int32(15) === Int8(15) # 类型不同 false julia> 3+1im === 3+im # 类型与值均相同 true julia> Int32(10) === Int32(10) # 类型与值均相同 true
如例中所示,该运算符只有在两个操作数的值与类型均相同时,才会返回true值。
运算符!==或则与之相对,表示“不全相同”,值或类型不一致便会返回true值,例如:
julia> Int32(19) === Int32(19) # 类型与值均相同 true julia> Int32(19) !== Int32(19) # 类型与值均相同 false julia> Int32(19) !== Int64(19) # 类型不同 true julia> Int32(19) !== Int64(10) # 类型与值均不同 true
如果在开发中需要严格的比较,可以考虑使用该类运算符。
注意 事实上,是否相等及是否相同可以适用于两个操作数是任意类型的情况,即可以对任意两个对象使用这两类操作符,判断它们是否相同或相等。但大小比较并非如此,需要操作数是同类型,而且该类型在数学上要有意义才行。这点在开发中是需要注意的。
3.特殊值比较
上述的比较运算符对无穷值也是适用的,例如:
julia> Inf32 == Inf # 不同类型的正无穷是相等的 true julia> Inf == Inf true julia> Inf > Inf false julia> Inf < Inf false
但对于NaN这个特殊的浮点值,比较结果却相当特别,如下:
julia> NaN == NaN false julia> NaN > NaN false julia> NaN < NaN false julia> NaN != NaN true
无论是大于、小于还是等于均不能成立。
这一点可以从NaN的本质意义上去理解。NaN设立的目的是表达一种无效值,而无效值本质上并非是一个具体的值,所以无从进行比较。另外,在将Inf或普通数值与NaN进行比较时,也遵循这个原则,例如:
julia> Inf > NaN false julia> Inf == NaN false julia> Inf < NaN false julia> NaN != Inf true
实际上,在对无穷值与NaN在内的操作数进行比较时,Julia遵循IEEE 754标准[1]的约定:
·正无穷大等于自身,但大于除了NaN外的任何数值。
·负无穷大等于自身,但小于除了NaN外的任何数值。
·NaN不等于、不小于也不大于任何数,包括自身。
·正零等于但不大于负零。
但在实际使用中,往往希望将同为NaN的两个值作为同一事物进行处理,或者需要区分“正零”与“负零”,则可使用isequal()函数。通常情况下,该函数等效于==这个运算符,仅在值为NaN或零时会有所不同。例如:
julia> isequal(NaN, NaN) true julia> isequal(NaN, NaN32) true julia> -0.0 == 0.0 true julia> isequal(-0.0, 0.0) false
开发者可以根据应用的需求进行选择。
4.其他比较操作
运算符“约等于”≈和“不约等于”是Julia有特色的部分。两者的用途相反,用于判断两个操作数是否在默认的阈值下满足大约相等关系。例如:
julia> 0.1 ≈ (0.1 - 1e-10) # 两个操作数相差较小 true julia> 1.3 ≈ 1.300000002 #相差较小 true julia> 1.3 1.300002 #相差较大 true
这两个运算符在不需要精确对比的时候能够提供不少便利。
另外两个操作符,“大于等于”(>=或≥)以及“小于等于”(<=或≤),则可同时对上述的两种情况进行判断。但同样,这两类运算符不适用于复数。至于这两类运算符的用法,因与大小比较相似,不再赘述。
此外,对于多个操作数的比较,Julia还提供了链式比较表达式(Chaining Comparisons),例如:
julia> 1 < 2 <= 2 < 3 == 3 > 2 >= 1 == 1 < 3 != 5 true
不过由于可读性较差,笔者不建议使用,也不再过多介绍,有兴趣的读者可参见官方文档进一步了解。
[1] IEEE 754标准是二进位浮点数算术标准(IEEE Standard for Floating-Point Arithmetic)的编号,等同于国际标准ISO/IEC/IEEE 60559。该标准由美国电气电子工程师学会(IEEE)计算机学会旗下的微处理器标准委员会(Microprocessor Standards Committee, MSC)发布。