Warum klappt Binary bei mir nicht
-
@Th69 sagte in Warum klappt Binary bei mir nicht:
sowie die Originalschleife aus
Ich glaube, dort ist auch eine kleine Unkorrektheit
static unsafe bool MemCmp(byte[] a1, byte[] a2, int length) { if (a1 == null || a2 == null || a1.Length != a2.Length || a1.Length < length) return false; fixed (byte* p1 = a1, p2 = a2) { byte* x1 = p1, x2 = p2; int l = a1.Length; for (int i = 0; i < l / 8; i++, x1 += 8, x2 += 8) if (*((long*)x1) != *((long*)x2)) return false; if ((l & 4) != 0) { if (*((int*)x1) != *((int*)x2)) return false; x1 += 4; x2 += 4; } if ((l & 2) != 0) { if (*((short*)x1) != *((short*)x2)) return false; x1 += 2; x2 += 2; } if ((l & 1) != 0) if (*((byte*)x1) != *((byte*)x2)) return false; return true; } }
Das wird mit int l = a1.Length; die Länge von a1 benutzt, um später die zu vergleichende Restlänge zu bestimmen.
Wenn a1.Length > length ( zu vergleichende Länge bei großen Dateien ein Zwischenblock, der meistens ein Vielfaches von 16 lang ist) ist, dann muss die Restlänge aus length und nicht aus l bestimmt werden. Das war bei Deiner Version richtig.
-
Den
length
-Parameter habe ich ja hinzugefügt, und die Bedingung entsprechend geändert.Ich habe aber jetzt mal meine Variante abgewandelt (und noch ca. 0.2s Performance herausgeholt: 10.2s statt vorher 10.4s):
static unsafe bool MemCmp(byte[] a1, byte[] a2, int length) { if (a1 == null || a2 == null || a1.Length < length || a2.Length < length) return false; fixed (byte* p1 = a1, p2 = a2) { long* x1 = (long*)p1, x2 = (long*)p2; long* x0 = x1 + length / sizeof(long); for (; x1 < x0; x1++, x2++) if (*x1 != *x2) return false; byte* b1 = (byte*)x1, b2 = (byte*)x2; const int SizeInt = sizeof(int); if ((length & SizeInt) != 0) { if (*(int*)b1 != *(int*)b2) return false; b1 += SizeInt; b2 += SizeInt; } const int SizeShort = sizeof(short); if ((length & SizeShort) != 0) { if (*(short*)b1 != *(short*)b2) return false; b1 += SizeShort; b2 += SizeShort; } if ((length & 1) != 0) return *b1 == *b2; return true; } }
PS:
Was ich die ganze Zeit schon schreiben wollte:
Diese Methode geht davon aus, daß die übergebenenbyte
-Arrays immer auf einerlong
-Adresse beginnen (da sie ja vom OS bereitgestellt werden).
In der nativenmemcmp
wird jedoch auch überprüft, ob die Anfangsadresse "ungerade" ist, s. z.B. memcmp.c.
-
@Th69
jetzt ist das ja alles korrekt und sauber programmiert, falls int mal 20 Bytes lang wird in der übernächsten Generation.
Ich habe mal versucht, auch noch decimal einzubinden ( 16 Bytes statt long=8 Bytes), das klappt, es dauert aber länger.
-
Decimal
ist kein nativer Prozessor-Datentyp, daher hier nicht zu gebrauchen.
-
@Th69
bei c++ gibt es long long, das kennt C# wohl (noch) nicht.So ist es bei Delphi
Ausrichtung von strukturierten Typen
Per Voreinstellung sind die Werte in einem strukturierten Typ zur Beschleunigung des Zugriffs an Word- oder Double-Word-Grenzen ausgerichtet.Ich habe mir im Debugger die Adressen angeschaut, sie haben in der letzten Stelle eine 8, sind demnach auf long-Grenzen ausgerichtet.
-
In C# gibt´s Int64 und UInt64.
-
Bei C++ sind die Datentypen aber nur mit Mindestgrößen definiert und je nach Plattform unterschiedlich groß. Aber bei einem 64Bit System macht es keinen Sinn noch größere Datentypen zum iterieren zu verwenden (denn C#
long
=Int64
).
Und noch größere Prozessor-Adressbusbreiten (für PCs) machen keinen Sinn, denn 18 Trillionen (~ 18 Mrd. GB) hat man auch in weiterer Zukunft wohl nicht als RAM zur Verfügung.
-
@Th69
es geht ja nicht nur um Adressen, sondern um Daten.
Ich habe vor Jahren ein Programm zur Ermittlung der IBAN aus Konto und Bankleitzahl geschrieben.
Um die Prüfziffer zu berechnen muss man Modulo rechnen mit sehr langen Zahlen, die hatte ich damals in Delphi nicht.
Jetzt gibt es in C# BigInteger, damit war das problemlos möglich.
-
Ich hoffe doch, du speicherst Bankleitzahlen & Co. nicht als Integer. Das sind Ziffernfolgen, keine Zahlen!
-
@hkdd sagte in Warum klappt Binary bei mir nicht:
Um die Prüfziffer zu berechnen muss man Modulo rechnen mit sehr langen Zahlen, die hatte ich damals in Delphi nicht.
Lange Zahlen braucht man nicht. In einem Produkt
(a*b*c) % m
kann man das Modulo nach jedem einzelnen Schritt auswerten. Ist also dasselbe wie((((a%m)*(b%m))%m) * c) % m
-
@wob
das kenne ich, bei meinem Delphi-Programm von knapp 10 Jahren sah es z.B. so ausProcedure MakePZ; Label ExUP; Var sBBAN : Str254; iBBAN : Int64; iPZ : Integer; rc : Integer; Begin // sBBAN := sBLZ+sKTO+'131400'; // 8st + 10st + 6st = 24st BBAN BLZ KTO 1314(DE) 00(PZ) sBBAN := sBLZ+sKTO; {$R-} Val(sBBAN,iBBAN,rc); if rc <> 0 then Begin sIBAN := 'Fehler bei VAL'; goto ExUP; End; While iBBAN > 970 do Begin if iBBAN > 970000000000000000 then iBBAN := iBBAN - 970000000000000000 else if iBBAN > 97000000000000000 then iBBAN := iBBAN - 97000000000000000 else if iBBAN > 9700000000000000 then iBBAN := iBBAN - 9700000000000000 else if iBBAN > 970000000000000 then iBBAN := iBBAN - 970000000000000 else if iBBAN > 97000000000000 then iBBAN := iBBAN - 97000000000000 else if iBBAN > 9700000000000 then iBBAN := iBBAN - 9700000000000 else if iBBAN > 970000000000 then iBBAN := iBBAN - 970000000000 else if iBBAN > 97000000000 then iBBAN := iBBAN - 97000000000 else if iBBAN > 9700000000 then iBBAN := iBBAN - 9700000000 else if iBBAN > 970000000 then iBBAN := iBBAN - 970000000 else if iBBAN > 97000000 then iBBAN := iBBAN - 97000000 else if iBBAN > 9700000 then iBBAN := iBBAN - 9700000 else if iBBAN > 970000 then iBBAN := iBBAN - 970000 else if iBBAN > 97000 then iBBAN := iBBAN - 97000 else if iBBAN > 9700 then iBBAN := iBBAN - 9700 else if iBBAN > 970 then iBBAN := iBBAN - 970; End; // While iBBAN > 970 iBBAN := (iBBAN * 1000000) + 131400; While iBBAN > 96 do Begin if iBBAN > 970000000 then iBBAN := iBBAN - 970000000 else if iBBAN > 97000000 then iBBAN := iBBAN - 97000000 else if iBBAN > 9700000 then iBBAN := iBBAN - 9700000 else if iBBAN > 970000 then iBBAN := iBBAN - 970000 else if iBBAN > 97000 then iBBAN := iBBAN - 97000 else if iBBAN > 9700 then iBBAN := iBBAN - 9700 else if iBBAN > 970 then iBBAN := iBBAN - 970 else if iBBAN > 96 then iBBAN := iBBAN - 97; End; // While iBBAN > 96 iPZ := 98 - iBBAN; Str(iPZ:2,sPZ); While Length(sPZ) < 2 do sPZ := '0' + sPZ; // Vornull sIBAN := 'DE' + ' ' + sPZ + ' ' + sBLZ + ' ' + sKTO; ExUP: Form1.edIBAN.Text := sIBAN; End;
Und im C# Programm unter Nutzung von BigInteger sieht es so aus:
//=========================== // Prüfziffer erstellen //=========================== void MakePZ() { // sBBAN = sBLZ+sKTO+'131400'; // 8st + 10st + 6st = 24st BBAN BLZ KTO 1314(DE) 00(PZ) string sBBAN = sBLZ + sKTO + "1314" + "00"; // D E PZ=Prüfziffer 00 BigInteger iBBAN = (BigInteger.Parse(sBBAN, NumberStyles.Integer, CultureInfo.CurrentCulture)); BigInteger q; // Quotient BigInteger r; // Rest BigInteger d = 97; // Divisor q = BigInteger.DivRem(iBBAN, d, out r); // Modulo 97 string sRest = r.ToString(); // Rest int iRest = int.Parse(sRest); // Rest sPZ = (98 - iRest).ToString(); while (sPZ.Length < 2) { sPZ = "0" + sPZ; } // ggf Vornull ergänzen string s = "DE" + sPZ + sBLZ + sKTO; // IBAN 22-stellig sIBAN = " " + s.Substring(00, 4) + " " + s.Substring(04, 4) + " " + // IBAN aufbereitet s.Substring(08, 4) + " " + s.Substring(12, 4) + " " + s.Substring(16, 4) + " " + s.Substring(20, 2); edIBAN.Text = sIBAN; }
-
@SeppJ sagte in Warum klappt Binary bei mir nicht:
Ich hoffe doch, du speicherst Bankleitzahlen & Co. nicht als Integer. Das sind Ziffernfolgen, keine Zahlen!
Also doch wie befürchtet. Du speicherst Bankleitzahlen & Co. als Integer.
Ganz schlecht.
Es sind schon ungeheuer große Schäden dadurch verursacht worden, weil man bei wichtigen Programmen die billigsten Programmierer angeheuert hat, und dann Rechnungen nicht bezahlt wurden, Briefe nicht ankamen, Anrufe an die falsche Nummer gingen, …Starke Anhaltspunkte dafür, ob etwas eine Zahl oder eeine Ziffernfolge ist:
- Es macht keinen Unterschied, ob ich sie in Hexadezimal, base69, oder binär angebe -> Zahl
- Die Differenz zwischen zwei Objekten ist wieder ein sinnvolles Objekt -> Zahl
- Führende Nullen sind signifikant -> Ziffernfolge
Jeweils mit gültigem Umkehrschluss.
Bankleitzahlen, IBANs, Telefonnummern, Postleitzahlen, etc. haben jeweils 3/3 der Indikatoren für Ziffernfolge. (Weil sie es sind!)
PS: Modulo einer Ziffernfolge, falls du dir das aus dem Beitrag von wob nicht erschließen konntest:
modulo(ziffernfolge, divisor): ergebnis = 0 für jede ziffer in der ziffernfolge: ergebnis = (ergebnis * 10 + numerischer Wert der Ziffer) modulo divisor; return ergebnis
Ein bisschen einfacher, als dein Monster, oder?
-
@SeppJ sagte in Warum klappt Binary bei mir nicht:
Bankleitzahlen & Co. als Integer.
In diesem Programm geht es darum, aus einer BLZ und KTO-Nummer die IBAN zu ermitteln und dort insbesondere die Prüfziffer. Dafür gibt es eine Formel, wie man das machen muss. Da wird nichts gespeichert und es gibt auch keine Unsicherheiten irgendwelcher Art.
Am Bildschirm gibst LZ und KTO-Nummer ein und es wird die zugehörige IBAN angezeigt, das ist wie ein Taschenrechner.
Die lange 24-stelligen Zahl muss / 97 dividiert werden - und das geht mit INT64 nicht.
Da muss man eben immer wieder 97 bzw. ein Vielfaches davon subtrahieren, um den Rest zu ermitteln, den man für die Prüfziffer braucht.
-
Das hat @SeppJ doch geschrieben. Der Modulo ändert sich nicht, auch wenn es einen Überlauf bei der
int
-Zahl gibt.Hier auch ein Code in C#: IBAN-Validator (auch wenn man statt
int.Parse(...)
einfachcheckSumString[i] - '0'
benutzen sollte).
-
Ist schon irgendwie lustig, wenn du darauf hinweist, wieso du für das Modulo 97 Integer nehmen willst, wenn die letzten 3 Beiträge davon handeln, wieso man das nicht braucht. Mein Beispiel nutzt sogar "zufällig" 97 als Divisor.
-
@Th69 sagte in Warum klappt Binary bei mir nicht:
Der Modulo ändert sich nicht, auch wenn es einen Überlauf bei der int-Zahl gibt.
What? Doch!
uint8_t u8 = 240; cout << u8 % 17 << '\n'; u8 += 17; cout << u8 % 17 << '\n';
Vergleiche mit:
uint16_t u16 = 240; cout << u16 % 17 << '\n'; u16 += 17; cout << u16 % 17 << '\n';
-
@SeppJ
Ja, das geht wirklich einfach. Es kommt die richtige IBAN heraususing System; namespace IbanTest { internal class Program { static void Main(string[] args) { // BLZ+++++ KTO####### D E 00 string k = "11111111"+"2222222222"+"1314"+"00"; int e = 0; for (int i=0; i < k.Length; i++) { e = (e * 10 + int.Parse(k[i] + "")) % 97; } string pz = (98-e).ToString("00"); Console.WriteLine("BLZ="+k.Substring(0,8)+ ",KTO="+k.Substring(8,10)+ " => IBAN="+"DE"+ pz + k.Substring(0,18)); Console.ReadLine(); } } }
-
@wob: Da habe ich mich wohl etwas falsch ausgedrückt.
Ich meinte beim 'vermeintlichen Überlauf`: da ja immer wieder der Modulo benutzt wird, bleibt die Zahl klein und es können somit beliebig lange Ziffernfolgen bearbeitet werden.So wie ich es geschrieben habe, hast du natürlich mit deinem Code Recht - das würde zu einem falschen Ergebnis führen.
Schon das 2. Mal jetzt, daß ich nicht richtig überlegt habe - ich werde wohl alt.
-
@hkdd
Ah, ja. "kernel32.dll" ist vermutlich auch falsch. Sorry. Probier mal meine Variante nur mit "ntdll.dll". Das sollte passen.long
ist auf jeden Fall nicht korrekt. Wenn das Programm als 64 Bit läuft wird es zufällig funktionieren. Als 32 Bit Prozess aber ... nicht. Was auch erklären würde dass er haufenweise Unterschiede meldet - weil der Rückgabewert halt total falsch ist. MitIntPtr
sollte es bei 32 und 64 Bit passen.
-
@hustbaer
ich habe jetzt auch mal sehr große Videodateien verglichen, da sind die Puffer-Vergleiche von Th69 und meine ASM-DLL die schnellsten. Sie kommen nahe an mein Delphi-Programm heran.
Die Methode von Th69 hat den Charm, dass man kein DllImport und damit keine Zusatzdateien zur EXE braucht, also alles läuft C# intern.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CompHKw - Dateien Vergleichen - (C) 07.09.2022 Hartmut, Dresden ----------------------------------------------------------------- Parameter: "N:\VideosN" "N:\VideosN" "/U" "/VJ" "/F" "/M:2" "/A" ----------------------------------------------------------------- Vergleich: N:\VideosN\ mit: N:\VideosN\ ~~~~~~~~~~~~~~~~~~~~~~~ 2.446.Datei: N:\VideosN\Youtube-HK\...den 1971\zz09 So scheid ich denn (Finale).mp4 ================================================================= 82 Unter-Pfade gefunden - identisch. 2.446 Dateien gefunden - und verglichen... 2.446 Dateien verglichen - identisch. Es wurden 208.144.569.612 Bytes = 203.266.181 K-Bytes verglichen Start 18:42:22 Ende 19:34:59, Dauer = 00:52:37 ========================================================<Ende>=== Linke MausTaste / ESC = Schließen Rechte MausTaste / F1 = Info ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Start = 08.12.2022 18:30:45 CompCs Vergleichsmethode = unsafe MemCmp von Th69 MitRead=True, MitComp=True 16.777.216 Bytes = Puffergröße 208.144.569.612 Bytes eingelesen 83 Directories 2.446 Dateien 14.071 Puffer 2.446 identische Dateien Ende = 08.12.2022 19:28:56, Dauer = 00:58:11.2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Start = 08.12.2022 19:45:03 CompCs Vergleichsmethode = CompareByteArray (C++ ASM DLL von HK) MitRead=True, MitComp=True 16.777.216 Bytes = Puffergröße 208.144.569.612 Bytes eingelesen 83 Directories 2.446 Dateien 14.071 Puffer 2.446 identische Dateien Ende = 08.12.2022 20:38:14, Dauer = 00:53:11.7 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~