MediaTek处理器上的双精度值计算错误

Ido*_*lon 19 floating-point double android jit

我发现我在市场上发布的一个应用程序会在某些手机上产生奇怪的结果.经过调查发现,一个函数存在一个问题,即计算两个GeoPoints之间的距离 - 有时它会返回完全错误的值.此问题仅在使用MediaTek MT6589 SoC(又名MTK6589)的设备上重现.而AFAIK所有此类设备都安装了Android 4.2.

更新我还能够使用联发科MT8125/8389芯片和安装了MT6589并安装了Android 4.1的Fly IQ444 Quattro重现联想S6000平板电脑上的错误.

我创建了一个测试项目,有助于重现错误.它重复运行计算1000次或100次迭代.为了排除线程问题的可能性,在UI线程上执行计算(具有小的暂停以保持UI响应).在测试项目中,我只使用了原始距离公式的一部分:

private double calcX() {
    double t = 1.0;
    double X = 0.5 + t / 16384;
    return X;
}
Run Code Online (Sandbox Code Playgroud)

您可以在web2.0calc.com自行查看,其值X应约为:0.50006103515625.
然而,在具有MT6589芯片的设备上,通常会计算出错误的值:2.0.

项目可在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;

}
Run Code Online (Sandbox Code Playgroud)

要解决这个问题,只需将all更改doublefloatin calcX()方法即可.

进一步调查
关闭JIT(通过添加android:vmSafeMode="true"到应用程序清单)也可以修复bug.

以前有人见过这个bug吗?也许这是一个已知的问题?

ps:如果有人能够使用其他芯片在设备上重现此错误,或者可以使用任何联发科芯片和Android> = 4.3进行测试,我将非常感激.

小智 3

这是一个从 2012 年底到 2013 年初在 JellyBean 源代码中活跃的 JIT 错误。简而言之,如果在高 32 位中不同但在低 32 位中相同的两个或多个双精度常量被用于相同的基本块 JIT 会认为它们是相同的,并且不恰当地优化其中一个。

我在以下位置介绍了该缺陷:https ://android-review.googlesource.com/#/c/47280/

并将其修复在: https: //android-review.googlesource.com/#/c/57602/

该缺陷不应出现在任何最新的 Android 版本中。