递归幂函数给出了奇怪的答案

时间:2017-04-15 00:25:25

标签: java recursion memory double exponential

比我更好的程序员。这不是什么大不了的事,但我对这个功能感到好奇,更重要的是它有时给出的结果。所以我为家庭作业定义了一个递归幂函数,包括负指数。对于正值和0,它工作正常,但是当我输入一些负值时,答案真的很奇怪。这是功能:

public static double Power(int base, int exp){
    if (exp == 0)
        return 1.0;
    else if(exp >=1)
        return base * Power(base, exp - 1);
    else 
        return (1.0/base) * Power(base, exp + 1);            
}

因此,对于调用Power(5,-1),函数返回0.2,就像它应该的那样。但是对于说Power(5,-2),该函数返回0.04000000000000001而不是0.04。 同样,这不是一个大问题,因为它是为家庭作业而不是“现实生活”,但只是好奇为什么会发生这种情况。我认为它与计算机内存或双值存储有关,但实际上并不知道。谢谢大家!

PS,如果有所不同,可以使用Netbeans在Java中编码。

2 个答案:

答案 0 :(得分:0)

通过仔细组织算术可以减少浮点舍入误差。通常,您希望最小化舍入操作的数量,以及对舍入结果执行的计算次数。

我对你的功能做了一些小改动:

  public static double Power(int base, int exp) {
    if (exp == 0)
      return 1.0;
    else if (exp >= 1)
      return base * Power(base, exp - 1);
    else
      return (1.0 / Power(base, -exp));
  }

对于您的测试用例Power(5, -2),这只会进行一次舍入计算,即递归顶部的除法。它得到最接近的1 / 25.0倍,打印为0.04。

答案 1 :(得分:0)

这是1990年代的事情

这可能是一个被忽视或有争议的答案,但我认为需要说明。

其他人则关注“浮点”计算(例如涉及一个或多个“双打”数字的计算)近似数学的信息。

我对这个答案的关注点在于,即使普通Java代码中的这种方式,以及大多数编程语言中的普通代码,使用0.1等数字的计算也不必是近似的

一些语言将0.1之类的数字视为有理​​数,两个整数之间的比率(分子超过分母,在这种情况下为1超过10或十分之一)就像它们一样在学校数学。只涉及整数和有理数的计算是100%准确的(忽略整数溢出和/或OOM)。

不幸的是,如果分母变得过大,理性计算会在病理上变慢。

有些语言采取妥协的立场。他们将一些理性视为有理数(因此具有100%的准确性),只有在理性计算在病理上缓慢时,才会放弃100%的准确性,转换为浮点数。

例如,这里有一些相对较新且具有前瞻性的编程语言的代码:

sub Power(\base, \exp) {
    given exp {
        when 0       { 1.0                             }
        when * >= 1  { base     * Power(base, exp - 1) }
        default      { 1.0/base * Power(base, exp + 1) }
    }
}

这会以其他语言复制您的代码。

现在使用此函数获取指数列表的结果:

for 1000,20,2,1,0,-1,-2,-20,-1000 -> \exp { say Power 5, exp }

Running this code in glot.io显示:

9332636185032188789900895447238171696170914463717080246217143397959
6691097577563445444032709788110235959498993032424262421548752135403
2394841520817203930756234410666138325150273995075985901831511100490
7962651131182405125147959337908051782711254151038106983788544264811
1946981422866095922201766291044279845616944888714746652800632836845
2647429261829862165202793195289493607117850663668741065439805530718
1363205998448260419541012132296298695021945146099042146086683612447
9295203482686461765792691604742006593638904173789582211836507804555
6628444273925387517127854796781556346403714877681766899855392069265
4394240087119736747017498626266907472967625358039293762338339810469
27874558605253696441650390625
95367431640625
25
5
1
0.2
0.04
0.000000000000010
0

以上结果100%准确 - 直到最后一个指数-1000。如果我们检查结果的类型(使用WHAT),我们可以看到语言放弃了100%的准确度:

for 1000,20,2,1,0,-1,-2,-20,-1000 -> \exp { say WHAT Power 5, exp }

显示:

(Rat)
(Rat)
(Rat)
(Rat)
(Rat)
(Rat)
(Rat)
(Rat)
(Num)

Rat s(默认的理性类型)转换为FatRat s(任意精确的有理类型),即使使用病态较大的分母,也可以避免不准确:

sub Power(\base, \exp) {
    given exp {
        when 0       { 1.0.FatRat                             }
        when * >= 1  { base            * Power(base, exp - 1) }
        default      { 1.0.FatRat/base * Power(base, exp + 1) }
    }
}

这产生与原始代码相同的显示,除了最后的计算结果:

0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011

我不知道这是否准确,但是,aiui,它应该是。