5.4 复数和有理数

5.4.1 复数

Julia 预定义的复数类型是Complex。它是Number的直接子类型。为了构造出复数的虚部,Julia 还专门定义了一个常量im。这里的 im 是 imaginary 的缩写。它使用起来是这样的:

  1. julia> 1 + 2im; typeof(1+2im)
  2. Complex{Int64}
  3. julia> 1.1 + 2.2im; typeof(1.1+2.2im)
  4. Complex{Float64}
  5. julia>

可以看到,Complex是一个参数化的类型。因为在其名称的右侧还有一个由花括号包裹的类型参数。这个类型参数会是一个代表了某个类型的标识符。关于参数化类型,我们在下下一章就会讲到。

为了使常见的数学公式和表达式更加清晰,Julia 允许在变量之前紧挨一个数值字面量,以表示两个数相乘。比如,如果变量x的值是整数8,那么2x^3就表示2乘以83次方。又比如,2^3x表示224次方。在这种情况下,变量x就被称为数值字面量系数(numeric literal coefficient)。

正因为如此,我们才需要特别注意,上例中的2im2.2im虽然看起来与这种表示法非常相似,但其含义却是完全不同的。整数或浮点数的字面量与常量im共同组成的是一个复数的虚部。而且还要注意,在构造复数的虚部时,我们就不能再使用数值字面量系数了。因为这肯定会产生歧义。比如,1 + 2xim就是不合法的,除非已经存在一个名为xim的变量,但如此一来这表示的就不是一个复数了。如果必须有变量参与复数的构造,那么我们可以使用complex函数,例如:complex(1, 2x)

Julia 允许复数参与标准的数学运算。所以,下面的这些数学表达式是合法的:

  1. julia> (1 + 2im) + (3 + 4im)
  2. 4 + 6im
  3. julia> (1 + 2im) - (3 + 4im)
  4. -2 - 2im
  5. julia> (1 + 2im) * (3 + 4im)
  6. -5 + 10im
  7. julia> (1 + 2im) / (3 + 4im)
  8. 0.44 + 0.08im
  9. julia> 3(1 + 2im)^8
  10. -1581 + 1008im
  11. julia>

例子中的圆括号代表着对运算次序的设定。这与它在数学中的一般含义是一致的。

要想分别得到一个复数的实部和虚部,我们就需要调用real函数和imag函数。示例如下:

  1. julia> com1 = 1 + 2im
  2. 1 + 2im
  3. julia> real(com1), imag(com1)
  4. (1, 2)
  5. julia>

另外,我们还可以利用conj函数求出一个复数的共轭(conjugate),以及使用abs函数计算出一个复数与0之间的距离,等等。总之,Julia 预定义的很多数学函数都可以应用于复数。

5.4.2 有理数

我们在前面说过,浮点数无法精确地表示所有小数。比如,1/3是一个无限循环小数,但用浮点数表示的话只能是这样的:

  1. julia> 1/3
  2. 0.3333333333333333
  3. julia> typeof(ans)
  4. Float64
  5. julia>

严格来说,1/3并不是一个浮点数。因为浮点数会对无限循环小数做舍入,这会损失精度。但是,它肯定是一个有理数。

在 Julia 中,有理数用于表示两个整数之间的精确比率。有理数的类型是Rational。它的值可以由操作符//来构造。代码如下:

  1. julia> 1//3
  2. 1//3
  3. julia> typeof(ans)
  4. Rational{Int64}
  5. julia>

在操作符//左侧的被称为分子,而在它右侧的被称为分母。注意,这两个数都只能是整数,而不能是浮点数。

如果在分子和分母之间存在公因数,那么 Julia 会自动地把它们化为最小项并让分母变为非负整数。例如:

  1. julia> 3//9
  2. 1//3
  3. julia> 3//-9
  4. -1//3
  5. julia> 42//126
  6. 1//3
  7. julia>

函数numeratordenominator可以让我们分别得到一个有理数的分子和分母:

  1. julia> rat1 = 1//3
  2. 1//3
  3. julia> numerator(rat1)
  4. 1
  5. julia> denominator(rat1)
  6. 3
  7. julia>

有理数可以参与标准的数学运算。比如,我们可以拿一个有理数与一个整数、浮点数或者其他有理数进行比较。又比如,我们可以对有理数进行加减乘数等运算。另外,有理数也可以很容易地被转换为浮点数。例如:

  1. julia> float(1//3)
  2. 0.3333333333333333
  3. julia>

我在前面也说了,这实际上会存在精度上的损失。

最后,需要我们注意的是,0//0是不合法的。该字面量会引发一个错误。相应的,表示浮点数的字面量0/0等同于NaN。从技术标准的角度讲,NaN不与任何东西(包括它自己)相等。