BigDecimal
est une classe du package java.math
qui présente de nombreux avantages pour la gestion de grands nombres d'une certaine échelle. Existe-t-il une classe ou un type de données équivalent en c # avec cette fonctionnalité.
Réponses
Trop de publicités?Tout récemment, j'ai aussi besoin d'une précision arbitraire virgule en C# et en est venu à travers l'idée de posté ici: http://stackoverflow.com/a/4524254/804614
J'ai ensuite complété le projet à l'appui de tous les arithmétiques de base et les opérateurs de comparaison, ainsi que les conversions de tous les numérique typique types et quelques exponentielle méthodes, j'ai besoin à ce moment.
Ce n'est certainement pas exhaustive, mais très fonctionnelle et presque prêt à l'emploi. Comme c'est le résultat d'une nuit de codage, je ne peux pas assurer que cette chose est sans bug ou entièrement exact, mais il a très bien fonctionné pour moi. De toute façon, je veux le publier ici parce que je n'ai pas trouvé d'autre moyen d'utiliser les décimales de précision arbitraire en C# sans la nécessité d'inclure massive de la bibliothèque (pour la plupart même pas .net, mais wrappers c++), qui viennent avec toutes sortes de bibelots.
L'idée de base est de construire une coutume à virgule flottante type avec l'arbitraire d'un grand mantisse à l'aide de la BigInteger type de .NET 4.0 et une base de 10 exposant (Int32).
Si vous trouvez des bugs/inexactitudes, des suggestions ou quoi que ce soit de constructif, n'hésitez pas à modifier directement mon message ou laisser un commentaire afin que je puisse améliorer la réponse.
Je ne suis pas entièrement sûr si c'est le meilleur endroit pour placer cette chose, mais c'est l'une des questions les plus fréquentes posées sur ce sujet sujet et j'ai vraiment envie de partager ma solution. ;)
using System;
using System.Numerics;
namespace Common
{
/// <summary>
/// Arbitrary precision decimal.
/// All operations are exact, except for division. Division never determines more digits than the given precision.
/// Based on http://stackoverflow.com/a/4524254
/// Author: Jan Christoph Bernack (contact: jc.bernack at googlemail.com)
/// </summary>
public struct BigDecimal
: IComparable
, IComparable<BigDecimal>
{
/// <summary>
/// Specifies whether the significant digits should be truncated to the given precision after each operation.
/// </summary>
public static bool AlwaysTruncate = false;
/// <summary>
/// Sets the maximum precision of division operations.
/// If AlwaysTruncate is set to true all operations are affected.
/// </summary>
public static int Precision = 50;
public BigInteger Mantissa { get; set; }
public int Exponent { get; set; }
public BigDecimal(BigInteger mantissa, int exponent)
: this()
{
Mantissa = mantissa;
Exponent = exponent;
Normalize();
if (AlwaysTruncate)
{
Truncate();
}
}
/// <summary>
/// Removes trailing zeros on the mantissa
/// </summary>
public void Normalize()
{
if (Mantissa.IsZero)
{
Exponent = 0;
}
else
{
BigInteger remainder = 0;
while (remainder == 0)
{
var shortened = BigInteger.DivRem(Mantissa, 10, out remainder);
if (remainder == 0)
{
Mantissa = shortened;
Exponent++;
}
}
}
}
/// <summary>
/// Truncate the number to the given precision by removing the least significant digits.
/// </summary>
/// <returns>The truncated number</returns>
public BigDecimal Truncate(int precision)
{
// copy this instance (remember its a struct)
var shortened = this;
// save some time because the number of digits is not needed to remove trailing zeros
shortened.Normalize();
// remove the least significant digits, as long as the number of digits is higher than the given Precision
while (NumberOfDigits(shortened.Mantissa) > precision)
{
shortened.Mantissa /= 10;
shortened.Exponent++;
}
return shortened;
}
public BigDecimal Truncate()
{
return Truncate(Precision);
}
private static int NumberOfDigits(BigInteger value)
{
// do not count the sign
return (value * value.Sign).ToString().Length;
}
#region Conversions
public static implicit operator BigDecimal(int value)
{
return new BigDecimal(value, 0);
}
public static implicit operator BigDecimal(double value)
{
var mantissa = (BigInteger) value;
var exponent = 0;
double scaleFactor = 1;
while (Math.Abs(value * scaleFactor - (double)mantissa) > 0)
{
exponent -= 1;
scaleFactor *= 10;
mantissa = (BigInteger)(value * scaleFactor);
}
return new BigDecimal(mantissa, exponent);
}
public static implicit operator BigDecimal(decimal value)
{
var mantissa = (BigInteger)value;
var exponent = 0;
decimal scaleFactor = 1;
while ((decimal)mantissa != value * scaleFactor)
{
exponent -= 1;
scaleFactor *= 10;
mantissa = (BigInteger)(value * scaleFactor);
}
return new BigDecimal(mantissa, exponent);
}
public static explicit operator double(BigDecimal value)
{
return (double)value.Mantissa * Math.Pow(10, value.Exponent);
}
public static explicit operator float(BigDecimal value)
{
return Convert.ToSingle((double)value);
}
public static explicit operator decimal(BigDecimal value)
{
return (decimal)value.Mantissa * (decimal)Math.Pow(10, value.Exponent);
}
public static explicit operator int(BigDecimal value)
{
return (int)(value.Mantissa * BigInteger.Pow(10, value.Exponent));
}
public static explicit operator uint(BigDecimal value)
{
return (uint)(value.Mantissa * BigInteger.Pow(10, value.Exponent));
}
#endregion
#region Operators
public static BigDecimal operator +(BigDecimal value)
{
return value;
}
public static BigDecimal operator -(BigDecimal value)
{
value.Mantissa *= -1;
return value;
}
public static BigDecimal operator ++(BigDecimal value)
{
return value + 1;
}
public static BigDecimal operator --(BigDecimal value)
{
return value - 1;
}
public static BigDecimal operator +(BigDecimal left, BigDecimal right)
{
return Add(left, right);
}
public static BigDecimal operator -(BigDecimal left, BigDecimal right)
{
return Add(left, -right);
}
private static BigDecimal Add(BigDecimal left, BigDecimal right)
{
return left.Exponent > right.Exponent
? new BigDecimal(AlignExponent(left, right) + right.Mantissa, right.Exponent)
: new BigDecimal(AlignExponent(right, left) + left.Mantissa, left.Exponent);
}
public static BigDecimal operator *(BigDecimal left, BigDecimal right)
{
return new BigDecimal(left.Mantissa * right.Mantissa, left.Exponent + right.Exponent);
}
public static BigDecimal operator /(BigDecimal dividend, BigDecimal divisor)
{
var exponentChange = Precision - (NumberOfDigits(dividend.Mantissa) - NumberOfDigits(divisor.Mantissa));
if (exponentChange < 0)
{
exponentChange = 0;
}
dividend.Mantissa *= BigInteger.Pow(10, exponentChange);
return new BigDecimal(dividend.Mantissa / divisor.Mantissa, dividend.Exponent - divisor.Exponent - exponentChange);
}
public static bool operator ==(BigDecimal left, BigDecimal right)
{
return left.Exponent == right.Exponent && left.Mantissa == right.Mantissa;
}
public static bool operator !=(BigDecimal left, BigDecimal right)
{
return left.Exponent != right.Exponent || left.Mantissa != right.Mantissa;
}
public static bool operator <(BigDecimal left, BigDecimal right)
{
return left.Exponent > right.Exponent ? AlignExponent(left, right) < right.Mantissa : left.Mantissa < AlignExponent(right, left);
}
public static bool operator >(BigDecimal left, BigDecimal right)
{
return left.Exponent > right.Exponent ? AlignExponent(left, right) > right.Mantissa : left.Mantissa > AlignExponent(right, left);
}
public static bool operator <=(BigDecimal left, BigDecimal right)
{
return left.Exponent > right.Exponent ? AlignExponent(left, right) <= right.Mantissa : left.Mantissa <= AlignExponent(right, left);
}
public static bool operator >=(BigDecimal left, BigDecimal right)
{
return left.Exponent > right.Exponent ? AlignExponent(left, right) >= right.Mantissa : left.Mantissa >= AlignExponent(right, left);
}
/// <summary>
/// Returns the mantissa of value, aligned to the exponent of reference.
/// Assumes the exponent of value is larger than of reference.
/// </summary>
private static BigInteger AlignExponent(BigDecimal value, BigDecimal reference)
{
return value.Mantissa * BigInteger.Pow(10, value.Exponent - reference.Exponent);
}
#endregion
#region Additional mathematical functions
public static BigDecimal Exp(double exponent)
{
var tmp = (BigDecimal)1;
while (Math.Abs(exponent) > 100)
{
var diff = exponent > 0 ? 100 : -100;
tmp *= Math.Exp(diff);
exponent -= diff;
}
return tmp * Math.Exp(exponent);
}
public static BigDecimal Pow(double basis, double exponent)
{
var tmp = (BigDecimal)1;
while (Math.Abs(exponent) > 100)
{
var diff = exponent > 0 ? 100 : -100;
tmp *= Math.Pow(basis, diff);
exponent -= diff;
}
return tmp * Math.Pow(basis, exponent);
}
#endregion
public override string ToString()
{
return string.Concat(Mantissa.ToString(), "E", Exponent);
}
public bool Equals(BigDecimal other)
{
return other.Mantissa.Equals(Mantissa) && other.Exponent == Exponent;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
return obj is BigDecimal && Equals((BigDecimal) obj);
}
public override int GetHashCode()
{
unchecked
{
return (Mantissa.GetHashCode()*397) ^ Exponent;
}
}
public int CompareTo(object obj)
{
if (ReferenceEquals(obj, null) || !(obj is BigDecimal))
{
throw new ArgumentException();
}
return CompareTo((BigDecimal) obj);
}
public int CompareTo(BigDecimal other)
{
return this < other ? -1 : (this > other ? 1 : 0);
}
}
}
Voici un C# de la bibliothèque qui fait ce que vous cherchez, et dans certains cas a des fonctionnalités supplémentaires: http://www.fractal-landscapes.co.uk/bigint.html (ou essayer au http://web.archive.org/web/20110721173046/http://www.fractal-landscapes.co.uk/bigint.html).
Par exemple, il a une fonction racine carrée, qui BigDecimal n'ont pas:
PrecisionSpec precision = new PrecisionSpec(1024, PrecisionSpec.BaseType.BIN);
BigFloat bf = new BigFloat(13, precision);
bf.Sqrt();
Console.WriteLine(bf.ToString());
Wikipedia a une liste d'autres bibliothèques http://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic#Libraries
Fâcheusement je n'ai pas les privilèges des commentaires, et mon montage a été rejeté parce qu'il était idéalement un commentaire. Voici donc une réponse à la place.
Ajout d'un lien vers un BigFloat mise en œuvre que les attributs de la fractale des paysages du site mentionné ci-dessus( http://www.fractal-landscapes.co.uk/bigint.html )
Vous devriez être en mesure de le trouver ici:
Ainsi, mis à part en utilisant les bibliothèques de tiers, avec l'appui de la BigDecimal (si elles existent), il n'y a pas de solutions de contournement. Le plus facile, moyen, autant que je suis concerné, c'est prendre un décimal mise en œuvre( à partir de mono par exemple) et de le réécrire à l'aide de la BigInteger type. À l'interne, en mono de la mise en œuvre, de type decimal est composé de trois entiers. Donc je ne pense pas que ce serait difficile à mettre en œuvre. Je ne suis pas sûr de l'efficacité de si. Vous devez tout d'abord d'envisager l'utilisation standard de type décimal comme codeka mentionné.