Android (большой) терпит неудачу на продукте с плавающей запятой
Ситуация драматическая... Мне нужно 5 дней, чтобы решить эту проблему, и я не могу выбраться.
Проблема: простая операция, как продукт, всегда дает неправильный результат. Зачем?
Извлечение кода:
//all vars are float
// resultVec is a vector [3]
// Rs is a vector [3]
// accelerationvalues is a vector [3]
resultVec[0]=Rs[0]*accelerationvalues[0]+Rs[1]*accelerationvalues[1]+Rs[2]*accelerationvalues[2];
//debug to print result
Log.d("e", "("+Rs[0]+")*("+accelerationvalues[0]+")+("+Rs[1]+")*("+accelerationvalues[1]+")+("+Rs[2]+")*("+accelerationvalues[2]+")="+(resultVec[0]));
И это результат Log Cat:Но вы можете просто попробовать, что это не так: поиск в Google
(0.040147018)*(-0.9942854)+(0.9984244)*(-0.32688835)+(0.039202508)*(9.343558)
И вы обнаружите, что истинный результат - 8,67678679 × 10-9, который очень отличается от других. Эта ошибка повторяется всегда, когда я выполняю программу, иногда также и в знаке!
В чем проблема?
Я пробовал весь путь, чтобы решить это! (некоторые опубликованы ниже):
Вы можете найти полный источник здесь.
- сохранить Rs и значения ускорения в массиве и выполнить расчет вне списка. Безрезультатно.
- Конвертировать float в double, безрезультатно.
- Много других способов
PS Эта проблема происходит только для resultVec[0]
а также resultVec[1]
вместо resultVec[2]
хорошо рассчитан.
4 ответа
Это не вина андроида, это то, как вы разработали приложение.
Выполните это в простом приложении Java:
public class ArithmTest {
public static void main(String[] args) {
double d1 = (0.040147018)*(-0.9942854)+(0.9984244)*(-0.32688835)+(0.039202508)*(9.343558);
System.out.println(d1);
float f1 = 0.040147018f;
float f2 = -0.9942854f;
float f3 = 0.9984244f;
float f4 = -0.32688835f;
float f5 = 0.039202508f;
float f6 = 9.343558f;
System.out.println(f1*f2 + f3*f4 + f5*f6);
}
}
Как видите, первая такая же, как у Google, а вторая распечатка - ценность вашего приложения. Чтобы решить эту проблему, я думаю, что вы должны использовать double вместо float в каждой объявленной вами переменной, например: accelerationvalues
а также resultVec
,
Вы умножаете числа с плавающей запятой, накапливая ошибки округления до конца. Использование двойной точности не решит основную проблему: двоичные компьютеры не могут точно представлять десятичные числа с плавающей запятой.
Прочитайте это: http://download.oracle.com/docs/cd/E19957-01/806-3568/ncg_goldberg.html для обзора проблемы.
Скорее всего, вы обнаружите, что вам нужно выполнить вычисления с использованием класса BigDecimal.
Вы можете столкнуться с ограниченной точностью значений с плавающей запятой. Чтобы подтвердить это, вы можете изменить float
в double
или использовать BigDecimal
,