I need to round a floating point number to two decimal places, but always down. Now I use RoundTo(number, -2)
, but it does the rounding mathematically correctly, which is undesired behavior for my situation. Let the reason, why I need to do this, aside...
I eventually achieved it using this:
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;
However, this solution just doesn't feel right. Is there a "mathematically clean" way to do it, without messing with strings and resetting decimal separators etc. ? I searched a lot, but didn't find any.
You can do the following:
Like this:
function RoundCurrTo2dpTruncate(const Value: Currency): Currency;
begin
Result := Trunc(Value*100) / 100;
end;
I've assumed that by rounding down you mean towards zero. So 0.678 rounds down to 0.67 and -0.678 to -0.67. However, if you want to round towards -∞ then you should replace Trunc
with Floor
.
function RoundCurrTo2dpDown(const Value: Currency): Currency;
begin
Result := Floor(Value*100) / 100;
end;
Another way to tackle the problem is to recognise that a Currency
value is simply a 64 bit integer with an implicit shift of 10000. So the entire operation can be performed using integer operations, unlike the code above which uses floating point operations.
From the documentation:
Currency is a fixed-point data type that minimizes rounding errors in monetary calculations. It is stored as a scaled 64-bit integer with the 4 least significant digits implicitly representing decimal places. When mixed with other real types in assignments and expressions, Currency values are automatically divided or multiplied by 10000.
For example you could implement RoundCurrTo2dpTruncate
like this:
function RoundCurrTo2dpTruncate(const Value: Currency): Currency;
begin
PInt64(@Result)^ := (PInt64(@Value)^ div 100)*100;
end;
Note that here the arithmetic has been an shifted by 10000. So multiplication by 100 has become division by 100. And so on.