Comment utiliser c# similaire Math.Round
con MidpointRounding.AwayFromZero
dans Delphi ?
Ce qui sera l'équivalent de :
double d = 2.125;
Console.WriteLine(Math.Round(d, 2, MidpointRounding.AwayFromZero));
Sortie : 2.13
Dans Delphi ?
Comment utiliser c# similaire Math.Round
con MidpointRounding.AwayFromZero
dans Delphi ?
Ce qui sera l'équivalent de :
double d = 2.125;
Console.WriteLine(Math.Round(d, 2, MidpointRounding.AwayFromZero));
Sortie : 2.13
Dans Delphi ?
Je crois que les RTL de Delphi SimpleRoundTo fait essentiellement cela, du moins si le mode d'arrondi de la FPU est "correct". Veuillez lire attentivement sa documentation et son implémentation, et décidez ensuite si elle est suffisante pour vos besoins.
Mais attention paramètre le mode d'arrondi pour une seule opération d'arrondi comme celle-ci utilise un changement global pour résoudre un problème local. Cela peut causer des problèmes (multithreading, bibliothèques, etc.).
Bavardage en prime : Si la question avait porté sur l'arrondi "normal" (à un entier), je pense que j'aurais essayé une approche du genre
function RoundMidpAway(const X: Real): Integer;
begin
Result := Trunc(X);
if Abs(Frac(X)) >= 0.5 then
Inc(Result, Sign(X));
end;
à la place.
Bien sûr, il est possible d'écrire une fonction similaire même pour le cas général de n des chiffres fractionnaires. (Mais veillez à traiter correctement les cas limites, les débordements, les problèmes de virgule flottante, etc.)
Mise à jour : Je pense que ce qui suit fait l'affaire (et est rapide) :
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;
Bien sûr, si vous utilisiez cette fonction dans un code de production, vous ajouteriez également au moins 50 cas de tests unitaires pour tester son exactitude (à exécuter quotidiennement).
Mise à jour : I croire la version suivante est plus 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;
mais je ne l'ai pas entièrement testé. (Actuellement, il passe Plus de 900 cas de tests unitaires mais je ne considère pas que la suite de tests soit encore suffisante).
Ce que vous cherchez, c'est SimpleRoundTo en combinaison avec SetRoundMode . Comme le dit la documentation :
SimpleRoundTo renvoie la valeur la plus proche qui a la puissance de dix spécifiée. Dans le cas
AValue
est exactement au milieu des deux valeurs les plus proches qui ont la puissance de dix spécifiée (au-dessus et au-dessous), cette fonction renvoie :
La valeur vers plus l'infini si
AValue
est positif.La valeur vers moins l'infini si
AValue
est négatif et le mode d'arrondi de la FPU n'est pas défini sur rmUp
Notez que le deuxième paramètre de la fonction est TRoundToRange
qui se réfère à l'exposant (puissance de 10) plutôt qu'au nombre de chiffres fractionnaires dans le langage .NET. Math.Round méthode. Par conséquent, pour arrondir à deux décimales, vous utilisez -2 comme intervalle d'arrondi.
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;
rmPlus proche
2,13
-2,13
rmDown
2,13
-2,13
rmUp
2,13
-2,12
rmTruncate
2,13
-2,13
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.