Pi仍然是错误的[关闭]


27

皮错了

计算pi的一种常见方法是将“飞镖”扔到1x1的盒子中,并查看与投掷的总数相比,单位圆中的哪一块土地:

loop
   x = rand()
   y = rand()
   if(sqrt(x*x + y*y) <= 1) n++
   t++
pi = 4.0*(n/t)

编写一个看起来应该正确计算pi的程序(使用此方法或其他常用的pi计算方法),但应计算tau(tau = 2 * pi = 6.283185307179586 ...)。您的代码必须至少产生前6个小数位:6.283185

优胜者在6月6日(从今天起一周)加冕。


43
为什么优胜者未在6月28日加冕?
corsiKa

9
我不确定为什么要在人气竞赛中加冕冠军。
蒂姆·S.

1
我不明白 这就像要求一个似乎返回1但返回的函数一样2。我们在骗谁?
2014年

3
@ ja72代码的阅读者:)
炸毁了

8
人人都知道,保罗是正确的。:P
Justin Krejcha 2014年

Answers:


57

的JavaScript

alert(Math.atan2(0, -0) - Math.atan2(-0, -0) + Math.atan2(0, 0))

帮忙,我被困在Universe工厂中,我不确定自己在做什么。Math.atan2应该返回具有良好值的pi,对吗?Math.atan2(0, -0)返回pi,所以如果我减去它,然后加上它,我仍然应该有pi。


14
我想我要躺下来哭泣。Goddamnit,JavaScript。
杰克M

3
请解释一下?:)
Jaa-c 2014年

2
x轴与点(Y,X)之间的弧度逆时针角度。在Y点的符号确定这是否是正或负的角,并且这成为π - (-π)

8
0_o >>> 0 === -0 ;true ;>>> Math.atan2(0, 0) ;0 ;>>> Math.atan2(0, -0) ;3.141592653589793
伊兹方2014年

5
@JackM,该语句总是很适合说:)尽管在这种情况下,这是由于IEEE标准引起的,并且许多语言(不仅仅是JS)都存在零与负零的问题。
Paul Draper 2014年

40

基本知识

(更具体地说,花栗鼠基础版

这使用了Nilakantha Somayaji在15世纪发现的无穷级数

' Calculate pi using the Nilakantha series:
'               4       4       4       4
'  pi  =  3 + ----- - ----- + ----- - ------ + ...
'             2x3x4   4x5x6   6x7x8   8x9x10
i = pi = 0
numerator = -4
while i<10000
  i = i + 2
  numerator = -numerator
  pi = pi + numerator / (i * (i+1) * (i+2))
wend
pi = pi + 3
print using "#.##########";pi

输出量

6.2831853072

如果您不知道发生了什么,请参考以下提示:

在Chipmunk Basic中,当程序开始运行时,变量pi预设为π的值。

在BASIC中,等号用于分配变量和测试相等性。因此,a = b = c被解释为a =(b == c)


等一下,我不明白,i等于false吗?然后添加2到它?它有效吗???
Dunno 2014年

2
@Dunno:当然,在循环开始时i == false这是类似i == 0。关键是累加器的初始值pi不为0…
Bergi 2014年

1
@Bergi是啊,我只是不能换我的头周围的事实是false + 2 == 2:d
说不上

@Dunno动态类型等:执行算术时,false隐式转换为0。您在C中也具有相同的表观行为,但缺少bool类型,并且使用0和非零来表示falsetrue表示。并不是说它很优雅,而是,它就是这样工作的。
SuzanneDupéron2014年

15

C-半个单位圆的长度

一种计算π的方法就是简单地测量点(1, 0)原点旋转时所经过的距离,(-1, 0)因为它是单位圆周长的一半(即)。

在此处输入图片说明

但是,没有sin(x)或者cos(x)因为这是需要可以这样做步进周围所有的方式起源添加点移动每一步的距离。每一步的大小越小,您将获得越精确的π

注意:当y小于零(正好通过(-1, 0))时,步进将结束。

#include <stdio.h>                          // for printf
#define length(y, x) ((x * x) + (y * y))
int main()
{
    double x, y;
    double pi, tau, step;
    // start at (2, 0) which actually calculates tau
    x  = 2;
    y  = 0;
    // the step needs to be very low for high accuracy
    step = 0.00000001;  
    tau = 0;
    while (y >= 0)
    {   // the derivate of (x, y) is itself rotated 90 degrees
        double dx = -y;
        double dy = x;

        tau += length(dx, dy) * step; // add the distance for each step to tau
        // add the distance to the point (make a tiny rotation)
        x += dx * step;
        y += dy * step;
    }
    pi = tau / 2;   // divide tau with 2 to get pi

    /* ignore this line *\                      pi *= 2;    /* secret multiply ^-^ */

    // print the value of pi
    printf("Value of pi is %f", pi); getchar(); 
    return 0;
}

它给出以下输出:

Value of pi is 6.283185

3
似乎合法……肯定。
bjb568

1
您的length宏缺少sqrt。那是故意的吗? xy在定义和调用之间交换(无效)
Ben Voigt 2014年

@BenVoigt嘘!不要破坏the俩,但是可以。sqrt意外省略,所以pi的值被打印为6,28 ...还要注意+1 xy而我没有!
2014年

1
哦,现在我看到您正在追踪的不是单位圆,而是半径为2的圆。是的,效果很好。
Ben Voigt 2014年

7
我必须承认,在理解其工作原理之前,我浪费了几分钟而不忽略这条线……
loreb 2014年

10

C

(这最终比预期的要长,但是我还是会张贴它……)

在17世纪,Wallis为Pi发行了无穷系列:

在此处输入图片说明

(有关更多信息,请参见 关于π,e和√(2 +√2)的新的Wallis和Catalan型无限产品

现在,为了计算Pi,我们必须首先乘以2得出分母:

在此处输入图片说明

然后,我的解决方案计算出Pi / 2和2的无穷级数,然后将两个值相乘。请注意,在计算最终值时,无限乘积的收敛速度非常慢。

输出:

pi: 6.283182
#include "stdio.h"
#include "stdint.h"

#define ITERATIONS 10000000
#define one 1

#define IEEE_MANTISSA_MASK 0xFFFFFFFFFFFFFULL

#define IEEE_EXPONENT_POSITION 52
#define IEEE_EXPONENT_BIAS 1023

// want to get an exact as possible result, so convert
// to integers and do custom 64-bit multiplication.
double multiply(double aa, double bb)
{
    // the input values will be between 1.0 and 2.0
    // so drop these to less than 1.0 so as not to deal 
    // with the double exponents.
    aa /= 2;
    bb /= 2;

    // extract fractional part of double, ignoring exponent and sign
    uint64_t a = *(uint64_t*)&aa & IEEE_MANTISSA_MASK;
    uint64_t b = *(uint64_t*)&bb & IEEE_MANTISSA_MASK;

    uint64_t result = 0x0ULL;

    // multiplying two 64-bit numbers is a little tricky, this is done in two parts,
    // taking the upper 32 bits of each number and multiplying them, then
    // then doing the same for the lower 32 bits.
    uint64_t a_lsb = (a & 0xFFFFFFFFUL);
    uint64_t b_lsb = (b & 0xFFFFFFFFUL);

    uint64_t a_msb = ((a >> 32) & 0xFFFFFFFFUL);
    uint64_t b_msb = ((b >> 32) & 0xFFFFFFFFUL);

    uint64_t lsb_result = 0;
    uint64_t msb_result = 0;

    // very helpful link explaining how to multiply two integers
    // http://stackoverflow.com/questions/4456442/interview-multiplication-of-2-integers-using-bitwise-operators
    while(b_lsb != 0)
    {
        if (b_lsb & 01)
        {
            lsb_result = lsb_result + a_lsb;
        }
        a_lsb <<= 1;
        b_lsb >>= 1;
    }
    while(b_msb != 0)
    {
        if (b_msb & 01)
        {
            msb_result = msb_result + a_msb;
        }
        a_msb <<= 1;
        b_msb >>= 1;
    }

    // find the bit position of the most significant bit in the higher 32-bit product (msb_answer)
    uint64_t x2 = msb_result;
    int bit_pos = 0;
    while (x2 >>= 1)
    {
        bit_pos++;
    }

    // stuff bits from the upper 32-bit product into the result, starting at bit 51 (MSB of mantissa)
    int result_position = IEEE_EXPONENT_POSITION - 1;
    for(;result_position > 0 && bit_pos > 0; result_position--, bit_pos--)
    {
        result |= ((msb_result >> bit_pos) & 0x01) << result_position;
    }

    // find the bit position of the most significant bit in the lower 32-bit product (lsb_answer)
    x2 = lsb_result;
    bit_pos = 0;
    while (x2 >>= 1)
    {
        bit_pos++;
    }

    // stuff bits from the lowre 32-bit product into the result, starting at whatever position
    // left off at from above.
    for(;result_position > 0 && bit_pos > 0; result_position--, bit_pos--)
    {
        result |= ((lsb_result >> bit_pos) & 0x01) << result_position;
    }

    // create hex representation of the answer
    uint64_t r = (uint64_t)(/* exponent */ (uint64_t)IEEE_EXPONENT_BIAS << IEEE_EXPONENT_POSITION) |
            (uint64_t)( /* fraction */ (uint64_t)result & IEEE_MANTISSA_MASK);

    // stuff hex into double
    double d = *(double*)&r;

    // since the two input values were divided by two,
    // need to multiply by four to fix the result.
    d *= 4;

   return d;
}

int main()
{
    double pi_over_two = one;
    double two = one;

    double num = one + one;
    double dem = one;

    int i=0;

    i=ITERATIONS;
    while(i--)
    {
        // pi = 2 2 4 4 6 6 8 8 ...
        // 2    1 3 3 5 5 7 7 9
        pi_over_two *= num / dem;

        dem += one + one;

        pi_over_two *= num / dem;

        num += one + one;
    }

    num = one + one;
    dem = one;

    i=ITERATIONS;
    while(i--)
    {
        // 2 = 2 4 4 6   10 12 12 14
        //     1 3 5 7    9 11 13 15
        two *= num / dem;

        dem += one + one;
        num += one + one;

        two *= num / dem;

        dem += one + one;

        two *= num / dem;

        dem += one + one;
        num += one + one;

        two *= num / dem;

        dem += one + one;
        num += one + one + one + one;
    }

    printf("pi: %f\n", multiply(pi_over_two, two));

    return 0;
}

实际上不能忽略double转换中的指数。如果那是唯一的变化(除以2,除以4,则是整数乘法),那么一切都会令人惊讶地起作用。


8

Java-Nilakantha系列

Nilakantha系列的公式为:

pi = 3 + 4/(2*3*4) - 4/(4*5*6) + 4/(6*7*8) - 4/(8*9*10) ...

因此,对于每一项,分母是通过将连续的整数相乘而构成的,其中每一项的起始数增加2。请注意,您添加/减去交替项。

public class NilakanthaPi {
    public static void main(String[] args) {
        double pi = 0;
        // five hundred terms
        for(int t=1;t<500;t++){
            // each i is 2*term
            int i=t*2;
            double part = 4.0 / ((i*i*t)+(3*i*t)+(2*t));
            // flip sign for alternating terms
            if(t%2==0)
                pi -= part;
            else
                pi += part;
            // add 3 for first term
            if(t<=2)
                pi += 3;
        }
        System.out.println(pi);
    }
}

经过五百项,我们可以得出pi的合理估计值:

6.283185311179568

4

C ++:Sangamagrama的Madhava

这个无穷级数现在称为Madhava-Leibniz

系列

从48的平方根开始,然后将其乘以(-3)-k /(2k +1)的和。实现起来非常简单明了:

long double findPi(int iterations)
{
    long double value = 0.0;

    for (int i = 0; i < iterations; i++) {
        value += powl(-3.0, -i) / (2 * i + 1);
    }

    return sqrtl(48.0) * value;
}

int main(int argc, char* argv[])
{
    std::cout << "my pi: " << std::setprecision(16) << findPi(1000) << std::endl;

    return 0;
}

输出:

my pi: 6.283185307179588

3

Python-Nilakantha系列的替代品

这是另一个很容易理解的计算pi的无穷级数。

在此处输入图片说明

对于此公式,取6并开始在分子为2的分母加分母和分母之间进行交替,分母是两个连续整数与其和的乘积。随后的每个小数部分开始以1递增的整数集。将其执行几次,结果将非常接近pi。

pi = 6
sign = 1
for t in range(1,500):
i = t+1
   part = 2.0 / (i*t*(i+t))
   pi = pi + sign * part
   sign = - sign # flip sign for alternating terms  
print(pi)

得到6.283185。


-1
#include "Math.h"
#include <iostream>
int main(){
    std::cout<<PI;
    return 0;
}

Math.h:

#include <Math.h>
#undef PI
#define PI 6.28

输出:6.28

#include“ Math.h”与#include不同,但仅通过查看主文件,几乎没有人会想检查。也许很明显,但是类似的问题出现在我正在从事的项目中,并且很长一段时间都未被发现。


尽管如此,还是一个聪明的解决方案。
BobTheAwesome 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.