2017-04-09 2 views
1

So schrieb ich zwei Funktionen zur Berechnung natürlichen Logarithmus der Variable x, nach Erhöhung der oberen Grenze der inkrementellen Summe auf 33000 die Funktionen immer noch ungenau Ergebnisse in ghci getestet, verglichen mit dem Standardprotokoll Funktion von Prelude importiert, hier ist die Code Definition:Natural Logarithmus mit der Serie in Haskell gibt ungenaue Ergebnisse

lnOfx :: Float -> Float 
lnOfx x = netSum f 1 33000 
    where f i = 2*(1/oddTerm)*((x-1)/(x+1))**oddTerm 
      where oddTerm = 2*i-1 
      netSum f minB maxB = sum [f i | i <- [minB .. maxB]] 

lnOfx2 :: Float -> Float 
lnOfx2 x = netSum f 1 33000 
     where f i = (1/i)*((x-1)/x)**i 
      netSum f minB maxB = sum [f i | i <- [minB .. maxB]] 

und die Testergebnisse:

log 3 
1.0986122886681098 
lnOfx 3 
1.0986125 
lnOfx2 3 
1.0986122 

log 2 
0.6931471805599453 
lnOfx 2 
0.6931472 
lnOfx2 2 
0.6931473 

Also, warum die Ergebnisse unterscheiden und was ist der richtige Weg (wie die Log-Funktion von Prelude tut) zu berechnen den natürlichen Logarithmus richtig?

+0

(1) Gleitkommaarithmetik ist von Natur aus ungenau. (2) Ein Teil der Ungenauigkeit in Ihren Funktionen entsteht nur, weil Sie "Float" anstatt "Double" verwenden. – duplode

+0

@ duplode danke für die Antwort. Es ist in der Tat genauer, aber nachdem ich die Funktionen geändert habe, geben Sie die Signatur von Float nach Double ein und vergleichen Sie dann mit Prelude Log. Die letzten Ziffern unterscheiden sich immer noch erheblich. –

Antwort

6

Fließkomma-Mathematik ist schwierig. Eine Sache, die Genauigkeitsverlust verursachen kann, ist das Hinzufügen von Zahlen mit sehr unterschiedlichen Größen. Zum Beispiel, beginnend in Ihrem Algorithmus um i=25 die Begriffe in Ihrer Summe klein genug, dass sie einen Unterschied aufhören,:

-- 25t term:  
let f x i = let oddTerm = 2*i-1 in 2*(1/oddTerm)*((x-1)/(x+1))**oddTerm 
let y = f 3.0 25 

-- summation up to 24 item 
let s = 1.098612288668109 

-- this will return True, surprisingly! 
s + y == s 

Eine Sache, die Sie tun können, um zu mildern dies die Zahlen in umgekehrter Reihenfolge hinzufügen, so dass die kleine Zahlen werden addiert, bevor sie zu den großen Zahlen addiert werden.

lnOfx :: Float -> Float 
lnOfx x = netSum f 1 33000 
    where f i = 2*(1/oddTerm)*((x-1)/(x+1))**oddTerm 
      where oddTerm = 2*i-1 
      netSum f minB maxB = sum $ reverse [f i | i <- [minB .. maxB]] 

In meinen Tests war dies genug, so dass print (lnOfx 3.0) und print (log 3.0) alle die gleichen Ziffern zeigten.

Aber im Allgemeinen würde ich empfehlen, ein numerisches Analysebuch zu lesen, um mehr über diese Art von Problem zu erfahren.

Verwandte Themen