简体   繁体   English

与Delphi中的MidpointRounding.AwayFromZero相当的Math.Round()是什么?

[英]What is the equivalent of Math.Round() with MidpointRounding.AwayFromZero in Delphi?

How do I use c# similar Math.Round with MidpointRounding.AwayFromZero in Delphi? 如何在Delphi中使用带有MidpointRounding.AwayFromZero c#类似Math.Round

What will be the equivalent of: 什么相当于:

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

Output: 2.13 产量: 2.13

In Delphi? 在德尔福?

I believe the Delphi RTL's SimpleRoundTo function does essentially this, at least if the FPU rounding mode is "correct". 我相信Delphi RTL的SimpleRoundTo函数基本上就是这样,至少在FPU舍入模式是“正确的”时。 Please read its documentation and implementation carefully, and then decide if it is good enough for your purposes. 请仔细阅读其文档和实现,然后确定它是否足够用于您的目的。

But beware that setting the rounding mode for a single rounding operation like this is using a global change to solve a local problem. 但请注意,为这样的单个舍入操作设置舍入模式是使用全局更改来解决本地问题。 This might cause problems (multi-threading, libraries, etc.). 这可能会导致问题(多线程,库等)。

Bonus chatter: Had the question been about "regular" rounding (to an integer), I think I'd tried an approach like 奖金喋喋不休:如果问题是关于“常规”舍入(到整数),我想我尝试过像

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

instead. 代替。

Of course, it is possible to write a similar function even for the general case of n fractional digits. 当然,即使对于n个小数位的一般情况,也可以写出类似的函数。 (But be careful to handle edge cases, overflows, floating-point issues, etc., correctly.) (但要小心正确处理边缘情况,溢出,浮点问题等。)

Update: I believe the following does the trick (and is fast): 更新:我相信以下功能(并且很快):

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;

Of course, if you'd use this function in production code, you'd also add at least 50 unit test cases that test its correctness (to be run daily). 当然,如果您在生产代码中使用此功能,您还需要添加至少50个单元测试用例来测试其正确性(每天运行)。

Update: I believe the following version is more stable: 更新:相信以下版本更稳定:

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;

but I haven't fully tested it. 但我还没有完全测试过它。 (Currently it passes 900+ unit test cases , but I don't consider the test suite quite sufficient yet.) (目前它通过900多个单元测试用例 ,但我认为测试套件还不够。)

What you're looking for is SimpleRoundTo function in combination with SetRoundMode . 您正在寻找的是SimpleRoundTo功能与SetRoundMode的结合。 As the documentations says: 正如文件所说:

SimpleRoundTo returns the nearest value that has the specified power of ten. SimpleRoundTo返回具有指定幂10的最近值。 In case AValue is exactly in the middle of the two nearest values that have the specified power of ten (above and below), this function returns: 如果AValue恰好位于具有指定幂10(高于和低于)的两个最接近值的中间,则此函数返回:

  • The value toward plus infinity if AValue is positive. 如果AValue为正,则加上无穷大的值。

  • The value toward minus infinity if AValue is negative and the FPU rounding mode is not set to rmUp 如果AValue为负且FPU舍入模式未设置为rmUp,则朝向负无穷大的值

Note that the second parameter to the function is TRoundToRange which refers to exponent (power of 10) rather than number of fractional digis in .NET's Math.Round method. 请注意,函数的第二个参数是TRoundToRange ,它指的是指数(幂为10)而不是.NET的Math.Round方法中的小数位数。 Therefore to round to 2 decimal places you use -2 as round-to range. 因此,要舍入到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 rmNearest

2,13 2,13

-2,13 -2,13

rmDown rmDown

2,13 2,13

-2,13 -2,13

rmUp rmUp

2,13 2,13

-2,12 -2,12

rmTruncate rmTruncate

2,13 2,13

-2,13 -2,13

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 JavaScript中的Math.round MidPointRounding.AwayFromZero - Math.round MidPointRounding.AwayFromZero in javascript 使用Math.Round和MidpointRounding.AwayFromZero四舍五入一个数字 - Round a number using Math.Round with MidpointRounding.AwayFromZero Math.Round bug 81.725 MidpointRounding.AwayFromZero = 81.72 - Math.Round bug 81.725 MidpointRounding.AwayFromZero = 81.72 如何使用NET Math.Round( <decimal> , <int> ,MidpointRounding.AwayFromZero) - How to round off correctly with NET Math.Round(<decimal>,<int>,MidpointRounding.AwayFromZero) 使用Math.Round(ReportItems!category.Value,MidpointRounding.AwayFromZero)返回不正确的值 - Using Math.Round(ReportItems!category.Value,MidpointRounding.AwayFromZero) returns incorrect value .NET Math.Round(<double>,<int>,MidpointRounding.AwayFromZero)无法正常工作 - .NET Math.Round(<double>,<int>,MidpointRounding.AwayFromZero) not working correctly 为什么Math.Round(106.8,2,MidpointRounding.AwayFromZero)返回106.8而不是106.80? - Why does Math.Round(106.8, 2, MidpointRounding.AwayFromZero) return 106.8 and not 106.80? 中点舍入为零 - MidpointRounding.AwayFromZero Math.Round在AwayFromZero模式下失败 - Math.Round fail on AwayFromZero mode 将MidpointRounding.AwayFromZero设置为默认的舍入方法 - Setting MidpointRounding.AwayFromZero as the default rounding method
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM