Hello all. I am writing a simple program to compute pi, using the well-known and inefficient series: pi ~= 4(1 - 1/3 + 1/5 - 1/7 + 1/9 - 1/11 + ... and the program is: pie :: Int -> Double pie n | n==1 = 4 | n==1005 = 2/fromInt n - 4/(fromInt n - 2) + pie(n-4) | otherwise = pie(n-4) + 4/fromInt n - 4/(fromInt n - 2) and this gives, using Hugs Version November 2002, Main> pie 1005 3.14159 Now, if I change the number of terms to be about 10 times as many, by changing the program to: pie :: Int -> Double pie n | n==1 = 4 | n==10005 = 2/fromInt n - 4/(fromInt n - 2) + pie(n-4) | otherwise = pie(n-4) + 4/fromInt n - 4/(fromInt n - 2) I get the LESS-accurate result: Main> pie 10005 3.1416 This would seem to be a bug, but I am not sending it to the bug list in case I'm doing something wrong with Haskell or Hugs, or am mistaken with my program. Any comments? Thanks. John Mann ===== "I'd rather be debugging...." - Anon __________________________________ Do you Yahoo!? SBC Yahoo! DSL - Now only $29.95 per month! http://sbc.yahoo.com
G'day. On Sun, Jun 22, 2003 at 02:26:00PM -0700, John Mann wrote:
Now, if I change the number of terms to be about 10 times as many, by changing the program to: [...] I get the LESS-accurate result:
Your problem is entirely due to floating point round-off error. I can think of two solutions to your problem off the top of my head. The most idiomatic Haskell solution is to work in rationals and convert to Double at the end. This also gives you the most accurate answer because there is no rounding error (until the last minute, of course). The most "numeric analyst-like" solution is to be more careful about the order that you add the terms. This, for example, is a simple modification of your code which always ensure that the two smallest numbers-to-be-added are added first: import List -- This is like your "pie" function with two modifications: -- 1) It returns a list of terms -- 2) It doesn't have the special case for the last -- term. This is dealt with in the case expression -- instead. pieSeries :: Integer -> [Double] pieSeries n = case ps' n of (t:ts) -> t * 0.5 : ts [] -> [] where ps' n | n==1 = [4] | otherwise = 4/fromInteger n : -4/(fromInteger n - 2) : ps' (n-4) -- Note that we need to sort by absolute value. The -- simplest way is to use the Schwartzian transform. -- (See http://haskell.org/hawiki/SchwartzianTransform) pie :: Integer -> Double pie n = sumTerms (sort [ (abs x, x) | x <- pieSeries n ]) where sumTerms [(_,t)] = t sumTerms ((_,t1):(_,t2):ts) = let t = t1+t2 in sumTerms (insert (abs t, t) ts) Cheers, Andrew Bromage
participants (2)
-
Andrew J Bromage -
John Mann