[英]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.