使用Java对浮点数进行指数幂运算

4

我有一个类型为float的变量x,需要将其提高到某个幂次p。如果x是一个double型变量,我可以使用Math.pow(x, p)方法。但如果要计算x^p并且结果仍然是float类型,是否有可能呢?


使用相同的方法。它将被隐式转换为“double”。 - Maroun
3
转换为double存在什么问题? - Absurd-Mind
不使用标准JDK。但你为什么不只是使用 double 呢? - fge
@fge:不使用双精度浮点数的原因是我正在扩展一个仅使用单精度浮点数的框架。 - snakile
@EricPostpischil:请将您的评论发布为答案,我会接受它。 - snakile
显示剩余3条评论
3个回答

6
在使用双精度计算 pow 并转换为浮点数时,几乎总是会产生与计算单精度的 pow 相同的结果。要理解这一点,请考虑数学上准确的值 xp。如果采用四舍五入模式,将其正确地舍入为双精度数,然后再正确地舍入为浮点数,结果与直接舍入为浮点数相同,除非将值从一个舍入边界移动到另一个舍入边界。
这些边界仅存在于两个可表示值之间的中点,即尾数的第24位(将最高位视为第0位)。但是,双精度数的舍入发生在第53位。因此,只有当第24至53位具有特定值时,将双精度数舍入可能导致该值越过浮点数舍入边界,在下面这些情况中显示了这种特定值。
[需要有人检查这些内容;容易出错。]
情况0:
               Bit 23 24 25-52 53 54…
Original            1  0 11…11  1 anything
Rounded to double   1  1 00…00  0 0… (53 above midpoint: rounds up, carries to higher bits)
Then to float       0  0 00…00  0 0… (24 at midpoint, 23 is odd: rounds up, carries into bit 22, not shown)
Directly to float   1  0 00…00  0 0… (24 below midpoint: rounds down)

案例1:

               Bit 23 24 25-52 53 54…
Original            0  1 00…00  0 anything except all zeroes
Rounded to double   0  1 00…00  0 0… (53 below midpoint: rounds down)
Then to float       0  0 00…00  0 0… (24 at midpoint, 23 is even: rounds down)
Directly to float   1  0 00…00  0 0… (24 above midpoint: rounds up)

情景2:

               Bit 23 24 25-52 53 54…
Original            0  1 00…00  1 0…
Rounded to double   0  1 00…00  0 0… (53 at midpoint, 52 is even: rounds down)
Then to float       0  0 00…00  0 0… (24 at midpoint, 23 is even: rounds down)
Directly to float   1  0 00…00  0 0… (24 above midpoint: rounds up)

第0种情况需要31位具有特定值,因此在假设值有效均匀分布的情况下,它发生的概率是231中的一次。第1种情况与第0种情况相同,只是还需要在无限多个位中的任何位置出现一个一位,其概率有效为1。第2种情况要求无限多个位为零,因此其概率为零。至少在美学上,第1种和第2种情况是相互呼应的。

这些情况发生的综合概率为230中的1。

因此,将double结果四舍五入为float,产生与直接计算float结果不同的情况很少见。

除此之外,大多数pow实现都是不完美的。它们并不知道在所有情况下返回正确舍入的结果。(这很难实现。) 因此,您可能仍然会得到不完美的结果。将结果舍入为double,然后再舍入为float不会产生明显的差异。


2
你可以写一个小函数来实现它。
public float power(final float base, final int power) {
    float result = 1;
    for( int i = 0; i < power; i++ ) {
        result *= base;
    }
    return result;
}

编辑:一些额外的测试

正如评论中的人们所指出的,如果生成的浮点数太大而无法存储在浮点值中,这将返回错误。

以下是主要方法:

public static void main(final String[] args) {
    System.out.println( power(Float.MAX_VALUE, 2));
}

给我结果:
Infinity

显然,我的解决方案有其局限性。
编辑:进一步阅读
为了避免目前的限制,我想知道是否可以在计算过程中使用双精度浮点数,然后在返回之前将它们转换为单精度浮点数。可以通过使用 Math.pow() 或修改上面的代码来实现。请记住,这可能会导致一些精度问题,这些问题在这篇其他 SO 帖子中有所解释。this other SO post

你认为 float ^ someX 总是会返回一个 float 吗? - Ravinder Reddy
1
对于大多数整数幂的情况,该实现比使用double参数的 pow() 的合理实现更糟糕。 - Pascal Cuoq
如果 result*base 返回一个超过 float 可以表示的最大值的数字,那么这个方法将会导致溢出并返回虚假结果。 - BackSlash
我在更正问题时打错了一个字。你原来的解决方案是正确的,我确实希望结果是浮点数。如果没有更好的答案很快出现,我会接受你的答案。 - snakile
@snakile 很好,我已经删除了关于结果为双精度的部分。 - Dan Temple

0

你仍然可以使用

Math.pow(x, p);

即使您没有明确地进行转换,您的浮点数x也将被转换为双精度。问题在于当您将返回值作为双精度并对其进行操作时会出现问题。

编辑: 如果需要一个返回浮点数的方法,您仍然可以使用Math.pow,然后包括从双精度转换回浮点数并包括自己的逻辑。

public float myPow(float x, float p) {
    double dblResult = Math.pow(x, p);
    float floatResult = (float)dblResult; // <-- Change to something safe. It may easily overflow. 
    return floatResult;
}

谢谢,但实际上我需要结果是浮点数。我已经对问题进行了编辑以澄清这一点。 - snakile
你仍可以使用Math.pow并处理结果(例如将其强制转换回浮点数,但要注意溢出)。无论你选择哪种解决方案,都需要进行一些处理或编码,而如何处理更多地是个人品味的问题。 - DanielBarbarian
我认为你打错了,因为现在问题说你想要将结果作为双精度。 - DanielBarbarian
你是对的。我修复了它。我想要结果作为浮点数。 - snakile
不需要注释“更改为安全内容”或进行任何更改。如果在 (float)dblResult 转换中溢出,它将导致 inf,这就是如果存在 float 版本的 pow 将为相同的参数返回的东西。 - Pascal Cuoq
@PascalCuoq 或许是这样,但我想标记(毫无疑问地)将发生更改的行,以根据要求实现功能(由于我不知道所有细节,因此我不会有这些要求),同时我也想提供一种实际上也可以正确编译的方法。 - DanielBarbarian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接