使用FloatMath还是Math和强制转换?

37
在Android API的最新更新中,FloatMath被标记为以下lint警告:
在旧版本的Android中,使用android.util.FloatMath在处理浮点数时出于性能考虑是推荐的。然而,在现代硬件上,双精度浮点数与单精度浮点数一样快(尽管它们需要更多的内存),并且在最近的Android版本中,由于JIT优化java.lang.Math的方式,FloatMath实际上比使用java.lang.Math慢。因此,如果您只针对Froyo及以上版本进行开发,则应使用Math代替FloatMath。 here还提到,双精度浮点数和单精度浮点数在现代硬件上的速度相等。
我正在一个应用程序中使用一些三角函数数学运算(针对Froyo及以上版本),但不需要高精度,所以到目前为止我一直在使用浮点数和FloatMath,并且没有必要切换到双精度。
然而,“使用Math而不是FloatMath”建议并未说明如果需要浮点数结果应该使用哪一个。
简而言之,哪一个更可取?
float foo = FloatMath.sin(bar);

或者

float foo = (float) Math.sin(bar);

顺便说一句,我只有一个Froyo设备,所以我无法进行适当的基准测试。

从API级别22开始,FloatMath类已被弃用,建议使用通常的Math类。


我认为你引用的那段文字的意思是,如果你想得到浮点数结果,你应该使用 math 而不是 floatmath 并进行转换。 - aleph_null
2
我会对任何关于int/float/double相对性能的说法持怀疑态度。float可能会因为较小的缓存占用而略微更快,在iPhone 3GS上,float则明显更快,因为它可以使用NEON单元而不是较慢的VFP单元(NEON单元不支持双精度浮点数);这可能是iDevice特定的。此外,自动向量化JIT将自动使floatdouble更受益,因为它可以在每个向量寄存器中容纳更多的float - tc.
最新的Android设备上,浮点数是否都以双精度存储?! - Philip Guin
等等,所以在API 22之前,排除FloatMath的话会更好,现在Math更好吗?如果是这样,根据API使用更好的解决方案不是更有意义吗? - android developer
4个回答

14

从下面的结果可以看出,对于浮点数而言,使用java.lang.Math比使用双精度或FloatMath更快。此外,在API 17之前,FloatMath没有.exp()或.pow()函数。

在三星GT_i9295 (4.2.2)上,需要2^24个循环。

Math.exp(D)      Total:     7405 ms,     Per Op: 0.0004414 ms
(F)Math.exp(F)   Total:     5153 ms,     Per Op: 0.0003071 ms
FloatMath.exp(F) Total:     8533 ms,     Per Op: 0.0005086 ms

三星设备上没有Math.sin的数据,因为它已经随机决定忽略Log.d() >:(

在一台HTC Hero_HT99VL (2.3.7)上进行了2^12个循环

Math.sin(D)      Total:       42 ms,     Per Op: 0.0102539 ms
(F)Math.sin(F)   Total:       33 ms,     Per Op: 0.0080566 ms
FloatMath.sin(F) Total:       38 ms,     Per Op: 0.0092773 ms

Math.exp(D)      Total:       56 ms,     Per Op: 0.0136719 ms
(F)Math.exp(F)   Total:       47 ms,     Per Op: 0.0114746 ms

FloatMath.exp()、.pos()和.hypot()需要API级别17


5

FloatMath文档中写道:

这些数学函数类似于Math类。它们直接对float值进行计算,而不需要将其转换为double类型,因此可以避免转换带来的开销。

而您的引用说:

在操作float时,建议使用android.util.FloatMath以提高性能。

可以推测,使用FloatMath的好处一直是针对需要float的情况,但这个优势现在已经被抵消了。

因此,请使用:

float foo = (float) Math.sin(bar);

此外请注意,如果性能如此重要以至于您需要担心这个问题,也许转换为double仍是必要的(以避免产生转换成本)。

如果现在操作双精度浮点数和单精度浮点数没有区别,但是将单精度浮点数转换为双精度浮点数或反之仍会产生开销。因此,使用 Math 类会涉及从一种类型转换为另一种类型,而 FloatMath 类将直接操作单精度浮点数。那么为什么使用 Math 更好呢? - Egor
一些 Math 方法需要 double 类型的参数,因此也会发生一些隐式转换。 - Jave
3
@Egor,我不知道为什么使用 Math 更好的具体细节,除了已经提到的内容,我只是想说文档和 lint 警告似乎暗示你应该优先选择 Math 而不是 FloatMath,即使考虑到转换成本。另外,在一般情况下,使用 double 替代 float推荐的,除非空间是一个问题。 - kabuko

2
我最近刚好在研究同一个问题,发现了这个错误报告。如以下引用所示,Math函数的性能比FloatMath函数高出一个数量级:

使用DDMS,我对相关代码进行了分析。以下每个函数被调用了100多次。

       Name                 | Cpu Time / Call
----------------------------------------------
java/lang/Math.sin (D)D     | 0.005
java/lang/Math.cos (D)D     | 0.007
java/lang/Math.sqrt (D)D    | 0.004
android/util/FloatMath.sin  | 0.017
android/util/FloatMath.cos  | 0.017
android/util/FloatMath.sqrt | 0.016

如果你跟随 AOSP 树上的文档更改,你会在这里看到,在具有 JIT 的 Android 版本中(基本上是从 Froyo (2.2) 开始),优先使用 Math 函数,而不是 FloatMath

-1
如果性能很重要,那么你可能不想浪费时间在每次计算时都进行 double 类型的转换。
据我所知,在旧硬件上,float 类型比 double 类型更快,因此需要使用 float 类型的 Math 库。现在,“在现代硬件上,double 类型与 float 类型一样快”,因此应该使用默认的 double 类型的 Math 库。
如果对于你的应用程序来说,值为 float 类型很重要(例如,由于内存消耗),那么你应该继续使用 FloatMath,因为如果你经常使用“float foo = (float) Math.sin(bar);”这种方式,它会变得很烦人。

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