Moin,
Das eigentliche Thema wurde bereits zufriedenstellend gelöst (eine sehr Schnelle Methode ist in Post 15 zu finden), nun gibt es leider ein weiteres Problem mit Dezimal und Dualzahlen (oder Hexadezimalzahlen).
-----------------------------------------------------------------------------------------------
Ich stehle hiermit mal meinen eigenen Thread und ändere das Thema leicht ab.
Es geht jetzt um Folgendes:
Suche (schnelle) Funktion zum umrechnen ins Dual- oder Hexadezimalsystem
Problemstellung:
Mein Arithmetischer Kodierer spuckt mir einen String im Dezimalsystem entgegen. Dieser String enthält je nach Input hunderte bis zehntausende Zahlen. Um diesen Spaß anständig zu speichern wird er im Dualsystem gebraucht. (Der Header der daraus entstehenden "Datei" ist im Dualsystem, da einzelne Bits entscheidend sind, die Kodierung der Wahrscheinlichkeiten ist im Dualsystem usw usw. Also muss auch der kodierte String ins Dualsystem. Dann schön alles zusammensetzen und als Binary zurückgeben)
Wie funktioniert soetwas ?
Der Ansatz mit den hier diskutierten Methoden läuft jedenfalls nicht, da man nicht jedes Mal die komplette endlos lange Dezimalzahl durch 2 Teilen kann. Die Subtraktionsmethode fällt ebenfalls flach, da man nicht immer mit so enormen Zahlen potenzieren kann und anschließend 2 riesige Zeichenketten voneinander abziehen kann. Die restlichen Methoden bauen darauf auf die Dezimalzahl erstmal per Hex zu verarbeiten, sodass weitere Schritte sehr einfach sind; läuft bei großen Zahlen auch nicht.
Ich bitte daher um einen Ansatz, wie man eine große Dezimalzahl stück für Stück umwandeln kann.
Edit:
Funktion für kleine Zahlen (< 2^31)
Global $a_[256], $b_[256], $c_[11111112]
[/autoit] [autoit][/autoit] [autoit]_DecToBin_Startup()
[/autoit] [autoit][/autoit] [autoit]Global $sDec = '123456789'
Global $sBin = _DecToBin($sDec)
Global $sDec2 = _BinToDec($sBin)
ConsoleWrite('Original: ' & $sDec & @CRLF & 'Binär: ' & $sBin & @CRLF & 'Dezimal: ' & $sDec2 & @CRLF)
[/autoit] [autoit][/autoit] [autoit]; <Func>--------------------------------------------------|
; Wandelt eine Dualzahl in eine Dezimalzahl um |
; --------------------------------------------------------|
Func _BinToDec($s)
Local $l = StringLen($s)
Return $l<9?$c_[$s]:$l<17?$c_[StringTrimRight($s,8)]*256+$c_[StringRight($s,8)]:$l<25?$c_[StringTrimRight($s,16)]*65536+$c_[StringRight(StringTrimRight($s,8),8)]*256+ $c_[StringRight($s,8)]:BitShift($c_[StringTrimRight($s,24)],-24)+$c_[StringRight(StringTrimRight($s,16),8)]*65536+$c_[StringRight(StringTrimRight($s,8),8)]*256+$c_[StringRight($s,8)]
EndFunc
; <Func>--------------------------------------------------|
; Ermittelt eine Dualzahl (0101) aus einer Dezimalzahl |
; Die Dezimalzahl muss kleiner als 2^31 sein |
; --------------------------------------------------------|
Func _DecToBin($d)
Return $d<256?$a_[$d]:$d<65536?$a_[$d/256]&$b_[BitAND($d,255)]:$d<16777216?$a_[$d/65536]&$b_[BitAND($d/256,255)]&$b_[BitAND($d,255)]:$a_[BitShift($d,24)]&$b_[BitAND($d/65536,255)]&$b_[BitAND($d/256,255)]&$b_[BitAND($d,255)]
EndFunc
; <Func>--------------------------------------------------|
; Initialisiert die DecToBin UDF |
; --------------------------------------------------------|
Func _DecToBin_Startup()
Local $t = DllStructCreate('char[64]'), $p = _
DllStructGetPtr($t), $hDll = DllOpen('msvcrt.dll')
For $i = 0 To 255 Step 1
DllCall($hDll, 'ptr:cdecl', '_i64toa', 'int64', _
$i, 'ptr', $p, 'int', 2)
$a_[$i] = DllStructGetData($t, 1)
$b_[$i] = StringRight('0000000' & $a_[$i],
$c_[$a_[$i]] = $i
Next
DllClose($hDll)
EndFunc
Funktion für beliebig große Strings
; Sämtlicher Code ist von Make-Grafik.
Global $sDec = '1234567890123456789012345678901234567890'
Global $sHex = _Big_DecToHex($sDec)
Global $sDec2 = _Big_HexToDec($sHex)
ConsoleWrite('Original: ' & $sDec & @CRLF & 'Hexadezimal: ' & StringTrimLeft($sHex, 2) & @CRLF & 'Dezimal: ' & $sDec2 & @CRLF)
[/autoit] [autoit][/autoit] [autoit]Func _Big_DecToHex($sInteger)
Local $i, $bp, $carry, $n, $aiInt[1], $product
For $i = 1 To StringLen($sInteger)
$bp = 0
$carry = StringMid($sInteger, $i, 1)
For $n = 0 To UBound($aiInt) -1
$product = $aiInt[$n] & $carry
$aiInt[$bp] = BitAND($product, 0x00FF)
$carry = BitAND($product, 0xFF00) / 0x0100
$bp += 1
Next
If $carry Then
ReDim $aiInt[$bp +1]
$aiInt[$bp] = $carry
EndIf
Next
Return StringToBinary(StringFromASCIIArray($aiInt, 0, Default, 1))
EndFunc
Func _Big_HexToDec($aiInt)
$aiInt = StringToASCIIArray(BinaryToString($aiInt), 0, Default, 1)
Local $carry, $i, $product, $sInteger
Local $index = UBound($aiInt) -1
Do
$carry = StringRight($aiInt[$index], 1)
$aiInt[$index] = Floor($aiInt[$index] / 10)
For $i = $index -1 To 0 Step -1
$product = $carry * 0x0100 + $aiInt[$i]
$aiInt[$i] = Floor($product / 10)
$carry = StringRight($product, 1)
Next
$sInteger &= $carry
If Not $aiInt[$index] Then
ReDim $aiInt[$index]
$index -= 1
EndIf
Until $index = -1
Return StringReverse($sInteger)
EndFunc
Eine ASM Version von DecToBin (kann auch in Hex oder Str umwandeln) gibts von eukalyptus in Post36
lg
M
-----------------------------------------------------------------------------------------------
Originaler Inhalt des #1 Post
Moin
Geschwindigkeit ist wie immer König im Haus.
Es geht um die Umrechnung von 32Bit Ganzzahlen (int) ins Dualsystem (0101010)
Aus Wikipedia habe ich die Divisions und die Subtraktions-Methode angesehen und festgestellt, dass diese in AutoIt auch wunderbar funktionieren.
Problematisch wird es nun bei wirklich großen Zahlen. Da dauert der Aufruf schonmal 0.14 ms wenn man sich auf diese Methoden stützt.
Also habe ich etwas gebastelt und eine Methode entdeckt, mit der der Spaß immer nahezu konstant in 0.08ms erledigt wird. (Fast doppelt so schnell)
(logisch, eigentlich sind die anderen Methoden weitaus schneller, aber in AutoIt ist die Welt eben manchmal verkehrt^^)
Bei kleinen Zahlen ist diese Methode den Wikipediamethoden weit unterlegen (Faktor ca. 4:1), bei großen Zahlen aber überlegen.
Jetzt interessiert es mich, falls jemand hier ab und zu mal sowas nutzt, welche Funktion für die Umwandlung benutzt wird und wie schnell diese ist.
Gesucht wird die schnellste Funktion die eine beliebige 32Bit Int Zahl ins Dualsystem (010101) bringt.
Dabei muss keine Rücksicht auf nativen AutoIt Code genommen werden. Sollte es eine Dll-Func geben die ich nicht kenne bin ich natürlich erfreut mit dieser Bekanntschaft zu machen
Code
_Main()
[/autoit] [autoit][/autoit] [autoit]Func _Main()
[/autoit] [autoit][/autoit] [autoit]Local $a[3], $t, $r, $Zeitbedarf[3], $Versuche = 5000, $Schritte = 0
Local $Excel = False
If $Excel Then Sleep(5000)
[/autoit] [autoit][/autoit] [autoit]While $Schritte < 2 ^ 31 - 1
ConsoleWrite('Zahl: ' & $Schritte & @CRLF)
If $Excel Then
Send($Schritte, 1)
Sleep(100)
Send('{RIGHT}')
Sleep(100)
EndIf
For $i = 0 To $Versuche Step 1
$r = $Schritte
$t = TimerInit()
$a[0] = _DecToBin_1($r) ; Divisionsmethode
$t = TimerDiff($t)
$Zeitbedarf[0] += $t
$t = TimerInit()
$a[1] = _DecToBin_2($r) ; Subtraktionsmethode
$t = TimerDiff($t)
$Zeitbedarf[1] += $t
$t = TimerInit()
$a[2] = _DecToBin_3($r) ; Ausgedacht 1
$t = TimerDiff($t)
$Zeitbedarf[2] += $t
If ($a[0] <> $a[1]) Or ($a[0] <> $a[2]) Then
ConsoleWrite('Error: ' & @CRLF & 'Int: ' & $r & @CRLF & ' 1.Bin: ' & $a[0] & @CRLF & ' 2.Bin: ' & $a[1] & @CRLF & ' 3.Bin: ' & $a[2])
EndIf
Next
If $Excel Then
Send(Round(($Zeitbedarf[0] / $Versuche) * 1000, 0), 1)
Sleep(100)
Send('{RIGHT}')
Sleep(100)
Send(Round(($Zeitbedarf[1] / $Versuche) * 1000, 0), 1)
Sleep(100)
Send('{RIGHT}')
Sleep(100)
Send(Round(($Zeitbedarf[2] / $Versuche) * 1000, 0), 1)
Sleep(100)
Send('{ENTER}')
Sleep(100)
Send('{LEFT}')
Sleep(100)
Send('{LEFT}')
Sleep(100)
Send('{LEFT}')
Sleep(100)
Else
ConsoleWrite('Func 1: ' & Round($Zeitbedarf[0] / $Versuche, 5) & ' ms' & @CRLF & 'Func 2: ' & Round($Zeitbedarf[1] / $Versuche, 5) & ' ms' & @CRLF & 'Func 3: ' & Round($Zeitbedarf[2] / $Versuche, 5) & ' ms' & @CRLF)
EndIf
$Zeitbedarf[0] = 0
$Zeitbedarf[1] = 0
$Zeitbedarf[2] = 0
$Schritte += 1
$Schritte *= 1.5
$Schritte = Int($Schritte)
WEnd
EndFunc ;==>_Main
Func _DecToBin_1($iDec) ; Divisionsmethode
Local $sRet
While Not $iDec = 0
$iDec /= 2
If IsInt($iDec) Then
$sRet = '0' & $sRet
Else
$sRet = '1' & $sRet
$iDec -= 0.5 ; Ohne den Rest gehts weiter
EndIf
WEnd
If Not $sRet Then Return '0'
Return $sRet
EndFunc ;==>_DecToBin_1
Func _DecToBin_2($iDec) ; Subtraktionsmethode
Local $sRet
$iDec += 1
For $i = Ceiling(Log($iDec) / Log(2)) To 0 Step -1
If $iDec - 2 ^ $i > 0 Then
$sRet &= '1'
$iDec -= 2 ^ $i
Else
If $sRet Then $sRet &= '0'
EndIf
Next
If Not $sRet Then Return '0'
Return $sRet
EndFunc ;==>_DecToBin_2
Func _DecToBin_3($iDec) ; Ausgedachte Methode 1
Local $sHex = Hex($iDec), $sRet, $aBin[16] = ['0000', '0001', '0010', '0011', '0100', '0101', '0110', '0111', '1000', '1001', '1010', '1011', '1100', '1101', '1110', '1111']
While StringLeft($sHex, 1) = '0'
$sHex = StringTrimLeft($sHex, 1)
WEnd
For $i = 1 To StringLen($sHex) Step 1
$sRet &= $aBin[Int('0x' & StringMid($sHex, $i, 1))]
Next
While StringLeft($sRet, 1) = '0'
$sRet = StringTrimLeft($sRet, 1)
WEnd
If Not $sRet Then Return '0'
Return $sRet
EndFunc ;==>_DecToBin_3
Anbei eine kleine Tabelle mit einem Geschwindigkeitsvergleich.
Hier drin
[Blockierte Grafik: http://i.imgur.com/QEjhm.png]
lg
Mars(i)
-----------------------------------------------------------------------------------------------