Common Lisp 有着丰富的数字类型,其中包括整数、有理数、浮点数以及复数。

以下是相关的资料:

  • [Numbers](https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node16.html#SECTION00610000000000000000)
  • [Numbers, Characters and Strings](http://www.gigamonkeys.com/book/numbers-characters-and-strings.html)

概述

整数类型

Common Lisp 提供一个真正的整数类型,叫做 大数(bignum),这个值的大小只取决于内存的大小(不是机器的字大小)。例如,下面的例子在64位的机器上会溢出。

  1. * (expt 2 200)
  2. 1606938044258990275541962092341162602522202993782792835301376

考虑到效率因素,整数会被限制在修正的比特位内,叫做 修正数(fixnum)。可以查看相对应的变量来查看整数的大小范围

  1. most-positive-fixnum
  2. 4611686018427387903
  3. most-negative-fixnum
  4. -4611686018427387904

整型相对应的函数有:

  • [isqrt](http://clhs.lisp.se/Body/f_sqrt_.htm):返回最大的正根
  1. (isqrt 10)
  2. ;; => 3
  3. (isqrt 4)
  4. ;; => 2
  • [gcd](http://clhs.lisp.se/Body/f_gcd.htm):返回最大公因数
  1. (gcd 12 15)
  2. ;; => 3
  • [lcm](http://clhs.lisp.se/Body/f_lcm.htm#lcm):返回最小公倍数
  1. (lcm 9 12)
  2. ;; 36

与其他的低级语言类似,Common Lisp 也提供了相对应的十六进制,以及从二进制到三十六进制的表示方法:

  1. #xFF
  2. ;; 255
  3. #2r1010
  4. ;; 10
  5. #3r21
  6. ;; 7
  7. #4r33
  8. ;; 15
  9. #8r11
  10. 9
  11. #16rFF
  12. 255
  13. #36rz
  14. 35

有理数

有理数是由两个大数组成的[分数(ratio)](http://clhs.lisp.se/Body/t_ratio.htm)。所以可以表示任意大小的有理数:

  1. (/ (1+ (expt 2 100) (expt 2 100)))
  2. 1267650600228229401496703205377/1267650600228229401496703205376

其中 整型(integer)有理数(rational) 类中的一个子类。

浮点数

浮点数试图使用有限的比特位来表示连续的实数。这就意味着大部分实数是近似的表示。这个特性会导致一些令人不快的意外,尤其是在内部十进制和二进制的转换上。如果你需要经常处理浮点数,建议阅读 What Every Computer Scientist Should Know About Floating-Point Arithmetic

Common Lisp 标准中的浮点数有以下几种:

  • short-float
  • single-float
  • double-float
  • long-float

用来获取浮点数的精度的:

  • short-float-epsilon
  • single-float-epsilon
  • double-float-epsilon
  • long-float-epsilon

浮点数类型

浮点数的默认类型是由 *read-default-float-format* 来确定的,默认类型为 SINGLE-FLOAT,如果要将浮点数设为 double-float 型的,在数值后面加上 d0,其他类型的话分别为 s0 f0 d0 l0 e0(默认,single float):

  1. (type-of 1.24)
  2. SINGLE-FLOAT
  3. (type-of 1.24d0)
  4. DOUBLE-FLOAT

当然,也可以通过修改 read-default-float-format* 来修改默认的浮点类型

  1. (setq *read-default-float-format* 'double-float)
  2. (type-of 1.24)
  3. DOUBLE-FLOAT

有一点值得注意的,在整数后面加一个 . 的话,这个数依然被认为是整型,而不是浮点数。

  1. type-of 10.)
  2. (INTEGER 0 4611686018427387903)
  3. (type-of 10.0)
  4. SINGLE-FLOAT

浮点数溢出处理

如果浮点数过大,将会导致溢出,在 SBCL 中,会出现如下错误:

  1. (exp 1000)
  2. ; Evaluation aborted on #<FLOATING-POINT-OVERFLOW {10041720B3}>.

可以通过修改一些值,让溢出返回 +infinity,在 SBCL 中是这样的:

  1. (sb-int:set-floating-point-modes :traps '(:INVALID :DIVIDE-BY-ZERO))
  2. (exp 1000)
  3. #.SB-EXT:SINGLE-FLOAT-POSITIVE-INFINITY
  4. (/ 1 (exp 1000))
  5. 0.0

在 CCL 中:

  1. (set-fpu-mode :overflow nil)

在 SBCL 中检查浮点数的配置:

  1. (sb-int:get-floating-point-modes)
  2. (:TRAPS (:OVERFLOW :INVALID :DIVIDE-BY-ZERO) :ROUNDING-MODE :NEAREST :CURRENT-EXCEPTIONS NIL :ACCRUED-EXCEPTIONS NIL :FAST-MODE NIL)

任意精度

对于高精度的计算的话,需要使用 QuickLisp 中的 computable-reals

  1. (ql:quickload :computable-reals)
  2. (use-package :computable-reals)
  3. (sqrt-r 2)
  4. +1.41421356237309504880...
  5. (sin-r (/r +pi-r+ 2))
  6. +1.00000000000000000000...

结果的精度由变量 *PRINT-PREC* 控制,默认是小数点后 20 位

  1. (setq *PRINT-PREC* 50)
  2. (sqrt-r 2)
  3. +1.41421356237309504880168872420969807856967187537695...

复数

复数的实部和虚部必须要是同一类型——可以是有理数或是浮点数中的一种。

复数可由宏 #C 或函数 complex,其中宏 #C 创建是里面不能使用表达式

  1. #C(1 1)
  2. #C(1 1)
  3. #C((+ 1 2) 5)
  4. ;; Evaluation aborted on #<TYPE-ERROR expected-type: REAL datum: (+1 2)>.
  5. (complex (+1 2) 5)
  6. #C(3 5)

当复数中都是浮点数时,默认会转换为精度更高的那个类型:

  1. (type-of #C(1 1))
  2. (COMPLEX (INTEGER 1 1))
  3. (type-of #C(1.0 1))
  4. (COMPLEX (SINGLE-FLOAT 1.0 1.0))
  5. (type-of #C(1.0 1d0))
  6. (COMPLEX (DOUBLE-FLOAT 1.0d0 1.0d0))

使用 realpartimagepart 获取复数的实部和虚部

  1. (realpart #C(7 9))
  2. 7
  3. (imagepart #C(4.2 9.5))
  4. 9.5

复数运算

Common Lisp 中的数学运算函数也可以处理复数

  1. (sqrt -1)
  2. #C(0.0 1.0)
  3. (exp #C(0.0 0.5))
  4. #C(0.87758255 0.47942555)
  5. (sin #C(1.0 1.0))
  6. #C(1.2984576 0.63496387)

字符串转换为数字

parse-integer, parse-float

  1. (ql:quickload :parse-float)
  2. (use-package :parse-float)
  3. (parse-float "23.4e2" :type 'double-float)
  4. 2340.0d0
  5. 6

近似值

ceiling, floor, round, truncate

  1. * (ceiling 1.42)
  2. 2
  3. -0.58000004
  4. * (floor 1.42)
  5. 1
  6. 0.41999996
  7. * (round 1.42)
  8. 1
  9. 0.41999996
  10. * (truncate 1.42)
  11. 1
  12. 0.41999996

floortruncate 的区别

  1. * (truncate -1.42)
  2. -1
  3. -0.41999996
  4. * (floor -1.42)
  5. -2
  6. 0.58000004
  7. * (ceiling -1.42)
  8. -1
  9. -0.41999996

fceiling, ffloor, fround, ftruncate

  1. * (ftruncate 1.3)
  2. 1.0
  3. 0.29999995
  4. * (type-of (ftruncate 1.3))
  5. SINGLE-FLOAT
  6. * (type-of (ftruncate 1.3d0))
  7. DOUBLE-FLOAT

数字比较

=,常量 single-float-epsilon= 处理的最小的数值。

  1. * single-float-epsilon
  2. 5.960465e-8
  3. * (= (+ 1s0 5e-8) 1s0)
  4. T
  5. * (= (+ 1s0 6e-8) 1s0)
  6. NIL

这并不表示 single-float 的精确范围是 0 到 6e-8

  1. * (= (+ 10s0 4e-7) 10s0)
  2. T
  3. * (= (+ 10s0 5e-7) 10s0)
  4. NIL

这表示 single-float 大致是精确到小数点后 7 位。当在一连串的计算中,这个小错误最终会会合成一个很大的错误。如下面的例子:

  1. * (< (abs (- (+ 10s0 5e-7)
  2. 10s0))
  3. 1s-6)
  4. T

= 可以比较不同类型的数据,可以将它和 eql 做比较

  1. * (= 3 3.0)
  2. T
  3. * (eql 3 3.0)
  4. NIL

操作一系列数字

有很多处理惰性(无规则)数字序列的包/库:

处理罗马数字

format 中的 ~@r

  1. * (format nil "~@r" 42)
  2. "XLII"

随机数的生成

random

  1. * (random 10)
  2. 7
  3. * (type-of (random 10))
  4. (INTEGER 0 4611686018427387903)
  5. * (type-of (random 10.0))
  6. SINGLE-FLOAT
  7. * (type-of (random 10d0))
  8. DOUBLE-FLOAT

随机数的种子(seed)存储在 *random-state 中,这个是有内部解释器单独实现的。函数make-random-state 可以用来生成或是拷贝新的随机态(random states)

  1. * (dotimes (i 3)
  2. (let ((*random-state* (make-random-state nil)))
  3. (format t "~a~%"
  4. (loop for i from 0 below 10 collecting (random 10)))))
  5. (8 3 9 2 1 8 0 0 4 1)
  6. (8 3 9 2 1 8 0 0 4 1)
  7. (8 3 9 2 1 8 0 0 4 1)

比特位操作

Common Lisp 中也提供了位运算,下表是与 C/C++ 相对应的函数:

Common Lisp C/C++ 描述
(logand a b c) a & b & c 按位与
(logior a b c) a &#124; b &#124; c 按位或
(lognot a) ~a 取非
(logxor a b c) a ^ b ^ c 异或
(ash a 3) a << 3 左移位
(ash a -3) a >> 3 右移位

负数的位运算也是将其看作二进制进行运算的

  1. * (logior 1 2 4 8)
  2. 15
  3. ;; Explanation:
  4. ;; 0001
  5. ;; 0010
  6. ;; 0100
  7. ;; | 1000
  8. ;; -------
  9. ;; 1111
  10. * (logand 2 -3 4)
  11. 0
  12. ;; Explanation:
  13. ;; 0010 (2)
  14. ;; 1101 (two's complement of -3)
  15. ;; & 0100 (4)
  16. ;; -------
  17. ;; 0000
  18. * (logxor 1 3 7 15)
  19. 10
  20. ;; Explanation:
  21. ;; 0001
  22. ;; 0011
  23. ;; 0111
  24. ;; ^ 1111
  25. ;; -------
  26. ;; 1010
  27. * (lognot -1)
  28. 0
  29. ;; Explanation:
  30. ;; 11 -> 00
  31. * (lognot -3)
  32. 2
  33. ;; 101 -> 010
  34. * (ash 3 2)
  35. 12
  36. ;; Explanation:
  37. ;; 11 -> 1100
  38. * (ash -5 -2)
  39. -2
  40. ;; Explanation
  41. ;; 11011 -> 110

更多关于位操作的函数见 CLHS page