|
![]() |
|
Registriert seit: 17. Nov 2005 Ort: Hamburg 1.100 Beiträge Delphi XE2 Professional |
#1
@Solutor:
![]() Korrekt! Meine Funktion hat nur einen eingeschränkten Gültigkeitsbereich.
![]() Doch ein umfangreicherer Gültigkeitsbereich wäre nur für
astronomische Anwendungen interessant. Für die Anwendungen im täglichen Leben, spielt es keine Rolle. Stell dir vor du sitzt bei Günter Jauch und kriegst als 1 Mio € Frage "Wann wurde Humphrey Bogart geboren" und gefragt ist das genaue Datum. Mit deinem vorletzten Joker kriegst du aus dem Publikum die Information dass der am letzten Montag in 1899 geboren wurde, was dir aber so auch nicht hilft. Mit dem letzten Joker rufst du jemanden an der Deine Funktion auf dem Rechner hat, und der sagt dir "26.12.1899". Und futsch ist die Million, und warum sie futsch ist, erkläre ich etwas weiter unten. ![]() Der Ansatz sämtliche Formularien selbst zu kreieren ist natürlich sinnvoll, wenn man auf Geschwindigkeit optimiert
Und deshalb habe ich weiter unten eine weitere Version, die noch einmal fast doppelt so schnell ist, wie die aus #12. ![]() der nächste Schritt wäre dann Teile in Assembler abzuarbeiten.
Und die ist noch einmal fas doppelt so schnell. ![]() Bei meinem Ansatz ging es darum, mit der in Delphi vorhandenen Funktionalität, ohne tiefere Kenntnisse der mathematischen Hintergründe, das Problem anzupacken, ohne dabei Schleifenkonstrukte verwenden zu müssen.
![]() Damit ist diese Funktion immer noch mindestens um den Faktor 100 schneller, als die gleiche Lösung in Excel.
Da mich dieses Thema und die damit verbundenen Probleme interessierten, habe ich mir alle Funktionen aus diesem Thread genauer angeschaut: LastThursday aus #5 Funktioniert überhaupt nicht. Die Funktion liefert immer den 28. des Monats als letzten Donnerstag. Richtige Ergebnisse kommen bestenfalls zufallsbedingt, wie zum Beispiel im Dezember 2017. Warum ist das so: Mit DaysInAMonth(Year, Month) wird die Anzahl der Tage im Monat geholt und in die Variable Day gestellt. Anschließend wird Day so lange um 1 gesenkt, bis (Day+5) mod 7 = 5 ist. Das ist sehr zuverlässig immer dann der Fall, wenn Day = 28 ist. Das Ergebnis wird dann mit EncodeDate(Year,Month,Day) errechnet. Vermutlich hat der Autor erwartet, dass (Day+5) mod 7 den Wochentag ergibt, was aber nicht stimmt. LetzterDonnerstag aus #6 Arbeitet korrekt für den Zeitraum 01.0001 bis 11.9999 Bei 12.9999 erfolgt eine Exception, weil mit IncMonth(AYear,AMonth,1) versucht wird einen TDateTime für den ersten Tag des Folgemonats zu holen. Das scheitert, weil TDateTime nur bis 31.12.9999 23:59:59.999 unterstützt wird. Performance: 2777 ms für 11997600 Durchläufe = 231.5 µs je Durchlauf GetLastWeekDayOfMonth aus #7 Arbeitet korrekt für den Zeitraum 01.0001 bis 12.9999, ausgenommen für Montag bis Freitag im Dezember 1899. Ursache für die Fehler ist, dass die Funktion für den letzten Tag des Monats nicht einen Datumswert sondern einen Zeitpunkt verwendet, nämlich 23:59:59 am letzten Tag des Monats. Beispiel für Freitag im Dezember 1899: Der letzte Tag des Monats ist ein Sonntag und es wird richtig erkannt, dass das Datum vom letzten Tag des Monats um 2 gesenkt werden muss. Hierfür wird mit System.DateUtils.EndOfAMonth(yyyy,mm) das Ende des Monats geholt und dieser Wert um 2 gesenkt. Das Ende des Monats (31.12.1899 23:59:59) ist 1.999999988425930 Hiervon 2 subtrahiert ergibt -0.000000011574074, was dem 30.12.1899 00:00:00 entspricht. Richtig wäre der 29.12.1899. Der Hintergrund ist, dass der Integerteil eines TDateTime, der den Tag angibt, als vorzeichenbehafteter Wert betrachtet wird. Negative Werte sind Tage vor dem 30.12.1899. Abweichend hiervon wird der Nachkommateil, der die Uhrzeit angibt, als vorzeichenloser Wert betrachtet. Bei Berechnungen von TDateTime-Werten, bei denen der Nachkommateil nicht leer ist, führt das dann zu Problemen wenn ein Wechsel des Vorzeichens auftritt. Wie man oben schön sieht, wird, wenn 2 subtrahiert wird, nicht der Integeranteil um 2 gesenkt. Performance: 4945 ms für 8399160 Durchläufe = 588.7 µs je Durchlauf LastWeekDayOfMonth aus #9 Arbeitet korrekt für den Zeitraum 01.0001 bis 12.9999 Performance: 421 ms für 8399160 Durchläufe = 50.1 µs je Durchlauf LastWeekDayOfMonth aus #12 Arbeitet korrekt für den Zeitraum 01.0001 bis 12.9999 Performance: 249 ms für 8399160 Durchläufe = 29.6 µs je Durchlauf LastWeekDayOfMonth aus diesem Beitrag weiter unten Arbeitet korrekt für den Zeitraum 01.0001 bis 12.9999 Performance: 140 ms für 8399160 Durchläufe = 16.7 µs je Durchlauf LastWeekDayOfMonthAsm aus diesem Beitrag weiter unten Performance: 78 ms für 8399160 Durchläufe = 9.3 µs je Durchlauf
Delphi-Quellcode:
FUNCTION LastWeekDayOfMonth(Year,Month,WeekDay:Integer):TDateTime;
const DOffs:Array[1..12] of Integer=(31,59,90,120,151,181,212,243,273,304,334,365); var DN,DoW:Integer; begin DN:=Year*365+DOffs[Month]; if Month=1 then Dec(Year); Year:=Year shr 2; // Year Div 4 Inc(DN,Year); Year:=Year div 25; // Year Div 100 Inc(DN,Year shr 2 - Year); DoW:=(5+DN) mod 7; if WeekDay>DoW then Dec(DN,7-WeekDay+DoW) else Dec(DN,Dow-WeekDay); Result:=DN-System.SysUtils.DateDelta-365; end;
Delphi-Quellcode:
FUNCTION LastWeekDayOfMonthAsm(Year,Month,WeekDay:Integer):TDateTime;
asm {$IFDEF CPUX86} // EAX=Year, EDX=Month, ECX=WeekDay push ebx imul ebx,eax,365 // DN:=Year*365 add ebx,[edx*4+@Const-4] // Inc(DN,DOffs[Month]) cmp edx,1 // Month=1? jne @1 // Nein sub eax,1 // Dec(Year) @1: shr eax,2 // Year:=Year div 4 add ebx,eax // Inc(DN,Year) xor edx,edx div [13*4+@Const] // Year:=Year div 25 sub ebx,eax // Dec(DN,Year) shr eax,2 // Year:=Year div 4 add ebx,eax // Inc(DN,Year) lea eax,[ebx+5] xor edx,edx div [12*4+@Const] // DoW=(5+DN) mod 7 sub edx,ecx // DoW-WeekDay jns @2 // WeekDay<=Dow add edx,7 @2: sub ebx,edx sub ebx,System.Sysutils.DateDelta+365 push ebx fild DWord[esp] // Result:=DN pop ecx pop ebx ret //@Const: dd 31,59,90,120,151,181,212,243,273,304,334,365,7,25 {$ELSE} // RCX=Year, RDX=Month, R8=WeekDay lea r10,@Const // Zeiger auf Konstanten mov eax,ecx // Year imul r9d,eax,365 // DN:=Year*365 add r9d,DWord[r10+rdx*4-4] // Inc(DN,Const[Month]) cmp edx,1 // Month=1? jne @1 // Nein sub eax,1 // Dec(Year) @1: shr eax,2 // Year:=Year div 4 add r9d,eax // Inc(DN,Year) xor edx,edx div DWord[r10+13*4] // Year:=Year div 25 sub r9d,eax // Dec(DN,Year) shr eax,2 // Year:=Year div 4 add r9d,eax // Inc(DN,Year) lea rax,[r9+5] xor edx,edx div DWord[r10+12*4] // DoW=(5+DN) mod 7 sub edx,r8d // DoW-WeekDay jns @2 // WeekDay<=Dow add edx,7 @2: sub r9d,edx sub r9d,System.Sysutils.DateDelta+365 cvtsi2sd xmm0,r9d ret {$ENDIF} @Const: dd 31,59,90,120,151,181,212,243,273,304,334,365,7,25 end;
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 |
![]() |
![]() |