3.2 数值

Racket 数值 分为精确和不精确两种:

不精确数值的打印形式带有小数点或指数说明符,而精确数值则打印为整数和分数形式。 同样的约定也适用于读取数值常量,不过数值可通过前缀 #e#i 来分别强制解析为精确或不精确数值。前缀 #b#o#x 用于指示二进制、八进制和十六进制数字的解释。

+The Racket ReferenceReading Numbers一节中阐述了数值语法的要点。

例如:
> 0.5

0.5

> #e0.5

1/2

> #x03BB

955

涉及不精确数值的计算会产生不精确的结果,这种不精确性就像数值上的污点。 然而请注意,Racket 并未提供“不精确的布尔值”,因此对不精确数值进行比较的分支计算 仍然能产生精确的结果。过程 exact->inexactinexact->exact 可用于在这两种类型的数值间互相转换。

例如:
> (/ 1 2)

1/2

> (/ 1 2.0)

0.5

> (if (= 3.0 2.999) 1 2)

2

> (inexact->exact 0.1)

3602879701896397/36028797018963968

当试图将 sqrtlogsin 之类的过程产生的结果精确表示为非有理数的实数(即无理数、无穷值和非数值)时, 就会产生不精确的结果。Racket 只能将有理部分的结果表示为有理数和复数。

例如:
> (sin 0)   ; rational...

0

> (sin 1/2) ; not rational...

0.479425538604203

在性能方面,用小整数进行计算是最快的。此处“小”的程度指该数值的位长比机器 按字大小(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?) 则会在比较数值时同时考虑精确性和数值相等性。

例如:
> (= 1 1.0)

#t

> (eqv? 1 1.0)

#f

请注意涉及不精确数值的比较,根据它们的性质可能会有意料之外的行为。 即便显然很简单的不精确数值,其意思也可能与你所想的不同。例如,以 2 为底的 IEEE 可以精确地表示 1/2,但却只能近似地表示 1/10

例如:
> (= 1/2 0.5)

#t

> (= 1/10 0.1)

#f

> (inexact->exact 0.1)

3602879701896397/36028797018963968

+The Racket ReferenceNumbers一节中提供了关于数值与数值过程的更多信息。