Evgenii Legotckoi
05 січня 2017 р. 19:21

Qt/C++ - Урок 057. Помилки виводу qDebug() для чисел з плаваючою комою

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

Подібний user case може виникнути у такій ситуації:

У вас є об'єкт QString, який містить якесь число "8564.26495574", яке ми переводимо в число з плаваючою точкою за допомогою методу toDouble(), і перевіряємо результат за допомогою висновку qDebug() , але незадача , Виведення виявляється помилковим.

QString str("8564.26495574");
qDebug() << str.toDouble();

// В выводе получаем -> 8564.26

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

Те саме можна спостерігати, якщо спробувати просто вивести значення числа double у qDebug().

double a = 8564.26495574;
qDebug() << a;

// В выводе получаем -> 8564.26

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

QString str("8564.26495574");
bool ok = false;
qDebug() << str.toDouble(&ok);
qDebug() << ok;

// В выводе получим следующее
// 8564.26
// true - то есть ошибок не было

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

QString str("8564.26495574");

double a = 8564.26495574;
double b = str.toDouble();

qDebug() << QString("%1").arg(a, 0, 'f', 5);
qDebug() << QString("%1").arg(a, 0, 'g', 5);
qDebug() << QString("%1").arg(a, 0, 'e', 10);
qDebug() << QString("%1").arg(a, 0, 'g', 30);

qDebug() << QString("%1").arg(b, 0, 'f', 5);
qDebug() << QString("%1").arg(b, 0, 'g', 5);
qDebug() << QString("%1").arg(b, 0, 'e', 10);
qDebug() << QString("%1").arg(b, 0, 'g', 30);

В результаті отримаємо наступний висновок qDebug():

"8564.26496"
"8564.3"
"8.5642649557e+3"
"8564.26495574000000488013029099"

"8564.26496"
"8564.3"
"8.5642649557e+3"
"8564.26495574000000488013029099"

В обох випадках отримуємо ідентичний результат, оскільки і там і там число одне й те саме.

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

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

Як бачите у висновку є невелика похибка 8564.2649557400000488013029099. Це вже пов'язано з особливостями математичного апарату того комп'ютера, на якому виробляються розрахунки. Місткість розрядності чисел в будь-якому комп'ютері обмежена, тому в останньому розряді після коми відбувається округлення, яке викликає появу подібних варіантів числа. Це необхідно враховувати за точних розрахунків.

Рекомендовані статті на цю тему

По статті запитували0питання

1

Вам це подобається? Поділіться в соціальних мережах!

Alex
  • 06 лютого 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
  • 06 лютого 2018 р. 15:30

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

Коментарі

Only authorized users can post comments.
Please, Log in or Sign up