【发布时间】:2014-02-21 04:44:57
【问题描述】:
我发现我在市场上发布的一个应用程序在某些手机上产生了奇怪的结果。经调查发现,一个计算两个 GeoPoints 之间距离的函数存在问题 - 有时它返回完全错误的值。此问题仅在具有MediaTek MT6589 SoC(又名 MTK6589)的设备上重现。并且 AFAIK 所有此类设备都安装了 Android 4.2。
更新我还能够在带有MediaTek MT8125/8389 芯片的联想 S6000 平板电脑和带有 MT6589 并安装了 Android 4.1 的Fly IQ444 Quattro 上重现该错误。 p>
我创建了一个有助于重现错误的测试项目。它重复运行计算 1'000 或 100'000 次迭代。为了排除线程问题的可能性,计算是在 UI 线程上执行的(带有小暂停以保持 UI 响应)。在测试项目中,我只使用了原始距离公式的一部分:
private double calcX() {
double t = 1.0;
double X = 0.5 + t / 16384;
return X;
}
您可以自行检查web2.0calc.com,X 的值应约为:0.50006103515625。
但是在带有 MT6589 芯片的设备上,经常会计算出错误的值:2.0。
项目是available at Google Code(APK 也可用)。测试类的源码如下:
public class MtkTestActivity extends Activity {
static final double A = 0.5;
static final double B = 1;
static final double D = 16384;
static final double COMPUTED_CONST = A + B / D;
/*
* Main calculation where bug occurs
*/
public double calcX() {
double t = B;
double X = A + t / D;
return X;
}
class TestRunnable implements Runnable {
static final double EP = 0.00000000001;
static final double EXPECTED_LOW = COMPUTED_CONST - EP;
static final double EXPECTED_HIGH = COMPUTED_CONST + EP;
public void run() {
for (int i = 0; i < SMALL_ITERATION; i++) {
double A = calcX();
if (A < EXPECTED_LOW || A > EXPECTED_HIGH) {
mFailedInCycle = true;
mFails++;
mEdit.getText().append("FAILED on " + mIteration + " iteration with: " + A + '\n');
}
mIteration++;
}
if (mIteration % 5000 == 0) {
if (mFailedInCycle) {
mFailedInCycle = false;
} else {
mEdit.getText().append("passed " + mIteration + " iterations\n");
}
}
if (mIteration < mIterationsCount) {
mHandler.postDelayed(new TestRunnable(), DELAY);
} else {
mEdit.getText().append("\nFinished test with " + mFails + " fails");
}
}
}
public void onTestClick(View v) {
startTest(IT_10K);
}
public void onTestClick100(View v) {
startTest(IT_100K);
}
private void startTest(int iterationsCount) {
Editable text = mEdit.getText();
text.clear();
text.append("\nStarting " + iterationsCount + " iterations test...");
text.append("\n\nExpected result " + COMPUTED_CONST + "\n\n");
mIteration = 0;
mFails = 0;
mFailedInCycle = false;
mIterationsCount = iterationsCount;
mHandler.postDelayed(new TestRunnable(), 100);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler = new Handler(getMainLooper());
mEdit = (EditText) findViewById(R.id.edtText1);
}
private static final int IT_10K = 1000;
private static final int IT_100K = 100000;
private static final int SMALL_ITERATION = 50;
private static final int DELAY = 10;
private int mIteration;
private int mFails;
private boolean mFailedInCycle;
private Handler mHandler;
private int mIterationsCount;
private EditText mEdit;
}
要解决此问题,只需将 calcX() 方法中的所有 double 更改为 float 就足够了。
进一步调查
关闭 JIT(通过将 android:vmSafeMode="true" 添加到应用程序清单)也可以修复错误。
以前有人见过这个错误吗?也许这是一个已知问题?
p.s.:如果有人能够使用其他芯片在设备上重现此错误,或者可以使用任何 MediaTek 芯片和 Android >= 4.3 进行测试,我将不胜感激。
【问题讨论】:
-
code.google.com/p/android/issues/detail?id=63790 详细介绍了运行 Android 4.1.1 的 HTC One S 设备以及许多其他设备上的确切问题。在这个错误中,它最初看起来像是与 ProGuard 的一些不良交互,但进一步的分析已将其缩减为一些更小的 Dalvik 字节码集。根本原因尚未确定,唯一的解决方法是您发现关闭 JIT 的方法。
-
是的,它似乎与我发布的内容非常相似。 Google 代码的链接包含一个您可以尝试的测试 apk,here 是更详细的描述和与 ProGuard 的创建者的讨论。
-
我能够在装有 Android 4.2.1 的 MT6589 手机(Canvas HD)中重现此错误。
-
@ScottBarta 是的,它们看起来很相似,但是我无法使用 MT6589(使用 Dmitry 提供的 APK)在设备上重现 #63790。此外,这与 #63790 的不同之处在于它定期发生但并非一直发生。在过去的几天里,我对这个错误进行了更多的调查,我很快就会在我的回答中展示调查的结果。
-
抱歉,收到您的电子邮件时,我正在参加会议。我刚刚在受我影响的手机上尝试了您的 apk。我测试了 100K 迭代,每次或多次退出而没有退出,并且在大约 20 次测试后没有看到任何与“0 失败”不同的东西。所以,这些问题似乎有所不同。
标签: android floating-point double jit