为什么我在这个简单的C程序上得到这个奇怪的输出?

时间:2014-03-31 21:01:55

标签: c

我正在开发一个个人项目,其中一部分涉及在特定范围内计算正方形和立方体(在本例中为10,000)。所以,我写了一个简单的C程序,我认为可以用来验证我的结果。这是我放在一起查看所有立方体的小程序:

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


int main() {

      double i;
      int cubes = 0;

      for (i = 1; i < 10000; i++) {

        if ( i == cbrt(i) * cbrt(i) * cbrt(i) ) {

             printf("%f --- %f\n",i, cbrt(i));
             cubes++;
         }
       }

     printf("%i\n", cubes);

     return 0;
}

我得到了(不正确的)输出:24。如果你想看看这个问题,请看输出上的数字15和20。为什么我得到了错误的答案(正确答案是21)是一个完全不同的问题。我的问题出现了,当我搞乱我的代码试图解决这个问题时,我暂时将其更改为:

int main() {

    double i;
    int cubes = 0;

    for (i = 1; i < 10000; i++) {

        double temp = (cbrt(i) * cbrt(i) * cbrt(i));

        if ( i == temp ) {

            printf("%f -> %f\n", i, temp);
            cubes++;
        }
     }

   printf("%i\n", cubes);

   return 0;
}

现在,该程序正在打印1到9999之间的每个数字。那么,我是否遗漏了一些非常简单或正在发生的事情?我所做的只是在if条件中设置一个等于result的double变量而不是cbrt(i)*cbrt(i)*cbrt(i)。为什么我的程序会这样做?

我不确定为什么这会被投票。我觉得这是一个合理的问题。对不起S.O.社区...

3 个答案:

答案 0 :(得分:1)

double cbrt(double x)返回x最接近的可表示立方根。

结果的不精确性,然后立方,可能不再完全等于'x'。


为什么第二个程序不同:

C没有义务仅执行double数学 double精度。它可能使用更宽(long double)。根据许多事情,第二个代码似乎在long double中比第一个代码做得更多。通过额外的精确度,很容易看到四舍五入到double的结果看起来很精确。

  

C11dr§5.2.4.2.29除了赋值和强制转换(删除所有额外的范围和精度)之外,具有浮动操作数的运算符产生的值和通常算术转换以及浮动常量的值评估范围和精度可能大于类型所需的格式。


为什么典型程序运行(任一代码)会产生大约3333的结果。

考虑从2到4和8到64的double个数字。double数字以对数方式分布。从2到4的不同double为8到16,16到32为32到64.

所以现在从8到64的所有3组在2到4的1组中都有一些答案的立方根。现在,如果我们将数字2到4进行立方体,我们得到的答案是范围8到64. 1组数字映射到3组。往返不准确。见Pigeonhole principle。 IOW:平均而言,8到64范围内的3个数字具有相同的立方根。那个根的立方体将是3个原始的1个。


查找0到N的完美整数立方体的计数

unsigned Perfect_Cube_Count(unsigned n) {
  if (n == 0)
    return 1;
  unsigned i;
  // overflow not possible
  for (i = 0; i*i < n/i; i++);
  return i;
}

或者

// valid for 0 <= x <= something_well_over_1e9
double Perfect_Cube_Count_d(double x) {
  double y = cbrt(x);
  return floor(y) + 1;
}

答案 1 :(得分:1)

正如安德鲁猜测的那样,你可能想要整数立方根。由于舍入错误,浮动数学非常棘手。通常,您不能依赖相等,但必须与误差范围进行比较。

虽然我预先构造 21个立方体然后迭代整数,然后与预先构建的立方体进行比较,以解决您的问题。还是作弊? ;-)

在Samuel Becket的小说“Watt”中,有一章讲述苏格兰数学天才&#34;谁也可以在他的头脑中计算整数立方体的所有整数第三根,最多10000个左右!

答案 2 :(得分:1)

我的问题是,你的编译器在第二种情况下进行了优化,eli inating cbrt calls。它只是说cbrt的结果是由标准严格定义的,所以它也可能总是(i == temp)

您可以通过一些命令行参数来伪装它,并强制它完全按照代码中的内容执行操作。我记得,这应该是关于浮动关节模型的C编译器的默认操作,但是你的编译器可能认为它比你或者更聪明。

修改

是的,这段代码与找到完美的立方体无关......

修改 完全不是这个问题的答案,但作为一个快速练习,我写了这个:

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

 static unsigned long count_cubes(unsigned long max_n)
 {
     unsigned long n = 1;
     while (n*n*n <= max_n) {
         ++n;
     }
     return n-1;
 }

 int main(int argc, char **argv)
 {
     unsigned long max_n;
     char *p;

     if (argc < 2) {
         return EXIT_FAILURE;
     }
     max_n = strtoul(argv[1], &p, 10);
     if (max_n < 1 || max_n == ULONG_MAX) {
         return EXIT_FAILURE;

     }
     printf("%lu\n", count_cubes(max_n));
     return EXIT_SUCCESS;
 }

注意:不需要浮点运算

修改

对不起,我真的进入了这个......

这个可以快一点:

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

 static unsigned long count_cubes(unsigned long max_n)
 {
     unsigned long n;
     if (max_n < 256) {
         n = 1;
     }
     else {
         n = cbrtl(max_n) - 1;
     }
     while (n*n*n <= max_n) {
         ++n;
     }
     return n-1;
 }

 int main(int argc, char **argv)
 {
     unsigned long max_n;
     char *p;

     if (argc < 2) {
         return EXIT_FAILURE;
     }
     max_n = strtoul(argv[1], &p, 10);
     if (max_n < 1 || max_n == ULONG_MAX) {
         return EXIT_FAILURE;

     }
     printf("%lu\n", count_cubes(max_n));
     return EXIT_SUCCESS;
 }

编辑(上次,我保证......)

要显示上面我的小循环的解释,从cbrt(max_n)-1开始,我尝试了@chux建议的那个,这里有一些数字略大的结果:

PerfectCubes(18446724184312856125) == 2642246

这很好但也

PerfectCubes(18446724184312856125-10) == 2642246

这完全不好,因为18446724184312856125 == 2642245^3,意味着有2642245个完美的立方体&lt; = 18446724184312856125-10。

这也是由浮点表示的不准确造成的。如果您的计算机与我的计算机有些相似,您可以亲自尝试一下:

     printf("%f\n", cbrt( 2642245UL * 2642245UL * 2642245UL));
     /* prints 2642245.000000 */
     printf("%f\n", cbrt( 2642245UL * 2642245UL * 2642245UL - 10UL));
     /* prints 2642245.000000 */

这两个数字显然不具有相同的立方根,但cbrt返回相同的结果。在这种情况下,floor也无济于事。无论如何,总是需要非常小心使用浮点算术。现在我真的应该去睡觉了。