【发布时间】:2014-01-01 15:22:38
【问题描述】:
我遇到了一个非常奇怪的错误。下面的一小段代码使用了一个相当简单的数学运算。
protected double C_n_k(int n, int k)
{
if(k<0 || k>n)
return 0;
double s=1;
for(int i=1;i<=k;i++)
s=s*(n+1-i)/i;
return s;
}
编辑 在某些设备上使用 ProGuard 可能会出错。我已经在 HTC One S Android 4.1.1 build 3.16.401.8 上确认了这一点,但从我收到的电子邮件来看,很多 Android 4+ 的手机都受到了影响。对于其中一些(Galaxy S3),美国运营商品牌手机受到影响,而国际版本则不受影响。许多手机不受影响。
以下是计算 1
我有 3 个问题:
怎么可能?即使 ProGuard 出错了,设备和会话之间的计算也应该是一致的。
我们如何避免它?我知道在这种情况下用
long替换double很好,但这不是一种通用方法。放弃使用double或发布未混淆的版本是毫无疑问的。哪些 Android 版本会受到影响?我在游戏中修复它的速度非常快,所以我只知道很多玩家都看过它,至少大多数玩家使用的是 Android 4.0
溢出是毫无疑问的,因为有时我在计算C(3,3)=3/1*2/2*1/3 时会看到错误。通常不正确的数字从 C(10,...) 的某个地方开始,看起来手机已经“忘记”了一些划分。
我的 SDK 工具是 22.3(最新),我在 Eclipse 和 IntelliJ IDEA 创建的构建中看到了它。
活动代码:
package com.karmangames.mathtest;
import android.app.Activity;
import android.os.Bundle;
import android.text.method.ScrollingMovementMethod;
import android.widget.TextView;
public class MathTestActivity extends Activity
{
/**
* Called when the activity is first created.
*/
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
String s="";
for(int n=0;n<=25;n++)
for(int k=0;k<=n;k++)
{
double v=C_n_k_double(n,k);
s+="C("+n+","+k+")="+v+(v==C_n_k_long(n,k) ? "" : " Correct is "+C_n_k_long(n,k))+"\n";
if(k==n)
s+="\n";
}
System.out.println(s);
((TextView)findViewById(R.id.text)).setText(s);
((TextView)findViewById(R.id.text)).setMovementMethod(new ScrollingMovementMethod());
}
protected double C_n_k_double(int n, int k)
{
if(k<0 || k>n)
return 0;
//C_n^k
double s=1;
for(int i=1;i<=k;i++)
s=s*(n+1-i)/i;
return s;
}
protected double C_n_k_long(int n, int k)
{
if(k<0 || k>n)
return 0;
//C_n^k
long s=1;
for(int i=1;i<=k;i++)
s=s*(n+1-i)/i;
return (double)s;
}
}
main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/text"
android:text="Hello World!"
/>
</LinearLayout>
错误计算结果示例(记住,每次尝试都不一样)
C(0,0)=1.0
C(1,0)=1.0
C(1,1)=1.0
C(2,0)=1.0
C(2,1)=2.0
C(2,2)=1.0
C(3,0)=1.0
C(3,1)=3.0
C(3,2)=3.0
C(3,3)=1.0
C(4,0)=1.0
C(4,1)=4.0
C(4,2)=6.0
C(4,3)=4.0
C(4,4)=1.0
C(5,0)=1.0
C(5,1)=5.0
C(5,2)=10.0
C(5,3)=10.0
C(5,4)=30.0 Correct is 5.0
C(5,5)=1.0
C(6,0)=1.0
C(6,1)=6.0
C(6,2)=15.0
C(6,3)=40.0 Correct is 20.0
C(6,4)=90.0 Correct is 15.0
C(6,5)=144.0 Correct is 6.0
C(6,6)=120.0 Correct is 1.0
C(7,0)=1.0
C(7,1)=7.0
C(7,2)=21.0
C(7,3)=35.0
C(7,4)=105.0 Correct is 35.0
C(7,5)=504.0 Correct is 21.0
C(7,6)=840.0 Correct is 7.0
C(7,7)=720.0 Correct is 1.0
C(8,0)=1.0
C(8,1)=8.0
C(8,2)=28.0
C(8,3)=112.0 Correct is 56.0
C(8,4)=70.0
C(8,5)=1344.0 Correct is 56.0
C(8,6)=3360.0 Correct is 28.0
C(8,7)=5760.0 Correct is 8.0
C(8,8)=5040.0 Correct is 1.0
C(9,0)=1.0
C(9,1)=9.0
C(9,2)=36.0
C(9,3)=168.0 Correct is 84.0
C(9,4)=756.0 Correct is 126.0
C(9,5)=3024.0 Correct is 126.0
C(9,6)=10080.0 Correct is 84.0
C(9,7)=25920.0 Correct is 36.0
C(9,8)=45360.0 Correct is 9.0
C(9,9)=40320.0 Correct is 1.0
C(10,0)=1.0
C(10,1)=10.0
C(10,2)=45.0
C(10,3)=120.0
C(10,4)=210.0
C(10,5)=252.0
C(10,6)=25200.0 Correct is 210.0
C(10,7)=120.0
C(10,8)=315.0 Correct is 45.0
C(10,9)=16800.0 Correct is 10.0
C(10,10)=1.0
【问题讨论】:
-
听起来不太可能。您是否尝试过在该循环中记录
s的每个值以了解计算的不同之处? -
哪个是“正确的?”如果有的话?
-
FWIW,在数学中,C(12, 4) = 495。
-
问题肯定出在您的代码上。一个字——调试器!
-
由于
long在double不适用的地方工作,并且您声称没有溢出,我建议尝试使用strictfp关键字if 差异小得多。有这么大的差异,(并且在这个例子中没有丢失精度的事实)我非常怀疑这是一个舍入错误,但是。
标签: java android proguard android-sdk-tools