C++: повний посібник з перетворення рядка в число з подвійною точністю

pic

Перетворення рядків на числа з подвійною точністю — це фундаментальна операція в багатьох програмах на C++, починаючи з аналізу вводу користувача і закінчуючи обробкою файлів даних.

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

Давайте розглянемо різні методи перетворення рядків у числа з подвійною точністю на C++, їх переваги та недоліки, оптимальні сценарії використання.

Класичний підхід std::stod()

Функція std::stod() з C++11 — це основний метод перетворення рядків у числа з подвійною точністю для багатьох розробників. Вона є частиною заголовка ``, має чіткий і простий інтерфейс:

#include   
#include   

int main() {  
 std::string str = "3.14159";  
 double result = std::stod(str);  
 std::cout << "Converted value: " << result << std::endl;  
 return 0;  
}

За допомогою std::stod() обробляється пробіл на початку рядка, а синтаксичний аналіз зупиняється на першому непреобразовуваному символі. Якщо перетворення неможливе, ця функція викидає std::invalid_argument. Якщо ж перетворене значення виходить за межі діапазону типу double, викидається std::out_of_range.

Гнучкість stringstream

Для складніших сценаріїв синтаксичного аналізу в std::stringstream з заголовка `` пропонується гнучке рішення:

#include   
#include   

int main() {  
 std::string str = "3.14159 is pi";  
 std::stringstream ss(str);  
 double result;  
 if (ss >> result) {  
 std::cout << "Converted value: " << result << std::endl;  
 } else {  
 std::cout << "Conversion failed" << std::endl;  
 }  
 return 0;  
}

Цей метод корисний, коли потрібно парсити змішані типи даних або коли double є частиною більшого рядка.

Альтернатива в стилі C: atof()

Якщо ви працюєте зі застарілим кодом або віддаєте перевагу функціям в стилі C, скористайтесь atof() з заголовка ``:

#include   
#include   

int main() {  
 const char* str = "3.14159";  
 double result = atof(str);  
 std::cout << "Converted value: " << result << std::endl;  
 return 0;  
}

Незважаючи на простоту atof(), тут немає перевірки помилок, і на недопустимий ввід по-тихому повертається 0.0, тому atof() менш надійний, ніж альтернативи на C++.

Важлива точність… точність чисел з плаваючою точкою

При перетворенні рядків у числа з подвійною точністю можуть виникати проблеми з точністю:

Двійкові представлення чисел з плаваючою точкою можуть привести до результатів на кшталт 0.10000000000000001. Для точного десяткового представлення скористайтеся бібліотеками для десяткових значень або операціями з фіксованою точкою.

Перетворення залежно від локалі

У різних локалях застосовуються різні десяткові роздільники. Функція std::stod() відповідає глобальній локалі:

#include   
#include   
#include   

int main() {  
 std::locale::global(std::locale("de_DE.UTF-8"));  
 std::string str = "3,14159";  
 double result = std::stod(str);  
 std::cout << "Converted value: " << result << std::endl;  
 return 0;  
}

У цьому коді виконується правильний синтаксичний аналіз десяткового роздільника у німецькому стилі.

Обробка помилок: методи перетворення

Для надійного коду важлива коректна обробка помилок.
Ось приклад обробки виключень за допомогою std::stod():

#include   
#include   
#include   

double safe_string_to_double(const std::string& str) {  
 try {  
 size_t processed;  
 double result = std::stod(str, &processed);  
 if (processed != str.length()) {  
 throw std::invalid_argument("Вся строка не перетворена");  
 }  
 return result;  
 } catch (const std::invalid_argument& e) {  
 std::cerr << "Невірний аргумент: " << e.what() << std::endl;  
 throw;  
 } catch (const std::out_of_range& e) {  
 std::cerr << "Вихід за межі діапазону: " << e.what() << std::endl;  
 throw;  
 }  
}  

int main() {  
 try {  
 double result = safe_string_to_double("3.14159");  
 std::cout << "Перетворене значення: " << result << std::endl;  
 } catch (...) {  
 std::cout << "Перетворення не вдалося" << std::endl;  
 }  
 return 0;  
}

Ця функція не тільки перехоплює виключення, але й перетворює всю строку, що забезпечує високу достовірність результату.

Вибір методу за продуктивністю

Коли важлива продуктивність, важливе значення має вибір методу перетворення. Ось простий тест продуктивності std::stod(), std::stringstream і atof():

#include   
#include   
#include   
#include   
#include   
#include   

const int ITERATIONS = 1000000;  

double benchmark_stod(const std::vector& numbers) {  
 auto start = std::chrono::high_resolution_clock::now();  
 for (const auto& num : numbers) {  
 volatile double result = std::stod(num);  
 }  
 auto end = std::chrono::high_resolution_clock::now();  
 return std::chrono::duration(end - start).count();  
}  

double benchmark_stringstream(const std::vector& numbers) {  
 auto start = std::chrono::high_resolution_clock::now();  
 for (const auto& num : numbers) {  
 std::stringstream ss(num);  
 volatile double result;  
 ss >> result;  
 }  
 auto end = std::chrono::high_resolution_clock::now();  
 return std::chrono::duration(end - start).count();  
}  

double benchmark_atof(const std::vector& numbers) {  
 auto start = std::chrono::high_resolution_clock::now();  
 for (const auto& num : numbers) {  
 volatile double result = atof(num.c_str());  
 }  
 auto end = std::chrono::high_resolution_clock::now();  
 return std::chrono::duration(end - start).count();  
}  

int main() {  
 std::vector numbers(ITERATIONS, "3.14159");  

 std::cout << "Час для std::stod: " << benchmark_stod(numbers) << " секунд" << std::endl;  
 std::cout << "Час для stringstream: " << benchmark_stringstream(numbers) << " секунд" << std::endl;  
 std::cout << "Час для atof: " << benchmark_atof(numbers) << " секунд" << std::endl;  

 return 0;  
}

Результати варіюються залежно від конкретної системи та компілятора. Швидше за все працює atof(), за ним з невеликим відставанням слідує std::stod(), а найповільнішим зазвичай є std::stringstream. Не забувайте, що при виборі методу продуктивності в конкретному сценарії суперечать безпеці та коректності.

Реальні застосування

Перетворення рядка в число подвійної точності застосовується в багатьох реальних сценаріях:

  1. Фінансове ПЗ: синтаксичний аналіз грошових величин, що вводяться користувачем або з файлів даних.
  2. Наукові обчислення: обробка експериментальних даних або результатів моделювання.
  3. Аналіз даних: перетворення рядкових представлень вимірів у числові значення для статистичного аналізу.
  4. Парсинг конфігурацій: зчитування параметрів з плаваючою точкою з конфігураційних файлів.

У всіх випадках при виборі методу перетворення важливі конкретні вимоги до точності, обробки помилок і продуктивності.

Висновок

Перетворення рядків у числа з подвійною точністю на C++ — це звичайне завдання, і кожен підхід до нього має свої переваги і недоліки.
Оцініть нюанси std::stod(), std::stringstream і atof() і виберіть метод, що підходить під ваші конкретні вимоги. Реалізуючи в проектах на C++ перетворення рядків у числа з подвійною точністю, не забудьте врахувати налаштування локалі, обробку помилок і вплив на продуктивність.

Читайте також:

Читайте нас в Telegram, VK та Дзен

Переклад статті ryan: String to Double Conversion in C++: A Comprehensive Guide

Перекладено з: C++: полное руководство по преобразованию строки в число двойной точности

Leave a Reply

Your email address will not be published. Required fields are marked *