Evgenii Legotckoi
5 января 2017 г. 19:21

Qt/C++ - Урок 057. Ошибки вывода qDebug() для чисел с плавающей точкой

При разработке программного обеспечения на Qt довелось столкнуться с одним нюансом при выводе чисел с плавающей запятой через qDebug(). Проблема заключается в том, что выводятся не все символы после запятой. Тем же самым грешит и вывод std::cout .

Подобный user case может возникнуть в следующей ситуации:

У вас имеется объект QString, который содержит некое число "8564.26495574", которое мы переводим в число с плавающей точкой с помощью метода toDouble(), и проверяем результат с помощью вывода qDebug() , но вот незадача, вывода оказывается ложным.

  1. QString str("8564.26495574");
  2. qDebug() << str.toDouble();
  3.  
  4. // В выводе получаем -> 8564.26

Хотя на самом деле мы получаем совершенно правильное число, то есть из строки было получено число 8564.26495574 , просто вывод qDebug() показывает округлённый результат.

Тоже самое можно наблюдать, если попытаться просто вывести значение числа double в qDebug().

  1. double a = 8564.26495574;
  2. qDebug() << a;
  3.  
  4. // В выводе получаем -> 8564.26

Убедиться в том, что никаких ошибок не происходит при конвертировании числа из QString в double можно, воспользовавшись переменной bool, указатель на которую передаётся в качестве аргумента в метод toDouble().

  1. QString str("8564.26495574");
  2. bool ok = false;
  3. qDebug() << str.toDouble(&ok);
  4. qDebug() << ok;
  5.  
  6. // В выводе получим следующее
  7. // 8564.26
  8. // true - то есть ошибок не было

Для того, чтобы сделать корректный вывод, необходимо воспользоваться методом QString::arg() с указанием типа форматирования и точности.

  1. QString str("8564.26495574");
  2.  
  3. double a = 8564.26495574;
  4. double b = str.toDouble();
  5.  
  6. qDebug() << QString("%1").arg(a, 0, 'f', 5);
  7. qDebug() << QString("%1").arg(a, 0, 'g', 5);
  8. qDebug() << QString("%1").arg(a, 0, 'e', 10);
  9. qDebug() << QString("%1").arg(a, 0, 'g', 30);
  10.  
  11. qDebug() << QString("%1").arg(b, 0, 'f', 5);
  12. qDebug() << QString("%1").arg(b, 0, 'g', 5);
  13. qDebug() << QString("%1").arg(b, 0, 'e', 10);
  14. qDebug() << QString("%1").arg(b, 0, 'g', 30);

В результате получим следующий вывод qDebug():

  1. "8564.26496"
  2. "8564.3"
  3. "8.5642649557e+3"
  4. "8564.26495574000000488013029099"
  5.  
  6. "8564.26496"
  7. "8564.3"
  8. "8.5642649557e+3"
  9. "8564.26495574000000488013029099"

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

Пару слов о типах форматирования:

  • 'g' - выводит указанное количество символов начиная с самого большого разряда в целой части числа;
  • 'f' - выводит указанное количество символов с точностью после запятой;
  • 'e' - выводит указанное количество символов с точностью после запятой и указанием числа скрытых разрядов.

Как видите в выводе есть небольшая погрешность 8564.26495574000000488013029099. Это уже связано с особенностями математического аппарата того компьютера, на котором производятся расчёты. Емкость разрядности чисел в любом компьютере ограничена, поэтому в самом последнем разряде после запятой происходит округление, которое вызывает появление подобных вариантов числа. Это необходимо учитывать при точных расчётах.

Рекомендуемые статьи по этой тематике

По статье задано0вопрос(ов)

1

Вам это нравится? Поделитесь в социальных сетях!

Alex
  • 6 февраля 2018 г. 15:19

Добрый день, подскажите пожалуйста. Столкнулся с проблемой, парсю JSON получаю цифровые значения типа string, пример (QJsonValue(string, "0.0000000001")), делаю преобразование типа в double.

auto strToDouble = [] (const QString str) { return QString(str).toDouble(); };

На выходе у меня получается такое значение 1Е-10, каким образом мне получить значение 0.0000000001 в переменной double. Делать дополнительное преобразование в QML считаю не нормальным.

Alex
  • 6 февраля 2018 г. 15:30

Решение было найдено. В QML использовал метод JavaScript toFixed() Method, массив data_1[0].toFixed(10), 10 количество мне нужных знаков после запятой.

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь