2016-04-17 6 views
1

Ich muss eine Gleitkommazahl auf zwei Dezimalstellen runden, aber immer unten. Jetzt benutze ich RoundTo(number, -2), aber es tut die Rundung mathematisch korrekt, was unerwünschtes Verhalten für meine Situation ist. Lassen Sie den Grund, warum ich dies tun müssen, beiseite ...Delphi - RoundTo - immer runter

ich es schließlich erreicht dies mit:

var a,b: currency; 
    floatStr: string; 
    format: TFormatSettings; 
    localeDec: char; 
begin 


    format:= TFormatSettings.Create; 
    localeDec:= format.DecimalSeparator; 
    format.DecimalSeparator:= ','; 
    System.SysUtils.FormatSettings:= format; 

    a:= 2/30; 
    floatStr:= floatToStr(a); 
    b:= strToCurr(
     copy(floatStr, 1, ansiPos(',', floatStr) + 2) 
); 
    showMessage(currToStr(b)); 

    format.DecimalSeparator := localeDec; 
    System.SysUtils.FormatSettings:= format; 

end; 

Diese Lösung ist jedoch nur nicht richtig anfühlt. Gibt es einen "mathematisch sauberen" Weg, dies zu tun, ohne mit Strings zu verwirbeln und Dezimaltrennzeichen etc. zurückzusetzen? Ich habe viel gesucht, aber keine gefunden.

Antwort

4

Sie können folgendes tun:

  1. Multiplizieren Sie den Wert mit 100.
  2. Kürzen auf eine ganze Zahl, gegen Null.
  3. Teilen Sie den Wert von 100.

So:

function RoundCurrTo2dpTruncate(const Value: Currency): Currency; 
begin 
    Result := Trunc(Value*100)/100; 
end; 

Ich bin davon ausgegangen, dass Sie gegen Null bedeuten, durch Abrunden. So 0,678 rundet auf 0,67 und -0,678 bis -0,67 ab. Wenn Sie jedoch in Richtung -∞ runden möchten, sollten Sie Trunc durch Floor ersetzen.

function RoundCurrTo2dpDown(const Value: Currency): Currency; 
begin 
    Result := Floor(Value*100)/100; 
end; 

Ein andere Möglichkeit, das Problem zu lösen ist zu erkennen, dass ein Currency Wert ist lediglich ein 64-Bit-Integer mit einer impliziten Verschiebung von 10000. So ist die gesamte Operation kann unter Verwendung von Integer-Operationen durchgeführt werden, im Gegensatz zu dem Code, über den verwendet Fließkommaoperationen.

Vom documentation:

Währung ist eine Festkommadatentyp, den Rundungsfehler in dem Geld Berechnungen minimiert. Es wird als eine skalierte 64-Bit-Ganzzahl gespeichert, wobei die 4 niedrigstwertigen Stellen implizit die Nachkommastellen darstellen. Wenn mit anderen reellen Typen in Zuweisungen und Ausdrücken gemischt werden Währungswerte automatisch von 10000.

Zum Beispiel geteilt oder multipliziert Sie RoundCurrTo2dpTruncate wie dies implementieren könnte:

function RoundCurrTo2dpTruncate(const Value: Currency): Currency; 
begin 
    PInt64(@Result)^ := (PInt64(@Value)^ div 100)*100; 
end; 

Beachten Sie, dass hier die Arithmetik war um 10000 verschoben. Also ist die Multiplikation mit 100 durch 100 geteilt worden. Und so weiter.

+0

Ich glaube geändert verwenden 'Int' ist besser als' Trunc' weil 'Trunc' möglicherweise überlaufen kann. – kludg

+3

@ user246408 Ich denke eigentlich, es ist besser, das Ganze mit Integer-Operationen zu machen. –

0

Sie können SetRoundMode mit alten Delphi RoundTo

SetRoundMode(rmDown); 

function RoundTo(const AValue: Double; const ADigit: TRoundToRange): Double; 
var 
    LFactor: Double; 
begin 
    LFactor := IntPower(10, ADigit); 
    Result := Round(AValue/LFactor) * LFactor; 
end; 

Offensichtlich wurde es in den letzten Versionen

+0

Ja, ich habe das falsch verstanden. Die Frage dreht sich immer noch um 'Währung'. –

+0

Ja, das ist nicht geeignet. Ich lese nur den ersten Teil "um eine Fließkommazahl auf zwei Dezimalstellen". – smooty86

+0

Fair genug. Asker ist ein wenig verwirrt. Währung ist Fixpunkt. –

Verwandte Themen