没有获得常数的计算值e

时间:2017-02-06 21:57:30

标签: c

不确定这里的问题是什么。我知道我的因子函数是正确的,因为我单独测试它。但计算e的功能正在绊倒我。我所要做的就是在计算每个阶乘后添加所有值。但我无法将其转换为C代码。问题肯定在于我的第二个功能。任何帮助或指示将不胜感激。

#include <stdio.h>
#include <math.h>

#define NOERROR 0
#define DECIMAL_PLACES 16
#define EXPECTED_E 2.7182818284590452L

long calcFactorial(int);
double calcE(int);

long calcFactorial(int n)
{
    long sum = 0;
    sum = n;

    if(n == 0)
    {
        return 1;
    }
    else
    {
        while(n != 1)
        {
            sum = sum * (n - 1);
            n = n - 1;
        }
        printf("factorial sum: %ld\n", sum);
        return sum;
    }
}

double calcE(int n)
{
    double e = 0;
    int counter = 0;

    for (counter = 0; counter < DECIMAL_PLACES; counter++)
    {
        e = e + (1/calcFactorial(n));
        n--;
    } 

    printf("Expected e value: %0.16Lf\n", EXPECTED_E);
    printf("Calculated e value: %0.16d\n", e);

    return e;
}

int main()
{
    calcE(10);
}

2 个答案:

答案 0 :(得分:3)

您的代码中有很多错误:

  • 使用long存储浮点结果。使用double
  • 传递n但使用更大的值进行循环:n在一段时间后变为负数:无限循环
  • e = e + (1/calcFactorial(counter));大部分时间都会向e添加0,因为calcFactorial会返回一个整数(long
  • EXPECTED_E常量后缀为L,表示long。不是你想要的。

修正版:

#include <stdio.h>
#include <math.h>

#define NOERROR 0
#define DECIMAL_PLACES 16
#define EXPECTED_E 2.7182818284590452

long calcFactorial(int);
void calcE(int);

long calcFactorial(int n)
{
    long sum = 0;
    sum = n;

    if(n == 0)
    {
        return 1;
    }
    else
    {
        while(n != 1)
        {
             sum *= (n - 1);
             n = n - 1;
        }
    return sum;
    }
}

void calcE(int n)
{
    double e = 0;
    int counter = 0;

    for (counter = 0; counter < n; counter++)
    {
        e = e + (1.0/calcFactorial(counter));
    } 

    printf("Expected e value: %0.16lf\n", EXPECTED_E);
    printf("Calculated e value: %0.16lf\n", e);
}

int main( )
{
    calcE(10);
}

此代码输出:

Expected e value: 2.7182818284590451
Calculated e value: 2.7182815255731922

注意:您被限制在n的给定最大值,因为在此之后您会溢出long。也许可以考虑使用long longunsigned long long作为阶乘部分(即使你受到严格限制)。

答案 1 :(得分:1)

让 - 弗朗索瓦·法布尔很好地突出了你的形式错误,但目前还没有用整数计算直到最后的分区 - 当然,这必须用浮点数来完成。这个技巧可以通过一个名为binary splitting的方法完成,令我惊讶的是,它的原生双打非常好,只有一个十进制数字。它实现起来也非常简单(下面的代码在考虑易读性的情况下编写)。

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#define BS_AFU 0
#define BS_AOK 1

static int exp1_bin_split(uint64_t a, uint64_t b, uint64_t *P, uint64_t *Q){
  int err = BS_AOK;

  uint64_t p1, q1, p2, q2, t1, one;
  one = 1UL;

  t1 = b - a;
  if(t1 == one){
     *P = one;
     *Q = b;
     return err;
  }
  t1 = (a + b) >> 1;

  err = exp1_bin_split(a, t1, &p1, &q1);
  if(err != BS_AOK){
    return err;
  }
  err = exp1_bin_split(t1, b, &p2, &q2);
  if(err != BS_AOK){
    return err;
  }

  *P = q2 * p1 + p2;
  *Q = q1 * q2;

  return err;
}

#include <float.h>

static int exp1(double *a){
  int err = BS_AOK;
  uint64_t p = 0UL, q = 0UL, zero = 0UL;
  double dp, dq;

  // DBL_DIG + 2 = 17 here on my machine
  // had DBL_DIG + 1 first but found out via T&E that
  // one more is still inside the precision of a binary64
  err = exp1_bin_split(zero, DBL_DIG + 2, &p, &q);
  if(err != BS_AOK){
    return err;
  }

  p = p + q;

  dp = (double) p;
  dq = (double) q;

  *a = dp/dq;

  return err;
}


int main(void){
  double e = 0.0;
  int err = BS_AOK;

  err = exp1(&e);
  if(err != BS_AOK){
    fprintf(stderr,"Something went wrong in computing e\n");
    exit(EXIT_FAILURE);
  }

  printf("exp(1) ~ 2.7182818284590452353602874713526624978\nexp1   ~ %.20g\n",e);

  exit(EXIT_SUCCESS);
}

它使用与您相同的算法,但不计算各个分数并将它们总结为浮点数,而是使用整数一次性完成所有操作,使得我们在末尾有一大部分类似于{{1的近似值}}。这个解释有点过于简单,请阅读链接的文章了解详细信息。