浮点加法和乘法是交换的,但不是结合的。这意味着
(+ (+ a b) c)
可能不等于
(+ a (+ b c)
,由于错误传播。正如scar Lpez提到的alredy,参见
What Every Computer Scientist Should Know About Floating-Point Arithmetic
.
我用Common Lisp重写了你的例子,我可以观察到同样的行为。
然后我通过插入反引号和逗号稍微修改了代码,这样函数就可以返回一棵树而不是一个数字。看见
Macros: defining your own
如果您不熟悉synax。
这棵树是由数字和符号组成的(
*
,
+
, ...) 并对应于每个版本中所做的操作。
这应该有助于理解如何以及何时计算中间结果。
递归求和:
(defun sum (term a next b)
(labels ((it (a)
(if (> a b)
0
`(+ ,(it (funcall next a))
,(funcall term a)))))
(it a)))
尾部递归和:
(defun sum-it (term a next b)
(labels ((it (a result)
(if (> a b)
result
(it (funcall next a)
`(+ ,(funcall term a) ,result)))))
(it a 0)))
递归积分:
(defun integral (f a b dx)
(flet ((add-dx (x) (+ x dx)))
`(* ,(sum f (+ a (/ dx 2.0d0)) #'add-dx b) ,dx)))
尾部递归积分:
(defun integral-it (f a b dx)
(flet ((add-dx (x) (+ x dx)))
`(* ,(sum-it f (+ a (/ dx 2.0d0)) #'add-dx b) ,dx)))
和立方体:
(defun cube (x) (* x x x))
测试时要小心
:由于target precision参数太精确,您将得到一个很大的结果。
您可以看到,这两种方法最终以不同的顺序计算结果:
CL-USER> (integral #'cube 0 1 0.4d0)
(* (+ (+ (+ 0 1.0d0) 0.21600000000000008d0) 0.008000000000000002d0) 0.4d0)
相比之下:
CL-USER> (integral-it #'cube 0 1 0.4d0)
(* (+ 1.0d0 (+ 0.21600000000000008d0 (+ 0.008000000000000002d0 0))) 0.4d0)
在上面的例子中,结果没有差别,但是对于一些输入,比如你找到的输入,结果会有差别。