2015-02-24 92 views
-1

有可能在C++但是要做到这一点定义PI到36位小数

ifndef M_PI 
define M_PI   3.141592653589793238462643383279502884 
endif 

在C#中,我试图声明一个双var和36位小数分配给它的PI值。 这不起作用,因为双精度数据存储最多为16位小数(MSDN docx)。我尝试创建一个隐式类型局部变量,但编译器只是宣布它为双精度,然后我又回到原点。也许我可以在C#中包含cmath

我如何在C#中定义PI 36个小数点

+3

为了什么目的,你想π的精度高于双精度? – 2015-02-24 21:35:19

+1

使用小数点可以让你更接近一点:'decimal dec = 3.141592653589793238462643383279502884M;',看起来它会给你28个点。过去你可能需要一个库来处理数字这么高的精度。但我也很好奇你为什么需要这么高的精度。 – 2015-02-24 21:36:59

+5

你有没有试过用C++打印这个值?无论是“double”还是“long double”,你都会看到你得到的实际价值并非你所定义的。 – 2015-02-24 21:42:15

回答

1

Here is an article about calculating pi to 1 million digit in C#,您可以缩小它下降到36位

这里是一个示例代码,我建议阅读完整的文章和背后的逻辑。

public static HighPrecision GetPi(int digits) 
{ 
    HighPrecision.Precision = digits; 
    HighPrecision first = 4 * Atan(5); 
    HighPrecision second = Atan(239); 
    return 4 * (first - second); 
} 

,如果你只是想存储PI(不计算它),你可以将值从here复制,并存储在HighPrecision变量。

+1

他不想计算PI,他想***存储*** PI。 (但是这篇文章提供了一个'HighPrecision'类,允许任意的精度小数) – 2015-02-24 21:48:16

+0

所以问题只是缩小到BigDecimal像C#中的类一样? @ScottChamberlain – dotctor 2015-02-24 21:53:59

+0

'HighPrecision'类似乎很有趣。我很想知道@latkin是否已经发布了他的TPL版本:) – rducom 2015-02-24 22:00:46

3

据我知道C#在typedecimal精度最高,但它仅达28-29显著十进制数字。我不认为您可以在不使用自定义库的情况下在C#中覆盖最多36位十进制数字。

+0

您可以将小数存储在BigInteger中... – rducom 2015-02-24 21:41:20

+0

根据文档“与浮点类型相比,小数类型具有更高的精度和更小的范围,因此适用于财务和货币计算“。我不认为Pi被包含在财务/货币计算中,因此数据类型可能不是最适合他的需求的。 – 2015-02-24 21:42:08

+2

@Shared是,但你仍然需要实现你自己的缩放因子。我仍然称它为“自定义库”,它只是你自己写的一个。如果你打算这么做的话,只需使用一个完全实现的[BigDecimal](https://www.nuget.org/packages/BigDecimal/)(给你精确到64K的小数点右边) ),它也实现了'+',''和'*'操作符的逻辑。 – 2015-02-24 21:42:20

1

如果你真的需要那么多的精度,可以使用任意精度的C库像GNU MPCGNU MPFR。由于他们是C函数库,所以你可能需要使用interop使用它们......但是,这会限制你的高精度pi使用使用你选择的套餐...

4

你可以在C++中做什么,而不能在C#中做的是为文字创建一个别名。在C++ #define M_PI 3.141592653589793238462643383279502884中写入不会将PI定义为36个地方。它只是将字面量化。

#define M_PI 3.141592653589793238462643383279502884 
double pi = M_PI; 

这是完全一样的写作

double pi = 3.141592653589793238462643383279502884; 

这将工作在C++和C#几乎相同。在C++中,该值与C#中的精度相同,因为文字将被解释为double,我相信这两者在两者中基本相同。

M_PI被宣布为高达36位的原因是因为这是quad double的最大精度。遗憾的是,在C#中没有如此精确的本机类型。在C++中,除非您将它用作quad double,否则您不会从M_PI获得任何特别的东西。

已经别名文字没有太大的帮助,它只是允许这样写:

float pif = M_PI; 
    int pii = M_PI; 
    double pid = M_PI; 
    quad double piq = M_PI; 

不幸的是,你需要使用自定义类型。

3

我发现这个问题很有趣,所以这里是我的2美分。

我把Machin的公式implementation,应用到大整数的IntXLib实现(它已经对Discrete Hartley transform进行了优化乘除运算符)。

最后,我将结果与Pi value posted here进行了比较。

这里,在i7 920 @ 2.66 Ghz上计算138 ms中的2500个小数。现在

static void Main(string[] args) 
{ 
    String referencePi = "3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989380952572010654858632788659361533818279682303019520353018529689957736225994138912497217752834791315155748572424541506959508295331168617278558890750983817546374649393192550604009277016711390098488240128583616035637076601047101819429555961989467678374494482553797747268471040475346462080466842590694912933136770289891521047521620569660240580381501935112533824300355876402474964732639141992726042699227967823547816360093417216412199245863150302861829745557067498385054945885869269956909272107975093029553211653449872027559602364806654991198818347977535663698074265425278625518184175746728909777727938000816470600161452491921732172147723501414419735685481613611573525521334757418494684385233239073941433345477624168625189835694855620992192221842725502542568876717904946016534668049886272327917860857843838279679766814541009538837863609506800642251252051173929848960841284886269456042419652850222106611863067442786220391949450471237137869609563643719172874677646575739624138908658326459958133904780275900994657640789512694683983525957098258226205224894077267194782684826014769909026401363944374553050682034962524517493996514314298091906592509372216964615157098583874105978859597729754989301617539284681382686838689427741559918559252459539594310499725246808459872736446958486538367362226260991246080512438843904512441365497627807977156914359977001296160894416948685558484063534220722258284886481584560285060168427394522674676788952521385225499546667278239864565961163548862305774564980355936345681743241125"; 
    Stopwatch watch = new Stopwatch(); 
    watch.Restart(); 
    DecimalX calculatedPi = PiHelper.Calculate(2500); 
    watch.Stop(); 
    Console.WriteLine("Pi with 2500 decimals in " + watch.ElapsedMilliseconds + " ms"); 
    String hmmmmm = calculatedPi.ToString(); 
    if (hmmmmm == referencePi) 
     Console.WriteLine("Pi approximation found"); 
} 

public class DecimalX 
{ 
    /// Integer represatation of the decimal 
    private readonly IntX _integerPart; 
    /// Power of 10 (10^X) 
    private readonly uint _scale; 
    public DecimalX(IntX integerPart, uint scale) 
    { 
     _integerPart = integerPart; 
     _scale = scale; 
    } 
    public override string ToString() 
    { 
     IntX afterPoint = null; 
     IntX beforePoint = IntX.DivideModulo(_integerPart, IntX.Pow(10, _scale, MultiplyMode.AutoFht), out afterPoint, DivideMode.AutoNewton); 
     return beforePoint.ToString() + "." + afterPoint.ToString(); 
    } 
} 

public class PiHelper 
{ 
    public static IntX InverseTan(int denominator, int numberOfDigitsRequired) 
    { 
     int demonimatorSquared = denominator * denominator; 
     int degreeNeeded = GetDegreeOfPrecisionNeeded(demonimatorSquared, numberOfDigitsRequired); 
     IntX tenToNumberPowerOfDigitsRequired = IntX.Pow(10, (uint)numberOfDigitsRequired, MultiplyMode.AutoFht); 
     IntX s = IntX.Divide(tenToNumberPowerOfDigitsRequired, new IntX(2 * degreeNeeded + 1), DivideMode.AutoNewton); // s = (10^N)/c 
     int c = 2 * degreeNeeded + 1; 
     for (int i = 0; i < degreeNeeded; i++) 
     { 
      c = c - 2; 
      var temp1 = IntX.Divide(tenToNumberPowerOfDigitsRequired, new IntX(c), DivideMode.AutoNewton); 
      var temp2 = IntX.Divide(s, new IntX(demonimatorSquared), DivideMode.AutoNewton); 
      s = temp1 - temp2; 
     } 
     return IntX.Divide(s, new IntX(denominator), DivideMode.AutoNewton); 
    } 

    private static int GetDegreeOfPrecisionNeeded(int demonimatorSquared, int numberOfDigitsRequired) 
    { 
     int degreeNeeded = 0; 
     while ((Math.Log(2 * degreeNeeded + 3) + (degreeNeeded + 1) * Math.Log10(demonimatorSquared)) <= numberOfDigitsRequired * Math.Log(10)) 
      degreeNeeded++; 
     return degreeNeeded; 
    } 

    public static DecimalX Calculate(int numberOfDigitsRequired) 
    { 
     int max = numberOfDigitsRequired + 8; // To be safe, compute 8 extra digits, to be dropped at end. The 8 is arbitrary 
     var a = IntX.Multiply(InverseTan(5, max), new IntX(16), MultiplyMode.AutoFht); //16 x arctan(1/5) 
     var b = IntX.Multiply(InverseTan(239, max), new IntX(4), MultiplyMode.AutoFht); //4 x arctan(1/239) 
     return new DecimalX(IntX.Divide(a - b, IntX.Pow(10, (uint)8), DivideMode.AutoNewton), (uint)numberOfDigitsRequired); 
    } 
} 

,你只需要实现对DecimalX类型与“正常”类型的所有运营商(如浮点/双/ INT /等..),但我认为这将是容易的!