|
![]() |
|
Registriert seit: 17. Nov 2005 Ort: Hamburg 1.101 Beiträge Delphi XE2 Professional |
#1
![]() Um nach einem Hin und Herwandeln wieder wirklich auf (Bit)-Gleichheit prüfen zu können, verwende ich meine xRound Funktion.
Dann ist 0,47 wieder wirklich gleich 0,47. Und man kann sich "abs(a-b) < Toleranz" sparen. ![]() Die sieht schon vom Quelltext sehr optimierungsfähig aus. Darf ich Dich da nochmal bemühen ? Hast Du da auch noch eine wunderschöne Idee ? Vielen Dank nochmal !! ich hab mir mal ein paar Gedanken dazu gemacht, und das Resultat ist weiter unten. Um die vielen Divs und Mods zu vermeiden, hab ich das ganze in Assembler geschrieben. Die Suche nach doppelten Nullen / Neunen lasse ich in den 128 Bit breiten XMM-Registern erledigen - ohne Schleifen, ifs und thens.... Soweit ich weiß unterstützt AMD die verwendeten Befehle auch. Bei den von Dir aufgeführten Beispielen bringt meine Funktion identische Resultate. Die Funktion, ich habe sie (in Anlehnung an XRound) YRound genannt, hat die Parameter v:extended - entspricht Deinem avalue mindigs:integer - soviel Stellen nach dem Dezimalpunkt bleiben mindestens maxdigs:integer - soviel Stellen nach dem Dezimalpunkt bleiben höchstens fromleft:boolean - wenn True, dann wird die Suche nach doppelten Nullen / Neunen von links nach rechts durchgeführt, sonst, wie bei Dir, von rechts nach links. Anders als Deine Version arbeitet sie auch mit negativen Zahlen. mindigs und maxdigs müssen im Bereich 0..18 liegen, was nicht geprüft wird, ggfs. also in einer Exception endet.
Delphi-Quellcode:
FUNCTION YRound(v:extended; mindigs,maxdigs:integer; fromleft:boolean=false):extended;
const e18:int64=1000000000000000000; p10:array[0..18] of int64= (1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000, 10000000000,100000000000,1000000000000,10000000000000,100000000000000, 1000000000000000,10000000000000000,100000000000000000, 1000000000000000000); nine:array[0..1] of int64= ($9999999999999999,$9999999999999999); mask:array[0..19] of integer= ($000,$000,$100,$100,$180,$180,$1C0,$1C0,$1E0,$1E0, $1F0,$1F0,$1F8,$1F8,$1FC,$1FC,$1FE,$1FE,$1FF,$1FF); asm // --> Stack = V // --> EAX = mindigs // --> EDX = maxdigs // --> ECX = fromleft // <-- Gerundetes V //---------------------------------------------------------------------- jmp @Entry // Subroutine zum Ermitteln der Nachkommastellen @GetDigs: // BCD-Zahl in XMM0 und XMM1 movdqu xmm0,[esp+4] // +4 wegen Returnadresse movdqa xmm1,xmm0 // Bytes mit '00' vergleichen und Digits ermitteln pcmpeqb xmm0,xmm2 pmovmskb ebx,xmm0 // Ergebnisse in EBX call @GetDigs1 // Bytes mit '99' vergleichen und Digits ermitteln pcmpeqb xmm1,xmm3 pmovmskb ebx,xmm1 // Ergebnisse in EBX @GetDigs1: and ebx,edi // nur gültige Bits zulassen or ecx,ecx // fromleft js @GetDigs2 // ja bsf ebx,ebx // niedrigstes 1 Bit in EBX jnz @GetDigs3 ret @GetDigs2: bsr ebx,ebx // höchstes 1 Bit in EBX jz @GetDigs4 // alle 0 @GetDigs3: sub ebx,8 neg ebx add ebx,ebx // =Nachkommastellen add bx,cx // +1 wenn aus verschobener BCD cmp ebx,edx // weniger als bisher ? jae @GetDigs4 // nein mov edx,ebx // kleineren Wert merken @GetDigs4: ret //---------------------------------------------------------------------- // Register retten @Entry: push ebx push edi push esi // 16 Bytes auf Stack reservieren sub esp,16 // Nachkommateil als BCD-Zahl an [ESP] speichern fld v // st0=v fabs // st0=Abs(v); fld st(0) // st1=st0=v fld st(0) // st2=s1=st0=v fnstcw word [esp] // cw retten fnstcw word [esp+2] fwait or word [esp],$0F00 // Richtung 0 runden fldcw word [esp] frndint // st2=st1=v, st0=Trunc(v) fwait fldcw word [esp+2] // altes cw laden fsubp st(1),st(0) // st1=v, st0=Frac(v) fild e18 fmulp // st1=v, st0=Frac(v)*1e18 frndint // st1=v, st0=Round(Frac(v)*1e18) fbstp TByte [esp] // [ESP]=18stellige BCD-Zahl,st0=v // --------------------------------------------------------------- // An ESP steht jetzt Round(Frac(v)*1e18) als 18 stellige BCD-Zahl // In St(0) steht noch V für späteres Runden // --------------------------------------------------------------- // XMM2 mit Nullen und XMM2 mi Neunen füllen pxor xmm2,xmm2 movdqu xmm3,nine // Digits ermitteln auf Basis der BCD-Zahl ror ecx,1 // ECX Bit 0 in Bit 31 lea edi,mask mov esi,[edi+edx*4] // Bitmaske wenn verschobene Zahl geprüft wird mov edi,[edi+edx*4+4] // Bitmaske wenn BCD-Zahl geprüft wird call @GetDigs // BCD-Zahl um 1 Nibble nach oben verschieben mov ebx,[esp+4] shld [esp+8],ebx,4 mov ebx,[esp] shld [esp+4],ebx,4 shl [esp],4 // Digits ermitteln auf Basis der verschobenen BCD-Zahl mov edi,esi // Bitmaske für verschobene BCD-Zahl add ecx,1 call @GetDigs // Prüfen ob mindigs unterschritten, ggfs. korriggieren cmp edx,eax // Nachkommastellen < mindigits ? jae @1 // nein mov edx,eax // mindigits verwenden @1: // Und jetzt v runden auf EDX Nachkommastellen // Pascal : Teiler := power(10, Nachkommastellen); // result := round(aValue * Teiler) / Teiler; // v schlummert noch in st0 fild QWord [p10+edx*8] // st1=v, st0=10^EDX fmulp st(1),st(0) // st0=v*10^EDX frndint // st0=Round(v*10^EDX) fild QWord [p10+edx*8] // st1=Round(v*10^EDX), st0=10^EDX fdivp st(1),st(0) // st0=result=Round(v*10^EDX)/10^EDX bt Word [ebp+16],15 // war v negativ jnc @2 // nein fchs // st0=-st0 @2: // Stack freigeben add esp,16 // Register auf alten Stand bringen pop esi pop edi pop ebx end; Der Test der von Dir aufgeführten Beispiele bringt identische Ergebnisse, mal abgesehen von dem Zeitbedarf. Die vorletzte Spalte zeigt die CPU-Ticks für Deine Version, die letzte Spalte die Ticks für die obige Version.
Code:
Ich habe nicht ausführlich getestet - würde ich vor Verwendung dringend empfehlen.
CPU-Ticks
Zahl XRound YRound XRound YRound ---------------- ------ ------ ----- ------ 115.859997410327 115.86 115.86 7772 1096 100.99 100.99 100.99 7440 924 100 100 100 1392 1040 101 101 101 1396 916 109 109 109 1520 908 100.4 100.4 100.4 5588 916 100 100 100 1560 916 126.61999511719 126.62 126.62 7360 1064 0.0099999997765 0.01 0.01 4496 1036 115.089999999776 115.09 115.09 5692 1016 99.99999997765 100 100 6892 1012 99.99999997765 100 100 6536 1052 0.99999997765 1 1 6488 932 99.09999997765 99.1 99.1 5248 956 99.0199999997765 99.02 99.02 4160 920 99.0100001245678 99.01 99.01 3976 1008 115.860000610352 115.86 115.86 6516 920 115.859997410327 115.86 115.86 6424 964 115.859974103278 115.86 115.86 6148 984 115.85074103278 115.85074103278 115.85074103278 7056 1004 0.00999999977648 0.01 0.01 4500 1084 115.790000915527 115.79 115.79 6800 920
Gruß, Klaus
Die Titanic wurde von Profis gebaut, die Arche Noah von einem Amateur. ... Und dieser Beitrag vom Amateurprofi.... |
![]() |
Ansicht |
![]() |
![]() |
![]() |
ForumregelnEs ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.
BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus. Trackbacks are an
Pingbacks are an
Refbacks are aus
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
![]() |
![]() |