Delphi - RoundTo - 总是向下
Delphi - RoundTo - always down
我需要将浮点数舍入到小数点后两位,但总是向下舍入。现在我使用 RoundTo(number, -2)
,但它在数学上正确地进行了舍入,这对我的情况来说是不希望的行为。让原因,为什么我需要这样做,放在一边...
我最终用这个实现了它:
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;
但是,这个解决方案感觉不对。有没有一种 "mathematically clean" 方法可以做到这一点,而不会弄乱字符串和重置小数点分隔符等?我找了很多,都没找到。
您可以执行以下操作:
- 将值乘以 100。
- 截断为整数,趋近于零。
- 将值除以 100。
像这样:
function RoundCurrTo2dpTruncate(const Value: Currency): Currency;
begin
Result := Trunc(Value*100) / 100;
end;
我假设四舍五入是指接近零。所以 0.678 向下舍入为 0.67,-0.678 向下舍入为 -0.67。但是,如果您想向 -∞ 舍入,则应将 Trunc
替换为 Floor
。
function RoundCurrTo2dpDown(const Value: Currency): Currency;
begin
Result := Floor(Value*100) / 100;
end;
解决该问题的另一种方法是认识到 Currency
值只是一个隐式移位 10000 的 64 位整数。因此,与上面的代码不同,可以使用整数运算来执行整个操作它使用浮点运算。
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.
例如,您可以这样实现 RoundCurrTo2dpTruncate
:
function RoundCurrTo2dpTruncate(const Value: Currency): Currency;
begin
PInt64(@Result)^ := (PInt64(@Value)^ div 100)*100;
end;
请注意,此处的算术运算已移位 10000。因此,乘以 100 已变为除以 100。依此类推。
您可以将 SetRoundMode 与旧 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;
显然它在最近的版本中发生了变化
我需要将浮点数舍入到小数点后两位,但总是向下舍入。现在我使用 RoundTo(number, -2)
,但它在数学上正确地进行了舍入,这对我的情况来说是不希望的行为。让原因,为什么我需要这样做,放在一边...
我最终用这个实现了它:
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;
但是,这个解决方案感觉不对。有没有一种 "mathematically clean" 方法可以做到这一点,而不会弄乱字符串和重置小数点分隔符等?我找了很多,都没找到。
您可以执行以下操作:
- 将值乘以 100。
- 截断为整数,趋近于零。
- 将值除以 100。
像这样:
function RoundCurrTo2dpTruncate(const Value: Currency): Currency;
begin
Result := Trunc(Value*100) / 100;
end;
我假设四舍五入是指接近零。所以 0.678 向下舍入为 0.67,-0.678 向下舍入为 -0.67。但是,如果您想向 -∞ 舍入,则应将 Trunc
替换为 Floor
。
function RoundCurrTo2dpDown(const Value: Currency): Currency;
begin
Result := Floor(Value*100) / 100;
end;
解决该问题的另一种方法是认识到 Currency
值只是一个隐式移位 10000 的 64 位整数。因此,与上面的代码不同,可以使用整数运算来执行整个操作它使用浮点运算。
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.
例如,您可以这样实现 RoundCurrTo2dpTruncate
:
function RoundCurrTo2dpTruncate(const Value: Currency): Currency;
begin
PInt64(@Result)^ := (PInt64(@Value)^ div 100)*100;
end;
请注意,此处的算术运算已移位 10000。因此,乘以 100 已变为除以 100。依此类推。
您可以将 SetRoundMode 与旧 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;
显然它在最近的版本中发生了变化