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位的机器上会溢出。
* (expt 2 200)1606938044258990275541962092341162602522202993782792835301376
考虑到效率因素,整数会被限制在修正的比特位内,叫做 修正数(fixnum)。可以查看相对应的变量来查看整数的大小范围
most-positive-fixnum4611686018427387903most-negative-fixnum-4611686018427387904
整型相对应的函数有:
[isqrt](http://clhs.lisp.se/Body/f_sqrt_.htm):返回最大的正根
(isqrt 10);; => 3(isqrt 4);; => 2
[gcd](http://clhs.lisp.se/Body/f_gcd.htm):返回最大公因数
(gcd 12 15);; => 3
[lcm](http://clhs.lisp.se/Body/f_lcm.htm#lcm):返回最小公倍数
(lcm 9 12);; 36
与其他的低级语言类似,Common Lisp 也提供了相对应的十六进制,以及从二进制到三十六进制的表示方法:
#xFF;; 255#2r1010;; 10#3r21;; 7#4r33;; 15#8r119#16rFF255#36rz35
有理数
有理数是由两个大数组成的[分数(ratio)](http://clhs.lisp.se/Body/t_ratio.htm)。所以可以表示任意大小的有理数:
(/ (1+ (expt 2 100) (expt 2 100)))1267650600228229401496703205377/1267650600228229401496703205376
其中 整型(integer) 是 有理数(rational) 类中的一个子类。
浮点数
浮点数试图使用有限的比特位来表示连续的实数。这就意味着大部分实数是近似的表示。这个特性会导致一些令人不快的意外,尤其是在内部十进制和二进制的转换上。如果你需要经常处理浮点数,建议阅读 What Every Computer Scientist Should Know About Floating-Point Arithmetic。
Common Lisp 标准中的浮点数有以下几种:
short-floatsingle-floatdouble-floatlong-float
用来获取浮点数的精度的:
short-float-epsilonsingle-float-epsilondouble-float-epsilonlong-float-epsilon
浮点数类型
浮点数的默认类型是由 *read-default-float-format* 来确定的,默认类型为 SINGLE-FLOAT,如果要将浮点数设为 double-float 型的,在数值后面加上 d0,其他类型的话分别为 s0 f0 d0 l0 e0(默认,single float):
(type-of 1.24)SINGLE-FLOAT(type-of 1.24d0)DOUBLE-FLOAT
当然,也可以通过修改 read-default-float-format* 来修改默认的浮点类型
(setq *read-default-float-format* 'double-float)(type-of 1.24)DOUBLE-FLOAT
有一点值得注意的,在整数后面加一个 . 的话,这个数依然被认为是整型,而不是浮点数。
type-of 10.)(INTEGER 0 4611686018427387903)(type-of 10.0)SINGLE-FLOAT
浮点数溢出处理
如果浮点数过大,将会导致溢出,在 SBCL 中,会出现如下错误:
(exp 1000); Evaluation aborted on #<FLOATING-POINT-OVERFLOW {10041720B3}>.
可以通过修改一些值,让溢出返回 +infinity,在 SBCL 中是这样的:
(sb-int:set-floating-point-modes :traps '(:INVALID :DIVIDE-BY-ZERO))(exp 1000)#.SB-EXT:SINGLE-FLOAT-POSITIVE-INFINITY(/ 1 (exp 1000))0.0
在 CCL 中:
(set-fpu-mode :overflow nil)
在 SBCL 中检查浮点数的配置:
(sb-int:get-floating-point-modes)(:TRAPS (:OVERFLOW :INVALID :DIVIDE-BY-ZERO) :ROUNDING-MODE :NEAREST :CURRENT-EXCEPTIONS NIL :ACCRUED-EXCEPTIONS NIL :FAST-MODE NIL)
任意精度
对于高精度的计算的话,需要使用 QuickLisp 中的 computable-reals 包
(ql:quickload :computable-reals)(use-package :computable-reals)(sqrt-r 2)+1.41421356237309504880...(sin-r (/r +pi-r+ 2))+1.00000000000000000000...
结果的精度由变量 *PRINT-PREC* 控制,默认是小数点后 20 位
(setq *PRINT-PREC* 50)(sqrt-r 2)+1.41421356237309504880168872420969807856967187537695...
复数
复数的实部和虚部必须要是同一类型——可以是有理数或是浮点数中的一种。
复数可由宏 #C 或函数 complex,其中宏 #C 创建是里面不能使用表达式
#C(1 1)#C(1 1)#C((+ 1 2) 5);; Evaluation aborted on #<TYPE-ERROR expected-type: REAL datum: (+1 2)>.(complex (+1 2) 5)#C(3 5)
当复数中都是浮点数时,默认会转换为精度更高的那个类型:
(type-of #C(1 1))(COMPLEX (INTEGER 1 1))(type-of #C(1.0 1))(COMPLEX (SINGLE-FLOAT 1.0 1.0))(type-of #C(1.0 1d0))(COMPLEX (DOUBLE-FLOAT 1.0d0 1.0d0))
使用 realpart 和 imagepart 获取复数的实部和虚部
(realpart #C(7 9))7(imagepart #C(4.2 9.5))9.5
复数运算
Common Lisp 中的数学运算函数也可以处理复数
(sqrt -1)#C(0.0 1.0)(exp #C(0.0 0.5))#C(0.87758255 0.47942555)(sin #C(1.0 1.0))#C(1.2984576 0.63496387)
字符串转换为数字
parse-integer, parse-float
(ql:quickload :parse-float)(use-package :parse-float)(parse-float "23.4e2" :type 'double-float)2340.0d06
近似值
ceiling, floor, round, truncate
* (ceiling 1.42)2-0.58000004* (floor 1.42)10.41999996* (round 1.42)10.41999996* (truncate 1.42)10.41999996
floor 与 truncate 的区别
* (truncate -1.42)-1-0.41999996* (floor -1.42)-20.58000004* (ceiling -1.42)-1-0.41999996
fceiling, ffloor, fround, ftruncate
* (ftruncate 1.3)1.00.29999995* (type-of (ftruncate 1.3))SINGLE-FLOAT* (type-of (ftruncate 1.3d0))DOUBLE-FLOAT
数字比较
=,常量 single-float-epsilon 是 = 处理的最小的数值。
* single-float-epsilon5.960465e-8* (= (+ 1s0 5e-8) 1s0)T* (= (+ 1s0 6e-8) 1s0)NIL
这并不表示 single-float 的精确范围是 0 到 6e-8
* (= (+ 10s0 4e-7) 10s0)T* (= (+ 10s0 5e-7) 10s0)NIL
这表示 single-float 大致是精确到小数点后 7 位。当在一连串的计算中,这个小错误最终会会合成一个很大的错误。如下面的例子:
* (< (abs (- (+ 10s0 5e-7)10s0))1s-6)T
= 可以比较不同类型的数据,可以将它和 eql 做比较
* (= 3 3.0)T* (eql 3 3.0)NIL
操作一系列数字
有很多处理惰性(无规则)数字序列的包/库:
处理罗马数字
format 中的 ~@r
* (format nil "~@r" 42)"XLII"
随机数的生成
random
* (random 10)7* (type-of (random 10))(INTEGER 0 4611686018427387903)* (type-of (random 10.0))SINGLE-FLOAT* (type-of (random 10d0))DOUBLE-FLOAT
随机数的种子(seed)存储在 *random-state 中,这个是有内部解释器单独实现的。函数make-random-state 可以用来生成或是拷贝新的随机态(random states)
* (dotimes (i 3)(let ((*random-state* (make-random-state nil)))(format t "~a~%"(loop for i from 0 below 10 collecting (random 10)))))(8 3 9 2 1 8 0 0 4 1)(8 3 9 2 1 8 0 0 4 1)(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 | b | c |
按位或 |
(lognot a) |
~a |
取非 |
(logxor a b c) |
a ^ b ^ c |
异或 |
(ash a 3) |
a << 3 |
左移位 |
(ash a -3) |
a >> 3 |
右移位 |
负数的位运算也是将其看作二进制进行运算的
* (logior 1 2 4 8)15;; Explanation:;; 0001;; 0010;; 0100;; | 1000;; -------;; 1111* (logand 2 -3 4)0;; Explanation:;; 0010 (2);; 1101 (two's complement of -3);; & 0100 (4);; -------;; 0000* (logxor 1 3 7 15)10;; Explanation:;; 0001;; 0011;; 0111;; ^ 1111;; -------;; 1010* (lognot -1)0;; Explanation:;; 11 -> 00* (lognot -3)2;; 101 -> 010* (ash 3 2)12;; Explanation:;; 11 -> 1100* (ash -5 -2)-2;; Explanation;; 11011 -> 110
更多关于位操作的函数见 CLHS page
