SICPを読む(42) 問題 2.10 ゼロ除算の検出
ゼロ除算を検出せよという問題。
簡単だったので、ちょっと工夫してみた。
0 * x = 0なので、乗算でのゼロを検出できる。
(define (div-interval x y) (if (= 0 (* (lower-bound y) (upper-bound y))) (error "div-interval : zero division error") (mul-interval x (make-interval (/ 1 (upper-bound y)) (/ 1 (lower-bound y))))))
こんなことも出来る。
(* 5 4 3 2 1 0) ; 0
ふふ。結構使えるかも!!
ぐはぁ・・・解答見たら全然違った!!
修正
; (1 .. 3) / (-1 .. 1) (/ 2.0 0.0000000000001) ; 20000000000000.0
除算に-1 .. 1とゼロを含んだ区間がある場合、ゼロに近い値を計算すると、値は非常に大きくなる。
しかし、現在のAlyssaのシステムでは
(div-interval (make-interval 1 3) (make-interval -1 1)) ; (-3 , 3)
全く違う値となってしまう。
区間で表わすと、
-∞<-----+ +------->∞ -3 3
というように、マイナス無限大 .. -3, 3 .. 無限大となり、区間に0を含む除算は区間で表わすことが出来ない。
(ホントは-∞ .. -1, 1 .. ∞)
また、区間にを含む0の除算を行う場合は、0に近似した値を2個取る必要がある。
僕のプログラムでは不完全なので、修正を加える。(a .. 0 .. b)と,間にゼロを含んでいない場合のみ計算するようにする。
(define (div-interval x y) (if (> (* (lower-bound y) (upper-bound y)) 0) (mul-interval x (make-interval (/ 1 (upper-bound y)) (/ 1 (lower-bound y)))) (error "div-interval : zero division error"))) (div-interval (make-interval 1 3) (make-interval -1 -0.00001)) ;(-299999.99999999994 . -1.0 (div-interval (make-interval 1 3) (make-interval 0.00001 1)) ; (1.0 . 299999.99999999994)
区間演算の難しさがわかってきたぞ・・・。
* 乗算でゼロを検出すると桁のオーバーフローを起こす可能性がある。intを使っている場合は要注意。Schemeの場合、多倍長演算に対応しているのでそれほど問題にならないと思うけど。