9.2 转换及取出 (Conversion and Extraction)

Lisp 提供四种不同类型的数字的转换及取出位数的函数。函数 float 将任何实数转换成浮点数:

  1. > (mapcar #'float '(1 2/3 .5))
  2. (1.0 0.6666667 0.5)

将数字转成整数未必需要转换,因为它可能牵涉到某些资讯的丧失。函数 truncate 返回任何实数的整数部分:

  1. > (truncate 1.3)
  2. 1
  3. 0.29999995

第二个返回值 0.29999995 是传入的参数减去第一个返回值。(会有 0.00000005 的误差是因为浮点数的计算本身就不精确。)

函数 floorceiling 以及 round 也从它们的参数中导出整数。使用 floor 返回小于等于其参数的最大整数,而 ceiling 返回大于或等于其参数的最小整数,我们可以将 mirror? (46 页,译注: 3.11 节)改成可以找出所有回文(palindromes)的版本:

  1. (defun palindrome? (x)
  2. (let ((mid (/ (length x) 2)))
  3. (equal (subseq x 0 (floor mid))
  4. (reverse (subseq x (ceiling mid))))))

truncate 一样, floorceiling 也返回传入参数与第一个返回值的差,作为第二个返回值。

  1. > (floor 1.5)
  2. 1
  3. 0.5

实际上,我们可以把 truncate 想成是这样定义的:

  1. (defun our-truncate (n)
  2. (if (> n 0)
  3. (floor n)
  4. (ceiling n)))

函数 round 返回最接近其参数的整数。当参数与两个整数的距离相等时, Common Lisp 和很多程序语言一样,不会往上取(round up)整数。而是取最近的偶数:

  1. > (mapcar #'round '(-2.5 -1.5 1.5 2.5))
  2. (-2 -2 2 2)

在某些数值应用中这是好事,因为舍入误差(rounding error)通常会互相抵消。但要是用户期望你的程序将某些值取整数时,你必须自己提供这个功能。 [1] 与其他的函数一样, round 返回传入参数与第一个返回值的差,作为第二个返回值。

函数 mod 仅返回 floor 返回的第二个返回值;而 rem 返回 truncate 返回的第二个返回值。我们在 94 页(译注: 5.7 节)曾使用 mod 来决定一个数是否可被另一个整除,以及 127 页(译注: 7.4 节)用来找出环状缓冲区(ring buffer)中,元素实际的位置。

关于实数,函数 signum 返回 10-1 ,取决于它的参数是正数、零或负数。函数 abs 返回其参数的绝对值。因此 (* (abs x) (signum x)) 等于 x

  1. > (mapcar #'signum '(-2 -0.0 0.0 0 .5 3))
  2. (-1 -0.0 0.0 0 1.0 1)

在某些应用里, -0.0 可能自成一格(in its own right),如上所示。实际上功能上几乎没有差别,因为数值 -0.00.0 有着一样的行为。

比值与复数概念上是两部分的结构。(译注:像 Cons 这样的两部分结构) 函数 numeratordenominator 返回比值或整数的分子与分母。(如果数字是整数,前者返回该数,而后者返回 1 。)函数 realpartimgpart 返回任何数字的实数与虚数部分。(如果数字不是复数,前者返回该数字,后者返回 0 。)

函数 random 接受一个整数或浮点数。这样形式的表达式 (random n) ,会返回一个大于等于 0 并小于 n 的数字,并有着与 n 相同的类型。