德尔福 - RoundTo - 永远失败

时间:2016-04-17 09:57:24

标签: delphi rounding

我需要将浮点数舍入到两位小数,但总是向下。现在我使用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;

但是,这个解决方案感觉不对。有没有"数学上干净"这样做的方法,没有弄乱字符串和重置小数分隔符等?我经常搜索,但没找到。

2 个答案:

答案 0 :(得分:4)

您可以执行以下操作:

  1. 将值乘以100。
  2. 截断整数,朝向零。
  3. 将值除以100。
  4. 像这样:

    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值只是一个64位整数,隐式移位为10000.因此整个操作可以使用整数运算来执行,这与上面使用的代码不同浮点运算。

    来自documentation

      

    货币是一种定点数据类型,可最大限度地减少货币计算中的舍入误差。它存储为缩放的64位整数,其中4个最低有效位隐式表示小数位。当与赋值和表达式中的其他实际类型混合时,货币值将自动分割或乘以10000。

    例如,你可以像这样实现RoundCurrTo2dpTruncate

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

    请注意,此处算术已移位10000.因此乘以100已除以100.依此类推。

答案 1 :(得分:0)

您可以将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;

显然在最近的版本中已经改变了