математические функции в потоке, возвращающие неправильные значения, с Android ndk

#android #c #math #android-ndk #math.h

#Android #c #математика #android-ndk #math.h

Вопрос:

У меня возникла проблема при попытке перенести код на Android NDK, который отлично работает на iOS. Код отображает 3D-модели и использует для этого OpenGL ES 1.1, поэтому он выполняет множество вычислений с использованием стандартной математической библиотеки c (включая math.h).

Вот фрагмент кода, используемого для проверки аномалии:

 double e;
e = sqrt(25);
assert(e == 5);
e = sqrt(16);
assert(e == 4);
e = sqrt(9);
assert(e == 3);
e = sqrt(4);
assert(e == 2);
e = sqrt(1);
assert(e == 1);
e = sqrt(0);
assert(e == 0);
  

Эти утверждения работают нормально везде, в то время как это:

 int vec[10] = {0, 1, 4, 9, 16, 25, 36, 49, 64, 81};
int res[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
double d;
for (int i=0; i<10; i  ) {
    d = sqrt(vec[i]);
    assert(d == res[i]);
}
  

сбой в любом потоке, отличном от основного. Например, он возвращает мне 0,37500000008731149 как sqrt(0), поэтому первое утверждение в цикле for завершается неудачей.

Похоже, что проблема заключается в вычислениях с использованием содержимого переменных, а не в вычислениях с жестко заданными значениями. Может ли это быть проблемой с отображением памяти в разных потоках? Я отладил его и напечатал значения vec[i] , и они верны.

Есть ли у математической библиотеки какие-либо проблемы при использовании в разных потоках? можете ли вы дать мне другое объяснение, почему это ведет себя так странно?

РЕДАКТИРОВАТЬ: я также видел такое же странное поведение с другими математическими функциями, такими как pow() и sin () . Кроме того, я также попробовал sqrtf и powf. Кажется, что они оба дают разные, но также неправильные значения, чем их двойные аналоги.

Комментарии:

1. вы синхронизируете свои потоки? Возможно ли, что другие потоки одновременно обращаются к вашему циклу for?

2. Просто для подтверждения: ваш код скомпилирован как C , а не C? И вы очень уверены, что вы включаете math.h ? Причина, по которой я спрашиваю, заключается в том, что C позволяет вам избегать вызова необъявленных функций, по умолчанию возвращая функцию, возвращающую an int . Поскольку эти функции, очевидно, не возвращаются int , вы получите неправильные результаты. Кроме того, вы передаете int аргументы функциям, ожидающим double аргументы. Будет автоматическое приведение, если включен соответствующий прототип функции. Но если по какой-то причине это не так, вы можете получить плохие результаты.

3. @DenisZaikin, я запускаю потоки с помощью java AsyncTasks с помощью метода doInBackground. Я не знаю, может ли это вызвать какие-либо проблемы с доступом к памяти, но значения верны непосредственно перед sqrt (например, vec[0] дает 0 в любом потоке в любой точке)

4. @RetoKoradi Я компилирую с использованием c ; код использует классы и методы C , а не C

Ответ №1:

 d == res[i]
  

Вот ваша проблема. Помимо того факта, что вы сравниваете double и int и понятия не имеете, какой из них будет приведен к типу другого, вы сравниваете числа с плавающей запятой с помощью оператора == .

Сравнение чисел с плавающей запятой и удвоений так не работает, потому что

 4.00000000000001 == 4.0
  

вернет false, а арифметика с плавающей запятой НЕ идеальна, и такие вещи, как sqrt(16), могут не обязательно возвращать ровно 4.

Вместо использования оператора == используйте функцию, подобную этой (я не проверял, компилируется ли это)

 bool AreEqual(double a, double b)
{
  return a < b   0.00001 amp;amp; a > b - 0.00001;
}
  

вместо этого.

Комментарии:

1. Плакат пишет, что sqrt(0) возвращает 0.37500000008731149. Это не похоже на проблему округления.

2. Ну, может быть, и нет, но поскольку то, что я сказал, по-прежнему верно, его код все равно не будет функционировать, даже если sqrt(0) вернул 0.

3. Спасибо @deek0146, но я протестировал этот же фрагмент в основном потоке на Android, и он работает хорошо. Также в iOS в любом потоке (поскольку я переношу приложение на Android с iOS). В любом случае, это не та функциональность, которую я имею в виду для приложения, вычисление sqrt (16) на самом деле действительно глупо. Я использовал это только для проверки, работает ли библиотека нормально.

Ответ №2:

Я, наконец, пришел к некоторому выводу о том, как «решить» проблему, хотя я немного растерялся, почему это происходило в первую очередь.

На самом деле я кодирую приложение, которое ссылается на несколько библиотек c , что является общим кодом для приложений iOS и Android. Когда математические вычисления выполняются внутри этих библиотек, math возвращает эти неправильные значения. Я считаю, что это проблема не с потоками, а с областями библиотек и связей. Проблема исчезла, когда я скомпилировал все библиотеки внутри приложения, поскольку это были другие источники CPP. Однако это не лучший способ, поскольку нарушает модульность библиотек. Мне придется это исправить, но это был бы совсем другой вопрос.