在C#中,如何将大整数的除法计算为十进制而不是双精度? TL;博士
在C#中,如何将大整数的除法计算为十进制而不是双精度? TL;博士,c#,type-conversion,C#,Type Conversion,BigRational类型可以转换为double来计算值。但是,在分子和分母达到一定大小后,返回的值是double.NaN 由于BigRational将分子和分母作为System.Numerics.BigIntegers进行跟踪,因此您可以使用对数属性的解决方法: a / b = e^(ln(a) - ln(b)) 通过我们的biginger分子和分母,我们可以调用 BigInteger num = myBigRational.Numerator; BigInteger den = myBig
BigRational
类型可以转换为double
来计算值。但是,在分子和分母达到一定大小后,返回的值是double.NaN
由于BigRational
将分子和分母作为System.Numerics.BigInteger
s进行跟踪,因此您可以使用对数属性的解决方法:
a / b = e^(ln(a) - ln(b))
通过我们的biginger
分子和分母,我们可以调用
BigInteger num = myBigRational.Numerator;
BigInteger den = myBigRational.Denominator;
double value = Math.Exp(BigInteger.Log(num) - BigInteger.Log(den));
由于接近0的值的double
类型的结构限制,我宁愿使用decimal
。我只是还不知道怎么做
上下文
只是为了好玩,我正在编写一个程序,使用arctan
的函数计算pi
arctan(x) = x - x^3/3 + x^5/5 - x^7/7 + x^9/9 - ...
如果我们在1处计算序列,我们得到
arctan(1) = 1 - 1/3 + 1/5 - 1/7 + 1/9 - ...
由于arctan(1)=pi/4,我们可以将级数乘以4来计算pi
我的程序的目标是计算要收敛到精确到n位数的pi所需的级数项数。例如,为了使序列精确到一位数字(3),它需要前三项:
1 term: 4 * (1) = 4
2 terms: 4 * (1 - 1/3) = 2.666666666666667
3 terms: 4 * (1 - 1/3 + 1/5) = 3.466666666666667
要精确到2位(3.1),它需要前19个术语。精确到3位(3.14)需要前119个术语,依此类推
我最初使用C#的decimal
type编写程序:
const int MaxDigits = 20;
private static void RunDecimalCalculation()
{
decimal pi = 0m; // our current approximation of pi
decimal denominator = 1m;
decimal addSubtract = 1m;
ulong terms = 0;
for (int digits = 0; digits < MaxDigits; digits++)
{
decimal piToDigits, upperBound;
GetBounds(digits, out piToDigits, out upperBound);
while (pi >= upperBound | pi < piToDigits)
{
pi += addSubtract * 4m / denominator;
denominator += 2m;
addSubtract *= -1m;
terms++;
}
PrintUpdate(terms, digits, pi);
}
}
/// <summary>
/// Returns the convergence bounds for <paramref name="digits"/> digits of pi.
/// </summary>
/// <param name="digits">Number of accurate digits of pi.</param>
/// <param name="piToDigits">Pi to the first <paramref name="digits"/> digits of pi.</param>
/// <param name="upperBound">same as <paramref name="piToDigits"/>, but with the last digit + 1</param>
/// <example>
/// <code>GetBounds(1)</code>:
/// piToDigits = 3
/// upperBound = 4
///
/// <code>GetBounds(2)</code>:
/// piToDigits = 3.1
/// upperbound = 3.2
/// </example>
private static void GetBounds(int digits, out decimal piToDigits, out decimal upperBound)
{
int pow = (int)Math.Pow(10, digits);
piToDigits = (decimal)Math.Floor(Math.PI * pow) / pow;
upperBound = piToDigits + 1m / pow;
}
然而,我意识到,由于每次迭代中的舍入错误,在足够的术语之后,所需的术语数量可能会减少。因此,我开始研究F#PowerPack的BigRational
,并重写代码:
// very minor optimization by caching common values
static readonly BigRational Minus1 = BigRational.FromInt(-1);
static readonly BigRational One = BigRational.FromInt(1);
static readonly BigRational Two = BigRational.FromInt(2);
static readonly BigRational Four = BigRational.FromInt(4);
private static void RunBigRationalCalculation()
{
BigRational pi = BigRational.Zero;
ulong terms = 0;
var series = TaylorSeries().GetEnumerator();
for (int digits = 0; digits < MaxDigits; digits++)
{
BigRational piToDigits, upperBound;
GetBounds(digits, out piToDigits, out upperBound);
while (pi >= upperBound | pi < piToDigits)
{
series.MoveNext();
pi += series.Current;
terms++;
}
double piDouble = Math.Exp(BigInteger.Log(pi.Numerator) - BigInteger.Log(pi.Denominator));
PrintUpdate(terms, digits, (decimal)piDouble);
}
}
// code adapted from http://tomasp.net/blog/powerpack-numeric.aspx
private static IEnumerable<BigRational> TaylorSeries()
{
BigRational n = One;
BigRational q = One;
while (true)
{
yield return q * Four / n;
n += Two;
q *= Minus1;
}
}
这是不准确的。有没有一种方法可以通过数学向导或一些库来解决这个问题,这些库具有Math.Exp()
和biginger.Log()
的decimal
版本
在C#中,如何将大整数的除法计算为十进制而不是双精度
您有较大的整数N和D,希望将精确值N/D近似为十进制。WOLOG假设两者都是正的
这很容易。首先解决这个问题:
- 给定N和D,找到使N/D-M/1028的绝对值最小的大整数M李>
第二,解决这个问题:
- 给定M,求大整数M'和非负整数E,使M/1028=M'/10e和E最小化
第三,解决这个问题:
- 给定M',求非负大整数I0,I1,I2,使M'=I0+I1*232+I2*264,且每个都严格小于232。如果没有这样的I0、I1、I2,那么您的数字就不能表示为十进制
将I0、I1和I2转换为无符号整数,然后再转换为有符号整数
现在你有了所有需要打电话的东西
嘿,你手里有一个小数点
也就是说:你不应该一开始就这么做。你在用大有理数做数学;你为什么要把它们改成小数或双数呢?只要把π近似到你想要的大有理数的任何水平,然后将你的缓慢转换序列与之进行比较
仅供参考,此系列收敛速度非常慢。一旦你根据经验确定了它的收敛速度,你能证明收敛速度的任何界限吗?这个问题的答案似乎可以满足你的两个需要:@Abion47-Hmm,也许吧。我不确定这段代码是否与我在做迭代十进制问题时遇到的问题相同。他们的DecimalExp()
和LogN()
代码的序列收敛性与我的相同。也就是说,如果他们的代码是准确的,我想我的十进制版本也会是准确的——很可能是这样;我只是不确定。看起来您在原始代码中进行了一系列从double
到int
和double
到decimal
的转换。每次这样做,都有可能在计算中引入错误。为了确保得到清晰的结果,你应该只计算一种类型。我不明白的是,为什么你要用双精度或十进制进行计算。将pi表示为一个BigRational,小数位数尽可能多,并使用它。提示:确定两个事物的接近程度的第一步是取它们的差。
double piDouble = Math.Exp(BigInteger.Log(pi.Numerator) - BigInteger.Log(pi.Denominator));