Entwickler-Ecke
Algorithmen, Optimierung und Assembler - Richtig bei 0.5 runden
Spaceguide - So 21.05.06 14:19
Titel: Richtig bei 0.5 runden
Kann ist den Befehlen FISTP (FPU) bzw. CVTSS2SI (SSE) irgendwie beibringen, bei 0.5 richtig aufzurunden? Normalerweise wird 0.5 nämlich auf 0 gerundet.
Delete - So 21.05.06 17:26
Hi Spaceguide. :wave:
Guck dir einfach in der Unit Math bei der Funktion SetRoundMode ab, was die bei SetRoundMode(rmNearest); macht. ;-)
(Hast ja laut Profil die Prof-Version, bei der sind ja die Quellen dabei)
Spaceguide - So 21.05.06 17:47
Das hatte ich schon untersucht. Standard ist rmNearest. Ist der ganzzzahlige Teil gerade (0.5,2.5,4.5 ...), so wird immer abgerundet, ist er ungerade (1.5,3.5,5.5 ...), dann aufgerundet.
Marc. - So 21.05.06 18:27
Hey,
notfalls könntest du dir auch einen eigene Funtkion schreiben.
Ich habe mal eine geschrieben, die dir hoffentlich weiterhelfen mag ;):
Sie Rundet bei >= 0,5 auf und bei < 0,5 ab:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20:
| function Round(Number: Real): Real; var FindC: Byte; NumAsStr: String; begin FindC := pos(',',FloatToStr(Number));
if FindC = 0 then Result := Number else begin NumAsStr := Copy(FloatToStr(Number),FindC,Length(FloatToStr(Number))-FindC+1); If StrToFloat(NumAsStr) >= 0.5 then begin NumAsStr := FloatToStr(Number); Delete(NumAsStr,FindC+1,Length(FloatToStr((Number)-FindC))); Result := StrToFloat(NumAsStr)+1; end else Result := RoundTo(Number,0); end; end; |
mfg
Spaceguide - So 21.05.06 18:42
Sehr performant ...
Marc. - So 21.05.06 18:48
War nur ein Vorschlag ;)
Spaceguide - So 21.05.06 18:51
Bei näherem Hinsehen muss ich aber lachen. Ein einfaches Trunc(x+0.5) macht prinzipiell das gleiche, nur eben korrekt: Deine Funktion funktioniert nur, wenn der Separator ein Komma ist (bei englischen Systemen ist es ein Punkt) und -0.5 rundet sie zu +1!
starsurfer - So 21.05.06 18:54
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7:
| function RountByFive(valve:real):integer; var help:integer; begin result:=trunc(valve); help:=trunc(frac(valve)*10)); if help>4 then inc(Result); end; |
das is dürfte auch gehen :D
und es ist ein bisschen schneller als der vorherige Vorschlag
//Edit, ok du hast ja deine Lösung schon ^^
Marc. - So 21.05.06 18:55
:oops: Du hast recht.
Ich sitze hier einfach schon zu lange am PC und bin vollkommen übermüded.
Ich denk, ich leg mich gleich mal hin.
Und nun back zum Topic.
Mfg ;)
Spaceguide - So 21.05.06 18:57
Ich find das jetzt nicht fair. Ich habe eine ernste Frage gestellt und werde nur verarscht.
Green - So 21.05.06 19:01
wieso das denn?? hast doch deine frage schon selber beantwortet...
und du hast sogar zwei weitere varianten ;)
Spaceguide - So 21.05.06 19:18
So? Meine Frage lautete aber nicht, wie ich eine Zahl korrekt runden kann, sonder:
"Kann ist den Befehlen FISTP (FPU) bzw. CVTSS2SI (SSE) irgendwie beibringen, bei 0.5 richtig aufzurunden? Normalerweise wird 0.5 nämlich auf 0 gerundet."
Green - So 21.05.06 19:25
nunja, eine Funktion kann man so einfach nicht ändern, ausserdem warum denn nicht eine eigene verwenden?
Spaceguide - So 21.05.06 19:29
Weil es performanzkritisch ist.
BenBE - So 21.05.06 20:46
Das ist leider Architektur-Bedingt nicht möglich ...
Du kannst aber
Delphi-Quelltext
1: 2: 3: 4:
| asm FADD [OneHalf] FRNDINT end; |
versuchen, wenn Du das Rounding Control-Flag auf Truncate gesetzt hast.
Die FPU unterstützt nur RoundUp, RoundDown, BankersRounding und Truncate als Modi. Das mathematisch korrekte Runden muss man emulieren.
Spaceguide - Mo 22.05.06 20:49
Hi,
also mit SetRoundMode(rmDown) bekomme ich Round(x) = Trunc(x+0.5) korrekt hin. Nur bin ich mir ein bisschen unsicher, ob das Control-Word auch so bleibt. Gibt es irgendwelche Komponenten, die gerne mal das Control-Word ändern und nicht mehr zurücksetzen?
BenBE - Mo 22.05.06 21:29
Wäre mir innerhalb der VCL nichts bewusst. Trunc und Round aus der RTL sind deshalb so lahmarschig, weil sie das CW immer erst wieder Resetten... Dadurch geht enorm Zeit verloren ...
Ansonsten setz einfach einmal vor Ausführung deiner Zeitkritischen Aktionen das CW ... Innerhalb deiner Routine kannst Du dann ggf. an kritischen Stellen noch mal kurz eine Überprüfung einbauen, sollte dies nötig sein ...
Spaceguide - Di 23.05.06 12:13
Gibt's für die SSE-Einheit auch sowas wie einen Rounding Mode?
Spaceguide - Di 23.05.06 13:13
OK, wenn ich die Bits 14/13 des Registers MXCSR mittels LDMXCSR auf 01 setze, bekomme ich auch die SSE-Einheit dazu, immer abzurunden, jedoch scheint irgendetwas das Register wieder zurückzusetzen, so dass ich das Register immer wieder vor jeder Rundungsaktion setzen muss. Irgendwelche Hinweise, was es zurücksetzt?
BenBE - Di 23.05.06 14:18
Wüsste ich jetzt aus dem Kopf nix... Hab mit SSE noch nicht gearbeitet. Könnte aber sein, dass durch das Neueinrichten der SSE-Umgebung das Flag resettet wird? FPU und SSE sharen sich ja die Register-Speicher ...
Oder meinst Du das Flag wird bei jedem Befehl resettet ... Dann kannst Du glaube nicht viel tun ...???
Spaceguide - Di 23.05.06 16:19
Nein, das Flag wird irgendwann mal resettet, nicht nach jedem Befehl. Das Laden des Registers ist halt relativ kostspielig.
BenBE - Di 23.05.06 20:10
Dann kannst Du nur testen, welcher Befehl genau das Resetten des Flags verursacht ... Am besten Du schreibst mal kurz ne Liste, welche Befehle Du nutzt (und die, die Du ausschließen kannst).
Wenn es zwischendurch resettet wird (nicht durch deinen Source), kann es nur (wie angedeutet) durch den FEMMS geschehen sein In diesem Fall musst Du vor Nutzung einmal das Flag neu setzen.
Spaceguide - Mi 24.05.06 11:15
Also zumindest scheint ein LoadLibrary auch das FPU-Controlword zurückzusetzen.
Allesquarks - Mi 24.05.06 16:46
.Bit10 | Bit11 |
.------------------
. 0 | 0 | Runden nächste gerade Zahl
. 1 | 0 | Runden gegen -unendlich
. 0 | 1 | Runden gegen +unendlich
. 1 | 1 | Nachkommastellen weg
Wozu benutzt du denn SSE wenn du dann doch nur den skalaren Befehl verwendest?
Egal was ich sagen wollte. Da man ja auch trunc(x+0,5) machen kann benutze doch:
CVTTS(S/P)2SI (convert with truncation scalar single-precision floating point value to doubleword integer)
Da hier die "Rundungsvorschrift im Befehl begründet ist sollte ein resetten des Controlwords keinen Einfluss nehmen.
Spaceguide - Mi 24.05.06 18:20
Ich verwende sowohl die Skalar- als auch die Parallel-Befehle, CVTTS(S/P)2SI kannte ich aber nicht, thx.
Gekko - Fr 02.06.06 12:30
Bei 0,5 richtig runden ist so eine Sache.
Es kommt nämlich auch darauf an, ob kaufmännisch oder technisch gerundet wird.
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!