c# - 與Delphi中的MidpointRounding.AwayFromZero相當的Math.Round()是什麼?




delphi-7 (2)

您正在尋找的是 SimpleRoundTo 功能與 SetRoundMode 結合。 正如文件所說:

SimpleRoundTo 返回具有指定冪10的最近值。 如果 AValue 恰好位於具有指定冪10(高於和低於)的兩個最接近值的中間,則此函數返回:

  • 如果 AValue 為正,則加上無窮大的值。

  • 如果 AValue 為負且FPU舍入模式未設置為rmUp,則朝向負無窮大的值

請注意,函數的第二個參數是 TRoundToRange ,它指的是指數(冪為10)而不是.NET的 Math.Round 方法中的小數位數。 因此,要捨入到2位小數,請使用-2作為捨入範圍。

uses Math, RTTI;

var
  LRoundingMode: TRoundingMode;
begin
  for LRoundingMode := Low(TRoundingMode) to High(TRoundingMode) do
  begin
    SetRoundMode(LRoundingMode);
    Writeln(TRttiEnumerationType.GetName(LRoundingMode));
    Writeln(SimpleRoundTo(2.125, -2).ToString);
    Writeln(SimpleRoundTo(-2.125, -2).ToString);
  end;
end;

rmNearest

2,13

-2,13

rmDown

2,13

-2,13

rmUp

2,13

-2,12

rmTruncate

2,13

-2,13

如何在Delphi中使用帶有 MidpointRounding.AwayFromZero c#類似 Math.Round

什麼相當於:

double d = 2.125;
Console.WriteLine(Math.Round(d, 2, MidpointRounding.AwayFromZero));

產量: 2.13

在德爾福?


我相信Delphi RTL的 SimpleRoundTo 函數基本上就是這樣,至少在FPU舍入模式是“正確的”時。 請仔細閱讀其文檔和實現,然後確定它是否足夠用於您的目的。

但請注意,為這樣的單個舍入操作 設置 舍入模式是使用全局更改來解決本地問題。 這可能會導致問題(多線程,庫等)。

獎金喋喋不休:如果問題是關於“常規”舍入(到整數),我想我嘗試過像

function RoundMidpAway(const X: Real): Integer;
begin
  Result := Trunc(X);
  if Abs(Frac(X)) >= 0.5 then
    Inc(Result, Sign(X));
end;

代替。

當然,即使對於 n 個小數位的一般情況,也可以寫出類似的函數。 (但要小心正確處理邊緣情況,溢出,浮點問題等。)

更新: 我相信以下功能(並且很快):

function RoundMidpAway(const X: Real): Integer; overload;
begin
  Result := Trunc(X);
  if Abs(Frac(X)) >= 0.5 then
    Inc(Result, Sign(X));
end;

function RoundMidpAway(const X: Real; ADigit: integer): Real; overload;
const
  PowersOfTen: array[-10..10] of Real =
    (
      0.0000000001,
      0.000000001,
      0.00000001,
      0.0000001,
      0.000001,
      0.00001,
      0.0001,
      0.001,
      0.01,
      0.1,
      1,
      10,
      100,
      1000,
      10000,
      100000,
      1000000,
      10000000,
      100000000,
      1000000000,
      10000000000
    );
var
  MagnifiedValue: Real;
begin
  if not InRange(ADigit, Low(PowersOfTen), High(PowersOfTen)) then
    raise EInvalidArgument.Create('Invalid digit index.');
  MagnifiedValue := X * PowersOfTen[-ADigit];
  Result := RoundMidpAway(MagnifiedValue) * PowersOfTen[ADigit];
end;

當然,如果您在生產代碼中使用此功能,您還需要添加至少50個單元測試用例來測試其正確性(每天運行)。

更新: 相信 以下版本更穩定:

function RoundMidpAway(const X: Real; ADigit: integer): Real; overload;
const
  FuzzFactor = 1000;
  DoubleResolution = 1E-15 * FuzzFactor;
  PowersOfTen: array[-10..10] of Real =
    (
      0.0000000001,
      0.000000001,
      0.00000001,
      0.0000001,
      0.000001,
      0.00001,
      0.0001,
      0.001,
      0.01,
      0.1,
      1,
      10,
      100,
      1000,
      10000,
      100000,
      1000000,
      10000000,
      100000000,
      1000000000,
      10000000000
    );
var
  MagnifiedValue: Real;
  TruncatedValue: Real;
begin

  if not InRange(ADigit, Low(PowersOfTen), High(PowersOfTen)) then
    raise EInvalidArgument.Create('Invalid digit index.');
  MagnifiedValue := X * PowersOfTen[-ADigit];

  TruncatedValue := Int(MagnifiedValue);
  if CompareValue(Abs(Frac(MagnifiedValue)), 0.5, DoubleResolution * PowersOfTen[-ADigit]) >= EqualsValue  then
    TruncatedValue := TruncatedValue + Sign(MagnifiedValue);

  Result := TruncatedValue * PowersOfTen[ADigit];

end;

但我還沒有完全測試過它。 (目前它通過 900多個單元測試用例 ,但我認為測試套件還不夠。)







delphi-7