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 сильно отличается от другого. Эта ошибка повторяется всегда, когда я запускаю программу, некоторое время разница знак тоже!

В чем проблема?

Я пробовал все пути, чтобы решить эту проблему! (некоторые из них размещены ниже):

Вы можете найти полный исходный код здесь.

  1. сохранить Rs и значения ускорения в массиве и выполнить расчет вне листнера. Безрезультатно.
  2. Преобразовать float в double, безрезультатно.
  3. Многие другие способы

P.S. Эта проблема возникает только для resultVec[0] и resultVec[1], вместо этого resultVec[2] хорошо вычисляется.


person Lork    schedule 07.11.2011    source источник
comment
Как много вы понимаете в двоичной плавающей запятой? В частности, если вы использовали float, знаете ли вы, что точность составляет всего 7 значащих цифр?   -  person Jon Skeet    schedule 07.11.2011
comment
Как вы преобразовали float в double?   -  person Max    schedule 07.11.2011
comment
Подскажите: почему ошибка возникает только для resultVec[0] и resultVec[1], а для resultVec[2] НИКОГДА?   -  person Lork    schedule 07.11.2011
comment
@Max Здесь нет преобразования, но в старых тестах я использовал (double) var для преобразования, не так ли? проверьте также мой предварительный комментарий   -  person Lork    schedule 07.11.2011
comment
@Lork: вы не можете просто посмотреть на два результата с плавающей запятой (например, один в вашем журнале и один из Google?) и решить, они неверны. Вот только результаты не с плавающей запятой сравниваются. Вам нужно сравнить их, используя эпсилон. Принимая во внимание распространение ошибки и используя правильный эпсилон, вы вполне можете обнаружить, что два результата на самом деле идентичны. (Я не говорю, что в данном случае это так, но ваша методология точно неверна)   -  person TacticalCoder    schedule 07.11.2011
comment
Я почти уверен, что истинный результат 8,676786 80 E-9. Но такая маленькая ошибка, вероятно, не будет иметь значения.   -  person Ishtar    schedule 07.11.2011


Ответы (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.

person Mister Smith    schedule 07.11.2011

вы умножаете числа с плавающей запятой, накапливая ошибки округления до конца. Использование двойной точности не решит основной проблемы, заключающейся в том, что двоичные компьютеры не могут точно представлять десятичные числа с плавающей запятой.

Прочитайте это: http://download.oracle.com/docs/cd/E19957-01/806-3568/ncg_goldberg.html для обзора проблемы.

Вы, вероятно, обнаружите, что вам нужно выполнять вычисления, используя класс BigDecimal.

person mcfinnigan    schedule 07.11.2011
comment
Отличная ссылка: статья Голдберга - одна из моих любимых; ) Но я не согласен с тем, что ему, вероятно, следует выполнять вычисления с использованием класса BigDecimal. Люди писали (и продолжают писать) игры (как на старых машинах, так и на мобильных устройствах), либо используя вычисления с плавающей точкой/двойным числом и правильно отслеживая распространение ошибок, либо используя целочисленную математику. Создание большого количества объектов BigDecimal (до 60 раз в секунду, до сотен маленьких объектов на экране, затрачиваемых на все необходимые вычисления) — верный способ убить производительность приложения. - person TacticalCoder; 07.11.2011
comment
Согласен, BigDecimal использует молоток, чтобы раздавить улитку, но без какого-либо дополнительного контекста того, что делает OP, и его очевидного незнания FP, это может быть лучшим вариантом. С другой стороны, я думаю, что всех новых программистов следует схватить за шкирку и заставить прочитать вышеуказанную статью :-) - person mcfinnigan; 07.11.2011

Я думаю, что вы должны использовать тип double вместо float

person Max    schedule 07.11.2011

Возможно, вы столкнулись с ограниченной точностью значений с плавающей запятой. Чтобы подтвердить это, вы можете изменить float на double или использовать BigDecimal.

person Thirler    schedule 07.11.2011