【问题标题】:rounding a currency将货币四舍五入
【发布时间】:2013-02-22 12:11:41
【问题描述】:

我有以下代码来舍入货币

function MyRound(value :currency) : integer;

begin
  if value > 0 then
    result := Trunc(value + 0.5)
  else
    result := Trunc(value - 0.5);
end;

到目前为止它运行良好,我现在的问题是如果我想对像 999999989000.40 这样的货币进行舍入,它会给出负值,因为 Truc 采用 int 而 MyRound 也返回 int。

我可能的解决方案是将货币转换为字符串并在 . 之前获取字符串并将字符串转换回货币。这是一个正确的方法吗?我是 delpi 的新手,所以请帮帮我。

【问题讨论】:

  • 使用 int64 代替整数?
  • 你建议的解决方案是错误的,不会起作用,因为你的问题是32位溢出。

标签: delphi delphi-xe rounding truncation


【解决方案1】:

在我看来,你有两个选择:

  1. 正如 David Heffernan 指出的那样,您使用 Round 函数;
  2. 您可以使用SimpleRoundTo 函数,如here 所述。 SimpleRoundTo 的优点是它接收 SingleDoubleExtended 数据类型的参数,并且它们可以很好地转换这些数字。

您不需要任何类型转换。您已经可以使用许多舍入函数。只需将所需的数字四舍五入即可。

【讨论】:

  • SimpleRoundTo(..., 0)Round(...) 有何不同?
  • @DavidHeffernan 在这种情况下,它没有。这就是为什么我说你的解决方案给出了相同的结果。但是,如果要将数字四舍五入到给定的精度,则会有所不同。 Round(...)SimpleRoundTo(...,0) 的特例
  • @DavidHeffernan:它不使用银行家的四舍五入。根据 Bogdan 链接到的文档“此函数的结果取决于当前的 FPU 舍入模式......要在“银行家模式”中舍入,请使用 System.Math.RoundTo。”
【解决方案2】:

你把事情复杂化了。你可以简单地使用Round:

program Project1;
{$APPTYPE CONSOLE}

uses
  SysUtils;

var
  C: Currency;

begin
  C := 999999989000.4;
  Writeln(Round(C));
  C := 999999989000.5;
  Writeln(Round(C));
  C := 999999989000.6;
  Writeln(Round(C));
  C := 999999989001.4;
  Writeln(Round(C));
  C := 999999989001.5;
  Writeln(Round(C));
  C := 999999989001.6;
  Writeln(Round(C));
  Readln;
end.

哪个输出

999999989000 999999989000 999999989001 999999989001 999999989002 999999989002

如果您不想要银行家的四舍五入,并且您确实想要您的Trunc 逻辑,那么您确实需要编写自己的函数。但是你的函数的问题是它被截断为 32 位整数。让函数返回一个 64 位整数:

program Project1;
{$APPTYPE CONSOLE}

uses
  SysUtils, Math;

var
  C: Currency;

function MyRound(const Value: Currency): Int64;
begin
  if Value > 0 then
    result := Trunc(Value + 0.5)
  else
    result := Trunc(Value - 0.5);
end;

begin
  C := 999999989000.4;
  Writeln(MyRound(C));
  C := 999999989000.5;
  Writeln(MyRound(C));
  C := 999999989000.6;
  Writeln(MyRound(C));
  C := 999999989001.4;
  Writeln(MyRound(C));
  C := 999999989001.5;
  Writeln(MyRound(C));
  C := 999999989001.6;
  Writeln(MyRound(C));
  Readln;
end.
999999989000 999999989001 999999989001 999999989001 999999989002 999999989002

【讨论】:

  • 2.5 到 2 和 3.5 到 4 的银行家四舍五入是否不会影响舍入? (或者反过来,永远记不住)。 0.5 技巧是解决该问题的最简单方法,而不会弄乱 FPU 设置?
  • @MarjanVenema 好吧,Round 就是这样工作的。如果你想要不同的东西,那就用不同的东西。
  • 好吧,这就是在标准 FPU 设置下回合的工作方式。鉴于基本的数学知识但没有财务背景,这不是人们期望的工作方式。当我发现它时,我确实感到惊讶......
  • @MarjanVenema 不幸的是,大多数高中生都被教导向无穷大规则。这种四舍五入不用于科学或金融,因为它会引入偏差,因为 0.5 总是四舍五入。 FPU 不默认为 infinity 舍入的原因是因为没有人关心它。
【解决方案3】:

看看 John Herbster 的舍入例程。它们提供您可能想要的几乎任何类型的舍入,例如:

drNone,    {No rounding.}
drHalfEven,{Round to nearest or to even whole number. (a.k.a Bankers) }
drHalfPos, {Round to nearest or toward positive.}
drHalfNeg, {Round to nearest or toward negative.}
drHalfDown,{Round to nearest or toward zero.}
drHalfUp,  {Round to nearest or away from zero.}
drRndNeg,  {Round toward negative.                    (a.k.a. Floor) }
drRndPos,  {Round toward positive.                    (a.k.a. Ceil ) }
drRndDown, {Round toward zero.                        (a.k.a. Trunc) }
drRndUp);  {Round away from zero.}

我现在不能给你一个链接,但是谷歌:十进制四舍五入 John Herbster 我认为他最新的舍入例程在 DecimalRounding_JH1.pas 中。他对浮点舍入的讨论(Embarcadero 网站上的某个地方是“必读”。

【讨论】:

    【解决方案4】:

    这是我实际使用的(很想知道这种方法是否有任何问题!):

    function RoundingFunction(X: Real): Int64;
    begin
      Result := Trunc(SimpleRoundTo(X, 0));
    end;
    

    【讨论】:

    • SimpleRoundTo(x,0) 没有留下小数,所以,恕我直言,Trunc 函数在那里没用。
    • E2010 Incompatible types: 'Int64' and 'Double'
    • 顺便说一句,是否可以从 Double 转换为 Int64?对于“更好”的任何定义,它更好吗?
    猜你喜欢
    • 2011-09-10
    • 2010-11-13
    • 1970-01-01
    • 1970-01-01
    • 2018-02-14
    • 2020-04-08
    • 1970-01-01
    • 2011-10-01
    • 1970-01-01
    相关资源
    最近更新 更多