3.2 数值
精确数值包括
整数,大小任意。例如 5、99999999999999999 或 -17;
有理数,即两个任意大小整数的比值。例如 1/2、99999999999999999/2 或 -3/4;
复数,带有精确的实部和虚部(其中虚部非零)。例如 1+2i 或 1/2+3/4i。
不精确数值包括
IEEE 浮点表示的数值。例如 2.0 或 3.14e+87, 其中 IEEE 无穷值和非数值写作 +inf.0、-inf.0 和 +nan.0(或 -nan.0);
复数,带有 IEEE 浮点表示的实部和虚部。例如 2.0+3.0i 或 -inf.0+nan.0i。作为特例,不精确的复数可拥有精确为零的实部 和不精确的虚部。
不精确数值的打印形式带有小数点或指数说明符,而精确数值则打印为整数和分数形式。 同样的约定也适用于读取数值常量,不过数值可通过前缀 #e 或 #i 来分别强制解析为精确或不精确数值。前缀 #b、#o 和 #x 用于指示二进制、八进制和十六进制数字的解释。
The Racket Reference的Reading Numbers一节中阐述了数值语法的要点。
> 0.5 0.5
> #e0.5 1/2
> #x03BB 955
涉及不精确数值的计算会产生不精确的结果,这种不精确性就像数值上的污点。 然而请注意,Racket 并未提供“不精确的布尔值”,因此对不精确数值进行比较的分支计算 仍然能产生精确的结果。过程 exact->inexact 和 inexact->exact 可用于在这两种类型的数值间互相转换。
> (/ 1 2) 1/2
> (/ 1 2.0) 0.5
> (if (= 3.0 2.999) 1 2) 2
> (inexact->exact 0.1) 3602879701896397/36028797018963968
当试图将 sqrt、log 和 sin 之类的过程产生的结果精确表示为非有理数的实数(即无理数、无穷值和非数值)时, 就会产生不精确的结果。Racket 只能将有理部分的结果表示为有理数和复数。
在性能方面,用小整数进行计算是最快的。此处“小”的程度指该数值的位长比机器 按字大小(word-sized)来表示带符号整数的位长少一位。用非常大的精确整数 或非整数进行计算的代价要比用非精确数的代价更高。
(define (sigma f a b) (if (= a b) 0 (+ (f a) (sigma f (+ a 1) b))))
> (time (round (sigma (lambda (x) (/ 1 x)) 1 2000))) cpu time: 310 real time: 83 gc time: 0
8
> (time (round (sigma (lambda (x) (/ 1.0 x)) 1 2000))) cpu time: 1 real time: 1 gc time: 0
8.0
数值的分类按通常的方式定义为整数、有理数、实数 (总是有理数)和复数,它们可使用过程 integer?、rational?、real? 和 complex? 以及一个通用的 number? 来识别。有些数学过程只接受实数, 不过大部分都为复数实现了标准扩展。
> (integer? 5) #t
> (complex? 5) #t
> (integer? 5.0) #t
> (integer? 1+2i) #f
> (complex? 1+2i) #t
> (complex? 1.0+2.0i) #t
> (abs -5) 5
> (abs -5+2i) abs: contract violation
expected: real?
given: -5+2i
> (sin -5+2i) 3.6076607742131563+1.0288031496599335i
过程 = 用于比较数字的数值相等性。如果给定了不精确和精确的数进行比较, 它实际上会在比较前将不精确数转换为精确数。而 eqv?(以及 equal?) 则会在比较数值时同时考虑精确性和数值相等性。
请注意涉及不精确数值的比较,根据它们的性质可能会有意料之外的行为。 即便显然很简单的不精确数值,其意思也可能与你所想的不同。例如,以 2 为底的 IEEE 可以精确地表示 1/2,但却只能近似地表示 1/10:
> (= 1/2 0.5) #t
> (= 1/10 0.1) #f
> (inexact->exact 0.1) 3602879701896397/36028797018963968
The Racket Reference的Numbers一节中提供了关于数值与数值过程的更多信息。