如何用C计算pow()?

时间:2016-11-27 00:42:19

标签: c pow math.h

我们的教授说,如果使用search()&lt; 0,则无法计算 b ,因为public class App { public static void main(String[] args) { Scanner s = new Scanner(System.in); int[] list = new int[5]; for (int i = 0; i < 5; i++) { System.out.println("Enter number: "); int n = s.nextInt(); if ((n >= 10 && n <= 100) && search(list, n) == 0) { list[i] = n; System.out.println("It has been entered."); } else i--; } System.out.println("Result = " + Arrays.toString(list)); s.close(); } public static int search(int[] list, int key) { int count = 0; for (int i = 0; i < list.length; i++) { if (list[i] == key) { count++; } } return count; } } 使用自然对数来计算它( b < / sup> = e b ln a )由于它未定义为负数,因此无法计算。我尝试了它,只要b是一个整数就可以工作。

我搜索了pow()和其他文件,但无法找到函数的定义方式以及用于计算的内容。我也试过在互联网上搜索,但没有任何成功。 Stack Overflow右侧herehere(对于C#)也有类似的问题。 (最后一个很好,但我找不到源代码。)

所以问题是pow()实际上是如何用C计算的?当基数是有限的且负数且指数是有限且非整数时,为什么它会返回域错误?

4 个答案:

答案 0 :(得分:12)

如果您对如何在实践中实现pow功能感到好奇,可以查看源代码。搜索不熟悉的(和大的)代码库以找到您正在寻找的部分有一种“诀窍”,并且进行一些练习是很好的。

C库的一个实现是glibc,它在GitHub上有镜像。我没有找到官方镜子,但是非正式的镜子在https://github.com/lattera/glibc

我们首先查看具有良好名称的math/w_pow.c文件。它包含一个调用__pow的函数__ieee754_pow,我们可以在sysdeps/ieee754/dbl-64/e_pow.c中找到它(请记住,并非所有系统都是IEEE-754,因此IEEE-754数学代码是有意义的。在它自己的目录中。)

首先是一些特殊情况:

if (y == 1.0) return x;
if (y == 2.0) return x*x;
if (y == -1.0) return 1.0/x;
if (y == 0) return 1.0;

稍远一点,你会找到一个带评论的分支

/* if x<0 */

这导致我们

return (k==1)?__ieee754_pow(-x,y):-__ieee754_pow(-x,y); /* if y even or odd */

因此,您可以看到,对于否定x和整数y,glibc版pow将计算pow(-x,y),然后在y时将结果设为负数是奇怪的。

这不是唯一的做法,但我的猜测是,这对许多实现来说都很常见。您可以看到pow充满了特殊情况。这在图书馆数学函数中很常见,它们可以正常使用非正规输入(如非正规和无穷大)。

pow函数特别难以阅读,因为它是经过大量优化的代码,可以对浮点数进行微调。

C标准

C标准(n1548§7.12.7.4)有关于pow的说法:

  

如果x为有限且为负且y为有限且不是整数值,则会发生域错误。

因此,根据C标准,否定x 工作。

还有附录F的问题,它对pow在IEEE-754 / IEC-60559系统上的工作方式提出了更为严格的限制。

答案 1 :(得分:6)

第二个问题(为什么它会返回域错误)已在评论中介绍,但添加完整性:pow需要两个实数并返回一个实数。在负数上应用有理指数会使您从实数域进入复数域,这个函数的结果( double )无法表示。

如果您对实际实施感到好奇,那么有很多,这取决于许多因素,例如架构和优化级别。很容易找到一个易读的,但是FDLIBM(Freely Distributable LIBM)has one which has at least has a good explanation in the comments

/* __ieee754_pow(x,y) return x**y
 *
 *            n
 * Method:  Let x =  2   * (1+f)
 *  1. Compute and return log2(x) in two pieces:
 *      log2(x) = w1 + w2,
 *     where w1 has 53-24 = 29 bit trailing zeros.
 *  2. Perform y*log2(x) = n+y' by simulating muti-precision 
 *     arithmetic, where |y'|<=0.5.
 *  3. Return x**y = 2**n*exp(y'*log2)
 *
 * Special cases:
 *  1.  (anything) ** 0  is 1
 *  2.  (anything) ** 1  is itself
 *  3.  (anything) ** NAN is NAN
 *  4.  NAN ** (anything except 0) is NAN
 *  5.  +-(|x| > 1) **  +INF is +INF
 *  6.  +-(|x| > 1) **  -INF is +0
 *  7.  +-(|x| < 1) **  +INF is +0
 *  8.  +-(|x| < 1) **  -INF is +INF
 *  9.  +-1         ** +-INF is NAN
 *  10. +0 ** (+anything except 0, NAN)               is +0
 *  11. -0 ** (+anything except 0, NAN, odd integer)  is +0
 *  12. +0 ** (-anything except 0, NAN)               is +INF
 *  13. -0 ** (-anything except 0, NAN, odd integer)  is +INF
 *  14. -0 ** (odd integer) = -( +0 ** (odd integer) )
 *  15. +INF ** (+anything except 0,NAN) is +INF
 *  16. +INF ** (-anything except 0,NAN) is +0
 *  17. -INF ** (anything)  = -0 ** (-anything)
 *  18. (-anything) ** (integer) is (-1)**(integer)*(+anything**integer)
 *  19. (-anything except 0 and inf) ** (non-integer) is NAN
 *
 * Accuracy:
 *  pow(x,y) returns x**y nearly rounded. In particular
 *          pow(integer,integer)
 *  always returns the correct integer provided it is 
 *  representable.
 *
 * Constants :
 * The hexadecimal values are the intended ones for the following 
 * constants. The decimal values may be used, provided that the 
 * compiler will convert from decimal to binary accurately enough 
 * to produce the hexadecimal values shown.
 */

因此,简而言之,该机制与您所描述的一样,并依赖于首先计算对数,但需要考虑许多特殊情况。

答案 2 :(得分:3)

假设x86系列处理器,pow相当于

double pow(double base, double exp)
{
   return exp2(exp * log2(base));
}

其中exp2log2是基数2中指数和对数运算的CPU基元。

不同的CPU固有地具有不同的实现。

理论上,如果你没有pow,你可以写:

double pow(double base, double exponent)
{
   return exp(exponent * log(base));
}

但是由于累积的舍入,这会比原生版本失去精确度。

Dietrich Epp透露我错过了一堆特殊情况。尽管如此,我还是应该允许我们对即将结束的事情说些什么。

答案 3 :(得分:2)

对于负数,

pow 可以正常工作。当基数为负且指数不是整数时,它就不起作用。

x / y 形式的数字实际上涉及x的第y个根。例如,当您尝试计算 1/2 时,您实际上是在寻找a的平方根。

那么如果你有一个负基数和一个非整数指数会怎么样?你得到一个负数的第y个根,其产生的是一个复数非实数。 pow()不适用于复数,因此它可能会返回NaN。