n-te Ziffer von Pi berechnen nach dem Bailey - Borwein - Plouffe-Verfahren

  • Spoiler anzeigen
    [autoit]

    ;Übersetzung FORTRAN => AutoIt by Andy
    ;Verfahren: http://www.unet.univie.ac.at/~a8727063/Science/BBP/
    ;
    ;Beispiele in FORTRAN;
    ;http://www.unet.univie.ac.at/~a8727063/Science/BBP/bbp.f90 bzw.
    ;http://www.unet.univie.ac.at/~a8727063/Science/BBP/bbpI2R4.f90
    ;
    ;errechnet die n-te Dezimalstelle von PI als HEX-Ziffer
    ;vgl. Tabelle der Ziffern bis 5000
    ;http://books.google.de/books?id=mchJC…tabelle&f=false

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ;~ program bbp
    ;~ implicit none
    ;~ integer :: i, k, l, n
    ;~ integer, dimension(4) :: arr1, arr2, arr4
    Dim $arrC[4] = [4, -2, -1, -1]
    Dim $arr1[4]
    Dim $arr2[4]
    Dim $arr3[4]
    Dim $arr4[4]

    [/autoit] [autoit][/autoit] [autoit]

    ;~ real , dimension(4) :: arr3
    ;~ real :: log2

    [/autoit] [autoit][/autoit] [autoit]

    ;log2 = log(2.0)
    $log2 = Log(2.0)
    $log16 = Log(2.0) * 4

    [/autoit] [autoit][/autoit] [autoit]

    ;err = 5q-32
    $err = 5 * 10 ^ - 32

    [/autoit] [autoit][/autoit] [autoit]

    ;~ print '(a)', &
    ;~ "+--------------------------------------------------------------------------+",&
    ;~ "| 'bbp.f90': |",&
    ;~ "| Fortran90 program implementing the BBP algorithm |",&
    ;~ "| for computing hex digits of Pi according to |",&
    ;~ "| http://ams.astro.univie.ac.at/~nendwich/Science/BBP/ |",&
    ;~ "+--------------------------------------------------------------------------+"
    ;~ print '(a,i0,a)', " max n: ", (sqrt(real(huge(n)))-5)/8, &
    $nmax = Int((Sqrt(2 ^ 63) - 5) / 8)
    ;MsgBox(262144,'Debug line ~' & @ScriptLineNumber,'Selection:' & @lf & '$nmax' & @lf & @lf & 'Return:' & @lf & $nmax) ;### Debug MSGBOX
    ;~ " (otherwise result might be uncorrect)"
    ;~ print *, "exit program with n < 0"

    [/autoit] [autoit][/autoit] [autoit]

    ;do
    While 1
    ;~ write (*,'(a)',advance='no') " n = ? : "
    ;~ read (*,'(i)') n
    $n = InputBox("n-te Stelle von PI", "Gesuchte Ziffer eingeben max=" & $nmax)

    [/autoit] [autoit][/autoit] [autoit]

    ;~ if (n<0) exit
    If $n <= 0 Or $n > $nmax Then Exit

    [/autoit] [autoit][/autoit] [autoit]

    ;~ arr3 = [0.,0.,0.,0.] ! ~ arr2 / arr4
    Local $arr3[4] = [0, 0, 0, 0]
    ;~ do k = 0, n
    For $k = 0 To $n
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $k = ' & $k & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
    ;~ l = n - k
    $l = $n - $k
    ;~ arr1 = [4,4,4,4] ! 16^(2^i) mod arr4, init: i = -1
    Local $arr1[4] = [4, 4, 4, 4]
    ;~ arr2 = [1,1,1,1] !
    Local $arr2[4] = [1, 1, 1, 1]
    ;~ arr4 = [1,4,5,6] + 8*k ! 8k+j
    Local $arr4[4] = [1 + 8 * $k, 4 + 8 * $k, 5 + 8 * $k, 6 + 8 * $k]
    ; _arraydisplay($arr4)
    ;~ if (l/=0) then
    If $l <> 0 Then
    ;~ do i = 0, int(log(l+0.5)/log2)
    For $i = 0 To Int(Log($l + 0.5) / $log2)
    ;~ arr1 = mod(arr1**2,arr4)
    $tt0 = Mod($arr1[0] ^ 2, $arr4[0])
    $tt1 = Mod($arr1[1] ^ 2, $arr4[1])
    $tt2 = Mod($arr1[2] ^ 2, $arr4[2])
    $tt3 = Mod($arr1[3] ^ 2, $arr4[3])
    Local $arr1[4] = [$tt0, $tt1, $tt2, $tt3]
    ;~ if (btest(l,i)) arr2 = mod(arr2*arr1,arr4)
    If _btest($l, $i) Then
    ; msgbox(0,"btest 1",Mod($arr2[0] * $arr1[0], $arr4[0]))
    $tt0 = Mod($arr2[0] * $arr1[0], $arr4[0])
    $tt1 = Mod($arr2[1] * $arr1[1], $arr4[1])
    $tt2 = Mod($arr2[2] * $arr1[2], $arr4[2])
    $tt3 = Mod($arr2[3] * $arr1[3], $arr4[3])
    Local $arr2[4] = [$tt0, $tt1, $tt2, $tt3]
    EndIf
    ;~ end do ! i
    Next
    ;~ end if
    EndIf
    ;~ arr3 = mod(arr3+real(arr2)/arr4,1.0)
    $tt0 = Mod($arr3[0] + $arr2[0] / $arr4[0], 1.0)
    $tt1 = Mod($arr3[1] + $arr2[1] / $arr4[1], 1.0)
    $tt2 = Mod($arr3[2] + $arr2[2] / $arr4[2], 1.0)
    $tt3 = Mod($arr3[3] + $arr2[3] / $arr4[3], 1.0)
    Local $arr3[4] = [$tt0, $tt1, $tt2, $tt3]
    ;~ end do ! k
    Next
    ;~ print '(a,i0,a,z1)', "Result: ", k, " th hex digit of pi = ", &
    ;~ int(16*modulo(dot_product([4.,-2.,-1.,-1.],arr3),1.0))

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ;~ fsum = modulo(dot_product(arrC,arr3),1.0_rk) ! final sum
    $fsum = _modulo(_dot_product($arrC, $arr3), 1.0)

    [/autoit] [autoit][/autoit] [autoit]

    ;~ cor = sum(arrC/(arr4+8_ik)) / 16 ! 1st correction
    Dim $asum[4]
    $asum[0] = $arrC[0] / ($arr4[0] + 8)
    $asum[1] = $arrC[1] / ($arr4[1] + 8)
    $asum[2] = $arrC[2] / ($arr4[2] + 8)
    $asum[3] = $arrC[3] / ($arr4[3] + 8)

    [/autoit] [autoit][/autoit] [autoit]

    $cor = _sum($asum) / 16
    ;MsgBox(262144,'Debug line ~' & @ScriptLineNumber,'Selection:' & @lf & '$cor' & @lf & @lf & 'Return:' & @lf & $cor) ;### Debug MSGBOX
    ;~ ester = k * err ! estimated error
    $ester = $k * $err
    ;~ print *, "Final sum = ", fsum ; fsum = 16 * fsum

    [/autoit] [autoit][/autoit] [autoit]

    ;~ print *, "Est. err. = ", ester ; l = -1 - log(ester)/log16
    ; MsgBox(0, "geschätzter Fehler ester=", $ester)
    ;~ print *, "1st corr. = ", cor
    ; MsgBox(0, "1. corr", $cor)
    ;~ write (*,'(a,i0,a/8x,z1)',advance='no') &
    ;~ "Result: ", k, " th and following hex digits of pi = ", int(fsum)
    ;~ do i = 2, -log(cor)/log16 ! 16^i * cor = 1.0
    $string = ""
    For $i = 2 To Int(-Log($cor) / $log16)
    ;~ if (i==l) write (*,'(" (")',advance='no')
    If $i = $l Then $string &= "( "
    ;~ fsum = 16 * mod(fsum,1.0)
    $fsum = 16 * Mod($fsum, 1.0)
    ;~ write (*,'(z2)',advance='no') int(fsum)
    $string &= Hex(Int($fsum), 1) & " "
    ;~ end do ! i
    Next
    ;~ if (i>l) write (*,'(" )")',advance='no')
    If $i > $l And StringInStr($string, "(") Then $string &= ")"

    [/autoit] [autoit][/autoit] [autoit]

    MsgBox(0, $k & " -te hex-ziffer von Pi =", Hex(Int(16 * _Modulo(_dot_product($arrC, $arr3), 1.0)), 1) & @CRLF & @CRLF & "nächste Ziffern (incl.) = " & $string)

    [/autoit] [autoit][/autoit] [autoit]

    ;end do

    [/autoit] [autoit][/autoit] [autoit]

    WEnd

    [/autoit] [autoit][/autoit] [autoit]

    ;end program bbp

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func _dot_product($a, $b)
    $vec = 0
    For $i = 0 To UBound($a) - 1
    $vec += $a[$i] * $b[$i]
    Next

    [/autoit] [autoit][/autoit] [autoit]

    Return $vec
    EndFunc ;==>_dot_product

    [/autoit] [autoit][/autoit] [autoit]

    Func _btest($a, $pos) ;bittest liefert TRUE, wenn das ($pos-te Bit von $a) = 1 ist
    ;http://gcc.gnu.org/onlinedocs/gcc-4.1.2/gfortran/BTEST.html
    $bit = BitAND(2 ^ 31 - 1, 2 ^ $pos)
    If BitAND($a, $bit) Then
    Return True
    Else
    Return False
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    EndFunc ;==>_btest

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func _modulo($a, $b) ;ir1 - floor(ir1/ir2)*ir2
    ;http://de.wikibooks.org/wiki/Fortran:_…nktionen#Modulo
    Return $a - Floor($a / $b) * $b
    EndFunc ;==>_modulo

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func _sum($e)
    $sum = 0
    For $i = 0 To UBound($e) - 1
    $sum += $e[$i]
    Next
    Return $sum
    EndFunc ;==>_sum

    [/autoit]


    Wer mag, kann das Script so umschreiben, dass die fortlaufenden Ziffern von Pi errechnet werden (die nächsten Ziffern werden mitberechnet!)

  • Zitat

    Warum wird manchmal aber A oder F oder so ausgegeben?

    Die Ausgabe ist hexadezimal, also zur Basis 16
    Warum und wieso das so ist, ist im Verfahren HIER beschrieben.

  • Zitat

    Entweder bin ich zu dumm, aber wie kann ich nun letztendlich 3,141... rausbekommen?

    Das hat mit dumm nichts zu tun, sondern mit der Umwandlung einer hexadezimalen Ziffer in eine Dezimale....
    Da bleib ich doch bei meinem Spruch: "Nur selbst fressen macht fett!", also lass dir was einfallen :thumbup:

  • Einen "Denkanstoss" geb ich noch... :rolleyes:

    wandele die HEX-Ziffern in das Binär-System um (jeweils 4 digits 0 oder 1), du erhälst also 4er "Pakete", die du einfach hintereinander schreibst
    3,243F6 wird zu
    0011 , 0010 0100 0011 1111 01010

    Dann zuerst VOR dem Komma von rechts nach links:
    1*2^0+ 1*2^1+0*2^2+0*2^3 = 3

    Dann HINTER dem Komma von links nach rechts:
    0*2^-1 + 0*2^-2 + 1*2^-3 + 0*2^-4 + 0*2^-5 + 1*2^-6.....uswusf

  • Allerdings bringt dir das nicht sonderlich viel ohne die Bignum.udf!
    AutoIt speichert Floatingpointzahlen intern als 64Bit-"double". Also ist nach 3.14159265358979 Schluss, denn damit sind die 64 Bit belegt...
    Die Hex-Digits findet das Programm bis zur 2^63-1. Stelle...wenn man reichlich Zeit hat^^

    Das schöne ist, wenn das Script z.B. 5 Hex-Digits errechnet, kann man diese Hex-Digits ja sofort in "lange" Floats (bignum.au3) umwandeln und einfach zum bisherigen Ergebnis dazuaddieren. Gleichzeitig ist die Startnummer für die nächste Hexziffer schon 5 Digits weiter!

  • autoit.de/wcf/attachment/12035/in 758 Sekunden per AutoIt@2.7Ghz
    Mal sehen, was da noch speedmässig zu machen ist^^

  • da schreib ich vorher lieber einige Zeilen Assembler^^

  • Diese Methode ist veraltet und langsam, schon mal von dr Chudnovsky Methode gehört? Da lässt sich auf diesem Rechner Pi innerhalb von Sekunden auf 1,~ Trilliarden Stellen berechenen, das wäre mal interessant in Autoit!

  • Zitat

    Diese Methode ist veraltet und langsam, schon mal von dr Chudnovsky Methode gehört? Da lässt sich auf diesem Rechner Pi innerhalb von Sekunden auf 1,~ Trilliarden Stellen berechenen, das wäre mal interessant in Autoit!

    ...wer lesen kann ist klar im Vorteil...

    Fabrice Bellard (December 2009) hat per Chudnovsky-Methode knapp 150 Tage gebraucht auf seinem Rechner, allerdings hatte er nur 6Gig RAM und nur 7 Terabyte Festplatten.
    Die Schwierigkeit der Verifikation infolge Bitfehler des non-ECC-RAM´s mal aussen vor gelassen....ich glaube kaum, dass ein 08/15 "Gaming"-PC da besser ist...
    Übrigens ein sehr interessantes Dokument, sehr verständlich geschrieben. Von Mathematik sollte man allerdings bissl Ahnung haben...

    Per BPP (also der langsamen Methode^^) auf einem Desktoprechner liegt der Rekord zzt. bei 5 Trilllionen Stellenin 90 Tagen. Man beachte, Dual-XEONs, 96 Gig RAM und knapp 30 Terabyte Platten. Auch da kommt ein 08/15 Gaming-PC nicht ganz mit...

    Soviel zum Thema "in Sekunden"...aber ich halte JEDE Wette, wer sich das Ding kauft, der glaubt das wirklich :D


    Btw. wieso muss man eigentlich in AutoIt

    [autoit]

    $tt0 = Mod($arr1[0] ^ 2, $arr4[0])
    $tt1 = Mod($arr1[1] ^ 2, $arr4[1])
    $tt2 = Mod($arr1[2] ^ 2, $arr4[2])
    $tt3 = Mod($arr1[3] ^ 2, $arr4[3])
    Local $arr1[4] = [$tt0, $tt1, $tt2, $tt3]

    [/autoit]

    schreiben, damit das Array richtig initialisiert wird. Denn die "kurze" Version

    [autoit]

    Local $arr1[4] = [Mod($arr1[0] ^ 2, $arr4[0]),Mod($arr1[1] ^ 2, $arr4[1]),Mod($arr1[2] ^ 2, $arr4[2]),Mod($arr1[3] ^ 2, $arr4[3])]

    [/autoit]

    liefert falsche Ergebnisse!


  • Weil du die Geltungsbereiche von Variablen etwas "merkwürdig" verwendest.
    Mit Local $arr1[4] = [...] wird im freien Speicher ein neues Array mit 4 Elementen erstellt und dem Bezeichner $arr1 zugewiesen.
    Danach(!) wird es dann gleich mit den, in der eckigen Klammer angegebenen, Werten gefüllt.
    Nun verweist der Bezeichner $arr1 auf den neuen Speicherbereich - also verweist du mit Mod($arr1[1] ^ 2, $arr4[1]) auf einen neuen leeren Speicherplatz - also NULL-Werte (In AutoIt entspricht dies einem Leerstring). Es bleibt also in jedem Fall folgendes übrig: Mod("" ^2, $arr4[1]) was wiederrum, aufgrund der impliziten Typumwandlung, dann folgendem entspricht: Mod(0,$arr4[1])

    Local macht hier auch keinen Sinn - es ist eine globale Variable da du sie im globalen Raum definierst anstatt in einer Funktion.
    Daher wird sie so oder so global.
    Mir ist schon klar dass du local nur deswegen genommen hast damit du das Array in nur einer Zeile neu schreiben kannst aber dir als Assembler-Fuchs sollte eigentlich schon klar sein dass du auf diese Weise immer wieder neuen Speicher allozierst anstatt das einmal erstellte Array nur zu aktualisieren.

    Warum schreibst du es z.B. nicht so:

    [autoit]

    For $x = 0 To 3
    $arr1[$x] = Mod($arr1[$x] ^ 2, $arr4[$x])
    Next

    [/autoit]


    So bleibt das Array bestehen, das allozieren des Speichers entfällt, die 4 zusätzlichen Variablen werden obselet und die Übersichtlichkeit und Wartbarkeit sollte ebenfalls gewahrt bleiben.

    Also komplett umgeschrieben als Vorschlag:

    Spoiler anzeigen
    [autoit]

    Opt("MustDeclareVars", 1)

    [/autoit] [autoit][/autoit] [autoit]

    Global $arrC[4] = [4, -2, -1, -1]
    Global $arr1[4], $arr2[4], $arr3[4], $arr4[4], $asum[4]

    [/autoit] [autoit][/autoit] [autoit]

    Global Const $log2 = Log(2.0)
    Global Const $log16 = Log(2.0) * 4
    Global Const $err = _GET_FLT_EPSILON()
    Global Const $nmax = Int((Sqrt(2 ^ 63) - 5) / 8)

    [/autoit] [autoit][/autoit] [autoit]

    Global $n, $l, $fsum, $cor, $ester, $string

    [/autoit] [autoit][/autoit] [autoit]

    While 1
    $n = Int(InputBox("n-te Stelle von PI", "Gesuchte Ziffer eingeben max=" & $nmax))

    [/autoit] [autoit][/autoit] [autoit]

    If $n <= 0 Or $n > $nmax Then Exit

    [/autoit] [autoit][/autoit] [autoit]

    For $i = 0 To 3
    $arr3[$i] = 0
    Next

    [/autoit] [autoit][/autoit] [autoit]

    For $k = 0 To $n
    $l = $n - $k
    For $i = 0 To 3
    $arr1[$i] = 4
    $arr2[$i] = 1
    $arr4[$i] = $i + 3 + (8 * $k)
    Next
    $arr4[0] = 1 + 8 * $k

    [/autoit] [autoit][/autoit] [autoit]

    If $l <> 0 Then
    For $i = 0 To Int(Log($l + 0.5) / $log2)

    [/autoit] [autoit][/autoit] [autoit]

    For $x = 0 To 3
    $arr1[$x] = Mod($arr1[$x] ^ 2, $arr4[$x])
    Next

    [/autoit] [autoit][/autoit] [autoit]

    If _btest($l, $i) Then
    For $x = 0 To 3
    $arr2[$x] = Mod($arr2[$x] * $arr1[$x], $arr4[$x])
    Next
    EndIf
    Next
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    For $x = 0 To 3
    $arr3[$x] = Mod($arr3[$x] + $arr2[$x] / $arr4[$x], 1.0)
    Next
    Next
    $fsum = _modulo(_dot_product($arrC, $arr3), 1.0)

    [/autoit] [autoit][/autoit] [autoit]

    For $x = 0 To 3
    $asum[$x] = $arrC[$x] / ($arr4[$x] + 8)
    Next

    [/autoit] [autoit][/autoit] [autoit]

    $cor = _sum($asum) / 16
    $ester = $k * $err
    $string = ""
    For $i = 2 To Int(-Log($cor) / $log16)
    If $i = $l Then $string &= "( "
    $fsum = 16 * Mod($fsum, 1.0)
    $string &= Hex(Int($fsum), 1) & " "
    Next
    If $i > $l And StringInStr($string, "(") Then $string &= ")"

    [/autoit] [autoit][/autoit] [autoit]

    MsgBox(0, $k & " -te hex-ziffer von Pi =", Hex(Int(16 * _Modulo(_dot_product($arrC, $arr3), 1.0)), 1) & @CRLF & @CRLF & "nächste Ziffern (incl.) = " & $string)
    WEnd

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func _dot_product(ByRef $a, ByRef $b)
    Local $vec = 0
    For $i = 0 To UBound($a) - 1
    $vec += $a[$i] * $b[$i]
    Next
    Return $vec
    EndFunc ;==>_dot_product

    [/autoit] [autoit][/autoit] [autoit]

    Func _btest(Const $a, Const $pos) ;bittest liefert TRUE, wenn das ($pos-te Bit von $a) = 1 ist
    Local Const $bit = BitAND(2 ^ 31 - 1, 2 ^ $pos)
    If BitAND($a, $bit) Then
    Return True
    Else
    Return False
    EndIf
    EndFunc ;==>_btest

    [/autoit] [autoit][/autoit] [autoit]

    Func _modulo(Const $a, Const $b) ;ir1 - floor(ir1/ir2)*ir2
    Return $a - Floor($a / $b) * $b
    EndFunc ;==>_modulo

    [/autoit] [autoit][/autoit] [autoit]

    Func _sum(ByRef $e)
    Local $sum = 0
    For $i = 0 To UBound($e) - 1
    $sum += $e[$i]
    Next
    Return $sum
    EndFunc ;==>_sum

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ; #FUNCTION#=================================================================================
    ; Name...........: _GET_FLT_EPSILON
    ; Description ...: Berechnet den binären Rundungsfehler und damit die Rechengenauigkeit
    ; Syntax.........: _GET_FLT_EPSILON
    ; Return values .: Epsilon
    ; Author ........: AspirinJunkie
    ;============================================================================================
    Func _GET_FLT_EPSILON()
    Local $x = 1
    While True
    $x /= 2
    If 1 + $x <= 1 Then Return $x
    WEnd
    EndFunc ;==>_GET_FLT_EPSILON

    [/autoit]

    3 Mal editiert, zuletzt von AspirinJunkie (12. Dezember 2010 um 17:15)

  • :thumbup:

    Zitat

    Mit Local $arr1[4] = [...] wird im freien Speicher ein neues Array mit 4 Elementen erstellt und dem Bezeichner $arr1 zugewiesen.

    Wenn das so wäre, dann ist es auch in der Hilfe falsch beschrieben! Denn dort heisst es:

    Zitat von Hilfe zu DIM

    ...Arrays....Deklariert man den den Variablennamen erneut, werden alle Werte des Arrays gelöscht, und auf die neu definierte Größe dimensioniert

    Und genau das wollte ich damit machen. Das Array mit den "neuen" Werten überschreiben.

    Zitat

    Mir ist schon klar dass du local nur deswegen genommen hast damit du das Array in nur einer Zeile neu schreiben kannst aber dir als Assembler-Fuchs sollte eigentlich schon klar sein dass du auf diese Weise immer wieder neuen Speicher allozierst anstatt das einmal erstellte Array nur zu aktualisieren.

    hehe, der Fuchs hatte Lunte gerochen und per Processexplorer vorsichtshalber festgestellt, dass mitnichten neuer Speicher angefordert wird. Somit war ich da auf der sicheren Seite. Aber was ich natürlich nicht bedacht hatte war, dass das alte Array gelöscht und das "neue" Array an einem anderen Speicherplatz erstellt wird, anstatt das "alte" einfach zu überschreiben....

    Zitat

    Nun verweist der Bezeichner $arr1 auf den neuen Speicherbereich - also verweist du mit Mod($arr1[1] ^ 2, $arr4[1]) auf einen neuen leeren Speicherplatz - also NULL-Werte (In AutoIt entspricht dies einem Leerstring). Es bleibt also in jedem Fall folgendes übrig: Mod("" ^2, $arr4[1]) was wiederrum, aufgrund der impliziten Typumwandlung, dann folgendem entspricht: Mod(0,$arr4[1])

    So klasse erklärt, das begreife sogar ich! :thumbup:
    Wobei ich aber nicht verstehe, warum ausgerechnet in diesem Fall "von aussen nach innen" geparst wird (seitens Interpreter), statt wie sonst üblich, vonn innen nach aussen.... Naja, wieder was gelernt^^

    Hab mir schon überlegt, den Assembler mal anzuwerfen. Da Vektoren im Spiel sind, könnte man die 128-Bit-SSE Befehle mal aktivieren. Schaumamal, ob ich ne Stunde abgezwackt bekomme dafür...

  • hehe, der Fuchs hatte Lunte gerochen und per Processexplorer vorsichtshalber festgestellt, dass mitnichten neuer Speicher angefordert wird. Somit war ich da auf der sicheren Seite. Aber was ich natürlich nicht bedacht hatte war, dass das alte Array gelöscht und das "neue" Array an einem anderen Speicherplatz erstellt wird, anstatt das "alte" einfach zu überschreiben....


    Das versteh ich nun wieder nicht.
    Das habe ich doch gesagt - oder nicht?
    Es wird ein neues Array woanders im Speicher erstellt und das alte gekillt.
    Oder hast du mich so verstanden dass das alte Array erhalten bleibt und dennoch ein neues Array erstellt wird - also ein Memory-Leak?
    Wenn ja dann nein so meinte ich das nicht. :)

    Wobei ich aber nicht verstehe, warum ausgerechnet in diesem Fall "von aussen nach innen" geparst wird (seitens Interpreter), statt wie sonst üblich, vonn innen nach aussen....

    Wissen tu ich es ja auch nicht - hab es ja auch nur durch n Testskript festgestellt.
    So gesehen hab ich das aber noch nicht - stimmt eigentlich... :huh:
    Könnte mir nur vorstellen dass es bei der direkten Zuweisung so gemacht wird da die Werte erst sinnlos irgendwo zwischengespeichert werden müssen bevor sie schlussendlich in das Array eingetragen werden und man das ganze so effektiver gestaltet hat.

    Hab mir schon überlegt, den Assembler mal anzuwerfen. Da Vektoren im Spiel sind, könnte man die 128-Bit-SSE Befehle mal aktivieren. Schaumamal, ob ich ne Stunde abgezwackt bekomme dafür...


    SSE im Einsatz wär wirklich mal interessant - würde mich interessieren.

  • Zitat

    SSE im Einsatz wär wirklich mal interessant - würde mich interessieren.

    macht aber nur bei einfacher Genauigkeit Sinn, dann könnte man 4*32Bit in einem Rutsch verarbeiten.
    Dann wäre jeweils ein komplettes Array in einem XMM-Register 8o
    Nur das MOD() ist übel, leider gibts da kaum was entsprechendes. Bliebe nur das übliche: REST= a-int(a/b)*b
    Aber das geht glücklicherweise auch recht einfach bei einfacher Genauigkeit! Leider aber nicht bei double...
    Ich pfriemel mal was zusammen....

  • Kurzes Beispiel, wie exterm SSE die Geschwindigkeit beeinflussen kann!

    Spoiler anzeigen
    [autoit]

    ;Übersetzung FORTRAN => AutoIt by Andy
    ;Verfahren: http://www.unet.univie.ac.at/~a8727063/Science/BBP/
    ;
    ;Beispiele in FORTRAN;
    ;http://www.unet.univie.ac.at/~a8727063/Science/BBP/bbp.f90 bzw.
    ;http://www.unet.univie.ac.at/~a8727063/Science/BBP/bbpI2R4.f90
    ;
    ;errechnet die n-te Dezimalstelle von PI als HEX-Ziffer
    ;vgl. Tabelle der Ziffern bis 5000
    ;http://books.google.de/books?id=mchJC…tabelle&f=false

    [/autoit] [autoit][/autoit] [autoit]

    #include<assembleit.au3>
    #include <Array.au3>

    [/autoit] [autoit][/autoit] [autoit]

    Dim $arrC[4] = [4, -2, -1, -1]
    Dim $arr1[4]
    Dim $arr2[4]
    Dim $arr3[4]
    Dim $arr4[4]

    [/autoit] [autoit][/autoit] [autoit]

    Global $time, $k

    [/autoit] [autoit][/autoit] [autoit]

    $log2 = Log(2)
    $log16 = Log(2.0) * 4

    [/autoit] [autoit][/autoit] [autoit]

    $err = 5 * 10 ^ - 32
    $nmax = Int((Sqrt(2 ^ 63) - 5) / 8)

    [/autoit] [autoit][/autoit] [autoit]

    ;ohne _MemVirtualAlloc() kein align!
    ;align 16 in der dllstruct() funktioniert NICHT zuverlässig!
    $pRemoteCode = _MemVirtualAlloc(0, 512, $MEM_COMMIT, $PAGE_EXECUTE_READWRITE)
    $struct = DllStructCreate("" & _ ;adresse in der struct
    "float arr1[4];" & _ ;edi+00 xmm7=4,4,4,4
    "float arr2[4];" & _ ;edi+16 xmm6=1,1,1,1
    "float arr3[4];" & _ ;edi+32 xmm3=0,0,0,0
    "float arr4[4];" & _ ;edi+48
    "float arr5[4];" & _ ;edi+64 xmm5=1,4,5,6
    "float nullfuenf;" & _ ;edi+80
    "float log2" & _ ;edi+84
    "int", $pRemoteCode) ;edi+88 speicherplatz div zwischenwerte
    $ptr = DllStructGetPtr($struct)
    For $i = 1 To 4
    DllStructSetData($struct, 1, 4.0, $i);xmm7=4,4,4,4
    DllStructSetData($struct, 2, 1.0, $i);xmm6=1,1,1,1
    DllStructSetData($struct, 3, 0.0, $i);xmm3=0,0,0,0
    Next
    DllStructSetData($struct, 5, 1.0, 1);xmm5=1
    DllStructSetData($struct, 5, 4.0, 2);xmm5=1,4
    DllStructSetData($struct, 5, 5.0, 3);xmm5=1,4,5
    DllStructSetData($struct, 5, 6.0, 4);xmm5=1,4,5,6

    [/autoit] [autoit][/autoit] [autoit]

    DllStructSetData($struct, 6, 0.5, 1);0.5
    DllStructSetData($struct, 7, $log2, 1);log2

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func _pi_hex()
    _("use32")
    _("finit") ;copro -init
    _("mov edi,[esp+4]") ;startadresse struct holen

    [/autoit] [autoit][/autoit] [autoit]

    _("movaps xmm7,[edi+00]") ;arr1=4,4,4,4
    _("movaps xmm6,[edi+16]") ;arr2=1,1,1,1
    _("movaps xmm3,[edi+32]") ;arr3
    _("movaps xmm5,[edi+64]") ;arr5=1,4,5,6

    [/autoit] [autoit][/autoit] [autoit]

    _("mov ebx,-1") ;zähler K
    _("_for_k:")
    _("add ebx,1")

    [/autoit] [autoit][/autoit] [autoit]

    _("cmp ebx,[esp+8]") ;vgl k mit N
    _("ja _ende") ;wenn fertig, ende

    [/autoit] [autoit][/autoit] [autoit]

    _("mov edx,[esp+8]") ;l=n
    _("sub edx,ebx") ;l=n-k

    [/autoit] [autoit][/autoit] [autoit]

    _("movaps xmm1,xmm7") ;arr1=4,4,4,4
    _("movaps xmm2,xmm6") ;arr2=1,1,1,1
    ;arr4=1+k+8 , 4+k*8 , 5+k*8 , 6+k*8
    _("mov eax,ebx") ;k
    _("shl eax,3") ;k*8 20
    _("mov [edi+48],eax") ;arr4=0 , k*8 , 0 , 0
    _("mov [edi+56],eax") ;arr4=0 , k*8 , 0 , k*8
    _("movsldup xmm4,[edi+48]") ;arr4=k*8 , k*8 , k*8 , k*8
    _("cvtdq2ps xmm4,xmm4") ;arr4 float( aus allen 4 integer)
    _("addps xmm4,xmm5") ;arr4=1+k*8,4+k*8,5+k*8,6+k*8

    [/autoit] [autoit][/autoit] [autoit]

    _("cmp edx,0") ;ist L=0?
    _("je _arr3") ;ja, dann arr3:
    _("")
    ;int(log(L+0.5)/log2) ;folgende zeilen stark kürzbar
    _("mov [edi+88],edx") ;L
    _("emms") ;fpu und sse zusammen braucht emms!
    _("fld dword[edi+80]") ;st0=0.5
    _("fld dword[edi+84]") ;st0=log2 st1=0.5
    _("fldln2") ;st0=ln2 st1=log2 st2=0.5
    _("fild dword[edi+88]") ;st0=L st1=1 st2=log2 st3=0.5
    _("fadd st0,st3") ;st0=L+0.5 st1=1 st2=log2 st3=0.5
    _("fyl2x") ;st0=ln2*log_2(L+0.5) st1=log2 st2=0.5 32550
    _("fdiv st0,st1") ;st0=(ln2*log_2(L+0.5)/log2) st1=log2 st2=0.5
    _("fisttp dword[edi+88]") ;st0=log2 st1=0.5
    _("fucompp") ;fpu-stack cleanen

    [/autoit] [autoit][/autoit] [autoit]

    _("mov esi,-1")
    _("_for_i:")
    _("add esi,1")
    _("cmp esi,[edi+88]") ;ist i=int(log(L+0.5)/log2) ?
    _("ja _arr3")
    _("")
    ;arr1=mod(arr1^2,arr4) für alle 4 floats

    [/autoit] [autoit][/autoit] [autoit]

    _("mulps xmm1,xmm1") ;arr1^2
    _("movaps xmm0,xmm1") ;arr1^2
    ;;berechnet mod=rest=a-int(a/b)*b
    _("divps xmm0,xmm4") ;a/b
    _("cvttps2dq xmm0,xmm0") ;int(a/b)
    _("cvtdq2ps xmm0,xmm0") ;float(int(a/b))
    _("mulps xmm0,xmm4") ;int(a/b)*b
    _("subps xmm1,xmm0") ;arr1=mod(arr1^2,arr4) für alle 4 floats ;
    _("")
    _("bt edx,esi") ;bit_test(L,I)
    _("jnc _for_i")
    _("")
    ;arr2=mod(arr2*arr1,arr4)
    _("mulps xmm2,xmm1") ;arr2*arr1
    _("movaps xmm0,xmm2") ;arr2*arr1
    ;;berechnet rest=a-int(a/b)*b
    _("divps xmm0,xmm4") ;a/b
    _("cvttps2dq xmm0,xmm0") ;int(a/b)
    _("cvtdq2ps xmm0,xmm0") ;float(int(a/b))
    _("mulps xmm0,xmm4") ;int(a/b)*b
    _("subps xmm2,xmm0") ;arr2=mod(arr2*arr1,arr4) für alle 4 floats

    [/autoit] [autoit][/autoit] [autoit]

    _("jmp _for_i")

    [/autoit] [autoit][/autoit] [autoit]

    _("_arr3:")
    _("movaps xmm0,xmm2") ;arr2
    _("divps xmm0,xmm4") ;arr2/arr4
    _("addps xmm3,xmm0") ;arr3+arr2/arr4
    _("movaps xmm0,xmm3") ;arr3+arr2/arr4
    ;;berechnet rest=a-int(a/b)*b
    _("divps xmm0,xmm6") ;a/b
    _("cvttps2dq xmm0,xmm0") ;int(a/b)
    _("cvtdq2ps xmm0,xmm0") ;float(int(a/b))
    _("mulps xmm0,xmm6") ;int(a/b)*b
    _("subps xmm3,xmm0") ;arr3=mod(arr3+arr2/arr4,arr6) für alle 4 floats
    _("jmp _for_k")

    [/autoit] [autoit][/autoit] [autoit]

    _("_ende:")
    _("movaps [edi],xmm3") ;xmm3 ins erste feld der struct schreiben
    _("movaps [edi+48],xmm4");xmm3 ins erste feld der struct schreiben
    _("mov eax,edx") ;L return
    _("ret")
    _("")

    [/autoit] [autoit][/autoit] [autoit]

    EndFunc ;==>_pi_hex

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    While 1
    $n = InputBox("n-te Stelle von PI", "Gesuchte Ziffer eingeben max=" & $nmax)
    If $n <= 0 Or $n > $nmax Then Exit
    For $i = 1 To 4 ;struct füllen für den Assemblerteil
    DllStructSetData($struct, 1, 4.0, $i);xmm7=4,4,4,4
    DllStructSetData($struct, 2, 1.0, $i);xmm6=1,1,1,1
    DllStructSetData($struct, 3, 0.0, $i);xmm3=0,0,0,0
    Next

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ;der Assemblerteil ersetzt die Hauptschleife For K=0 to N
    ;folgende 2 Zeilen gehören an den Anfang des Scripts

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Global $tCodeBuffer = DllStructCreate("byte[213]") ;reserve Memory for opcodes
    DllStructSetData($tCodeBuffer, 1, "0x9BDBE38B7C24040F283F0F2877100F285F200F286F40BBFFFFFFFF83C3013B5C24080F87A30000008B54240829DA0F28CF0F28D689D8C1E003894730894738F30F1267300F5BE40F58E583FA00745B8957580F77D94750D94754D9EDDB4758D8C3D9F1D8F1DB4F58DAE9BEFFFFFFFF83C6013B775877330F59C90F28C10F5EC4F30F5BC00F5BC00F59C40F5CC80FA3F273DD0F59D10F28C20F5EC4F30F5BC00F5BC00F59C40F5CD0EBC50F28C20F5EC40F58D80F28C30F5EC6F30F5BC00F5BC00F59C60F5CD8E950FFFFFF0F291F0F29673089D0C3") ;write opcodes into memory

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    $flag = 1 ;wenn 0, dann AssembleIt()

    [/autoit] [autoit][/autoit] [autoit]

    If $flag Then ;CallWindowProcW
    $t = TimerInit()
    $ret = DllCall("user32.dll", "int", "CallWindowProcW", "ptr", DllStructGetPtr($tCodeBuffer), "ptr", $ptr, "int", $n, "int", 0, "int", 0)
    $m = TimerDiff($t)
    $l = $ret[0]
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $m = ' & $m & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
    Else
    ;$_assembleit_flag=0 ;um opcodes aus dem Assemblercode zu erstellen
    $t = TimerInit()
    $l = _AssembleIt("int", "_pi_hex", "ptr", $ptr, "int", $n)
    $m = TimerDiff($t)
    ;$l = $ret[0]
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $m = ' & $m & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
    EndIf

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ;Auswertung:
    For $i = 1 To 4 ;array aus der struct füllen
    $arr3[$i - 1] = DllStructGetData($struct, 1, $i)
    $arr4[$i - 1] = DllStructGetData($struct, 4, $i)
    Next

    [/autoit] [autoit][/autoit] [autoit]

    $fsum = _modulo(_dot_product($arrC, $arr3), 1.0)

    [/autoit] [autoit][/autoit] [autoit]

    Dim $asum[4]
    $asum[0] = $arrC[0] / ($arr4[0] + 8)
    $asum[1] = $arrC[1] / ($arr4[1] + 8)
    $asum[2] = $arrC[2] / ($arr4[2] + 8)
    $asum[3] = $arrC[3] / ($arr4[3] + 8)

    [/autoit] [autoit][/autoit] [autoit]

    $cor = _sum($asum) / 16
    $ester = $k * $err
    $string = ""
    For $i = 2 To Int(-Log($cor) / $log16)
    If $i = $l Then $string &= "( "
    $fsum = 16 * Mod($fsum, 1.0)
    $string &= Hex(Int($fsum), 1) & " "
    Next
    If $i > $l And StringInStr($string, "(") Then $string &= ")"

    [/autoit] [autoit][/autoit] [autoit]

    MsgBox(0, $n + 1 & " -te hex-ziffer von Pi =", Hex(Int(16 * _Modulo(_dot_product($arrC, $arr3), 1.0)), 1) & @CRLF & @CRLF & "nächste Ziffern (incl.) = " & $string)

    [/autoit] [autoit][/autoit] [autoit]

    WEnd

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func _akt()
    ConsoleWrite('timerdiff($time) = ' & Int((TimerDiff($time) + 1) / 1000) & " k= " & $k & @CRLF) ;### Debug Console
    EndFunc ;==>_akt

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func _dot_product($a, $b)
    $vec = 0
    For $i = 0 To UBound($a) - 1
    $vec += $a[$i] * $b[$i]
    Next

    [/autoit] [autoit][/autoit] [autoit]

    Return $vec
    EndFunc ;==>_dot_product

    [/autoit] [autoit][/autoit] [autoit]

    Func _btest($a, $pos) ;bittest liefert TRUE, wenn das ($pos-te Bit von $a) = 1 ist
    ;http://gcc.gnu.org/onlinedocs/gcc-4.1.2/gfortran/BTEST.html
    $bit = BitAND(2 ^ 31 - 1, 2 ^ $pos)
    If BitAND($a, $bit) Then
    Return True
    Else
    Return False
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    EndFunc ;==>_btest

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func _modulo($a, $b) ;ir1 - floor(ir1/ir2)*ir2
    ;http://de.wikibooks.org/wiki/Fortran:_…nktionen#Modulo
    Return $a - Floor($a / $b) * $b
    EndFunc ;==>_modulo

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func _sum($e)
    $sum = 0
    For $i = 0 To UBound($e) - 1
    $sum += $e[$i]
    Next
    Return $sum
    EndFunc ;==>_sum

    [/autoit]


    Leider gibt es einen kleinen Pferdefuss, den ich bisher noch nicht lokalisieren konnte.
    Die hex-Ziffern werden leider nur bis zur ca. 550. Stelle richtig berechnet. Ab der 550. Stelle schleicht sich ein minimaler Fehler ein, der dazu führt, dass die folgenden Ergebnisse falsch werden.
    Ob das schon mit dem einfachen float-Format (32bit) zu tun hat, werde ich untersuchen.

    Jedenfalls ist die Geschwindigkeit sagenhaft, da ein komplettes $Array[4] in einem XMM-Register Platz hat und so alle Berechnungen für jeden Teilnehmer im Array durchgeführt werden.
    bsp Multiplikation zweier Arrays $a[4] und $b[4]:
    $a[0]=$a[0]*$b[0]
    $a[1]=$a[1]*$b[1]
    $a[2]=$a[2]*$b[2]
    $a[3]=$a[3]*$b[3]
    wird zu
    mulps xmm0,xmm1 ;jeweils gefüllt mit 4 floats
    und das wird in einer Handvoll Takte berechnet.

    Die millionste Stelle wird vom Script so in weit unter einer Sekunde ermittelt! Wobei man fairerweise zugeben muss, dass ab der ca. 5000. Stelle die Genauigkeit der floats sowieso nicht mehr ausreichend ist. Vielleicht weite ich das Script ja noch auf 64Bit Genauigkeit aus.....schaumamal^^

    ciao
    Andy


    "Schlechtes Benehmen halten die Leute doch nur deswegen für eine Art Vorrecht, weil keiner ihnen aufs Maul haut." Klaus Kinski
    "Hint: Write comments after each line. So you can (better) see what your program does and what it not does. And we can see what you're thinking what your program does and we can point to the missunderstandings." A-Jay

    Wie man Fragen richtig stellt... Tutorial: Wie man Script-Fehler findet und beseitigt...X-Y-Problem

    Einmal editiert, zuletzt von Andy (3. April 2011 um 09:14)