Я полагаю, что функция 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 + модульных тестовых случаев , но я пока не считаю набор тестов вполне достаточным.)