Я обнаружил, что одно из моих приложений, опубликованных на рынке, производит странные результаты на некоторых телефонах. При исследовании выясняется, что существует проблема с одной функцией, которая вычисляет расстояние между двумя GeoPoints - иногда она возвращает полностью неправильное значение. Эта проблема воспроизводится только на устройствах с MediaTek MT6589 SoC (aka MTK6589). И AFAIK у всех таких устройств установлен Android 4.2.
Обновление. Я также смог воспроизвести ошибку на планшете Lenovo S6000 с помощью MediaTek MT8125/8389 и на Fly IQ444 Quattro с MT6589 и с Android 4.1.
Я создал тестовый проект, который помогает воспроизвести ошибку. Он многократно выполняет вычисления для 1'000 или 100'000 итераций. Чтобы исключить возможность проблем с потоками, вычисление выполняется в потоке пользовательского интерфейса (с небольшими паузами для ответа на пользовательский интерфейс). В тестовом проекте я использовал только часть исходной формулы расстояния:
private double calcX() {
double t = 1.0;
double X = 0.5 + t / 16384;
return X;
}
Как вы можете проверить самостоятельно на 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;
}
Чтобы исправить проблему достаточно, чтобы просто изменить все double
на float
в calcX()
.
Дальнейшие исследования
Отключение JIT (добавление android:vmSafeMode="true"
в манифест приложения) также исправляет ошибку.
Кто-нибудь видел эту ошибку раньше? Может быть, это известная проблема?
p.s.: Если бы кто-нибудь смог воспроизвести эту ошибку на устройстве с другим чипом или смог протестировать его с помощью любого чипа MediaTek и Android >= 4.3, я буду очень признателен.