Небольшая числовая ошибка при расчете среднего веса
Вот часть физического движка.
Упрощенная функция centerOfMass
вычисляет 1D-центр масс двух твердых тел ( демо):-
#include <iostream>
#include <iomanip>
float centerOfMass(float pos1,float m1, float pos2,float m2){
return (pos1*m1+pos2*m2)/(m1+m2);
}
int main(){
float a=5.55709743f;
float b= centerOfMass(a,50,0,0);
std::cout << std::setprecision(9) << a << '\n'; //5.55709743
std::cout << std::setprecision(9) << b << '\n'; //5.55709696
}
я нуждаюсь b
быть точно = 5.55709743.
Крошечная разница, иногда (мой реальный случай = 5%), вносит неприятное расхождение в физике.
Есть несколько способов ее решить, например, выполнить некоторую условную проверку.
Тем не менее, это очень подвержено ошибкам для меня.
Вопрос: Как устранить ошибку в вычислениях, сохранив код чистым, быстрым и легким в обслуживании?
Между прочим, если это не может быть сделано элегантно, мне, вероятно, нужно улучшить вызывающую функцию, чтобы быть более устойчивой к такой числовой ошибке.
редактировать
(уточнить дублирующий вопрос)
Да, причина кроется в ошибке точности в формате хранения / вычисления (упоминается в разделе " Не работает ли математика с плавающей запятой?").
Однако этот вопрос задает вопрос о том, как нейтрализовать его симптом в очень конкретном случае.
2 ответа
Вы пытаетесь получить 9 десятичных знаков точности, но тип данных float
имеет точность около 7 десятичных цифр.
использование double
вместо. ( демо)
Используйте двойной, а не плавать. IEEE 754 double имеет около 16 знаков после запятой точности.
#include <iostream>
#include <iomanip>
double centerOfMass(double pos1, double m1, double pos2, double m2) {
return (pos1*m1 + pos2 * m2) / (m1 + m2);
}
int main() {
double a = 5.55709743;
double b = centerOfMass(a, 50, 0, 0);
std::cout << std::setprecision(16) << a << '\n'; //5.55709743
std::cout << std::setprecision(16) << b << '\n'; //5.55709743
std::cout << std::setprecision(16) << (b - a) << '\n'; // 0
}
Для приведенного примера centerOfMass(a, 50, 0, 0) следующее даст точные результаты для всех значений a, но, конечно, пример не выглядит реалистичным.
double centerOfMass(double pos1, double m1, double pos2, double m2) {
double divisor = m1 + m2;
return pos1*(m1/divisor) + pos2*(m2/ divisor);
}