在C中获得错误的乘法结果

时间:2019-03-22 17:01:10

标签: c floating-point

我正在使用C语言使用Archimedes方法以80位小数的精度计算pi。 This video可能会阐明该方法。

我基本上是从直径为2的正六边形开始,边的尺寸为1,然后做 Pi =周长/直径,然后将尺寸数加倍,从而得出多边形每当我的程序计算出它时,它就会接近一个圆。

代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main()
{
    long double pi, circunferencia, s, s1, a, b, diametro;
    int lado = 6;

    s=1;
    diametro = 2;

    while (lado<=(6*pow(2,27))){

        printf("numero de lados =%d\n", lado);

        a= sqrt(1-(s/2)*(s/2));
      //  printf("a= %.80Lf\n", a);
        b= 1 - a;
      //  printf("b= %.80Lf\n", b);
        s1= sqrt ((b*b)+((s/2)*(s/2)));
      //  printf("c= %.80Lf\n", s1);


        lado = lado*2;
        s = s1;
        circunferencia = lado * s1;
        pi = circunferencia/diametro;
        printf("\npi= %.80Lf\n\n\n", pi);

    }

    return 0;
}

使用2 27 ,我的精度达到15位小数,并且 lado =805306368。如果我执行2 28 或更高,这部分的乘积是错误的,我开始得到负数。一切都搞砸了, lado 突然变成了0,变成了无限循环。

我一般是C和程序设计的初学者,如果你们可以推荐给我一些有关该主题的文章,以了解它的工作原理以及我为什么得到此错误,那就太棒了。

我使用的是Code :: Blocks版本17.12,GNU GCC编译器(我启用了 gcc遵循2011 ISO C语言标准[-std = c11] ”选项,因为我无法不要使用"%Lf"来引用long double变量)和Windows 10 x64操作系统。

1 个答案:

答案 0 :(得分:4)

  

如果我的分数是2 ^ 28或更高,则乘法在这一部分会出错,

lado<=(6*pow(2,27))还可以,但是lado = lado*2;大约在此时开始溢出int数学。

一旦lado > INT_MAX/2(可能是1,073,741,823),int就会发生lado*2溢出,并且可能发生任何事情。对于OP,lado最终变为0。

代码可以使用long long

// int lado = 6;
long long lado = 6;
...
  // printf("numero de lados =%d\n", lado);
  printf("numero de lados =%lld\n", lado);

这将使立即循环问题再修补约20或 这样循环。


我怀疑代码是否可以使用long double(可能是17-34个十进制数字的精度)来实现“以80个十进制的精度计算pi”。


OP报告“我的老师说我最多可以存储80位数字,并具有长双精度”,这意味着{long double为80位 x86 extended precision format或十进制为20位位数。

我建议使用long double个函数,而不要使用double个函数。

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main() {
  long double pi, circunferencia, s, s1, a, b, diametro;
  long long lado = 6;

  s = 1;
  diametro = 2;

  while (lado <= (6 * pow(2, 27))) {
    printf("numero de lados =%lld\t", lado);

    // a = sqrt(1 - (s / 2) * (s / 2));
    a = sqrtl(1 - (s / 2) * (s / 2));
    b = 1 - a;
    // s1 = sqrt((b * b) + ((s / 2) * (s / 2)));
    s1 = hypotl(b, s / 2);

    lado = lado * 2;
    s = s1;
    circunferencia = lado * s1;
    pi = circunferencia / diametro;
    printf("pi= %.40Lf\n", pi);
  }
  printf("best                      \tpi= %.40Lf\n", acosl(-1.0));
  return 0;
}

样本输出

numero de lados =6  pi= 3.1058285412302491482368360653509853364085
numero de lados =12 pi= 3.1326286132812381970275117382129792531487
numero de lados =24 pi= 3.1393502030468672071242958176995330177306
....
numero de lados =201326592  pi= 3.1415926535897932057699033503439522974077
numero de lados =402653184  pi= 3.1415926535897932296223511450250498455716
numero de lados =805306368  pi= 3.1415926535897932356938833109438746760134
best                        pi= 3.1415926535897932385128089594061862044327
// in my case good to about     1 23456789012345678 (18 digits)