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

Хотя процесс этого преобразования кажется простым, его нюансы сложны даже для опытных разработчиков.

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

Классический подход std::stod()

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

#include <string>
#include <iostream>

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 из заголовка <sstream> предоставляет гибкое решение:

#include <sstream>
#include <iostream>

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() из заголовка <cstdlib>:

#include <cstdlib>
#include <iostream>

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 <string>
#include <iostream>
#include <locale>

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 <string>
#include <iostream>
#include <stdexcept>

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("Entire string not converted");
}
return result;
} catch (const std::invalid_argument& e) {
std::cerr << "Invalid argument: " << e.what() << std::endl;
throw;
} catch (const std::out_of_range& e) {
std::cerr << "Out of range: " << e.what() << std::endl;
throw;
}
}

int main() {
try {
double result = safe_string_to_double("3.14159");
std::cout << "Converted value: " << result << std::endl;
} catch (...) {
std::cout << "Conversion failed" << std::endl;
}
return 0;
}

Этой функцией не только перехватываются исключения, но и преобразовывается вся строка, чем обеспечивается высокая достоверность результата.

Выбор метода по производительности

Где важна производительность, существенное значение придается выбору метода преобразования. Вот простой тест производительности std::stod(), std::stringstream и atof():

#include <string>
#include <sstream>
#include <cstdlib>
#include <chrono>
#include <iostream>
#include <vector>

const int ITERATIONS = 1000000;

double benchmark_stod(const std::vector<std::string>& 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<double>(end - start).count();
}

double benchmark_stringstream(const std::vector<std::string>& 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<double>(end - start).count();
}

double benchmark_atof(const std::vector<std::string>& 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<double>(end - start).count();
}

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

std::cout << "std::stod time: " << benchmark_stod(numbers) << " seconds" << std::endl;
std::cout << "stringstream time: " << benchmark_stringstream(numbers) << " seconds" << std::endl;
std::cout << "atof time: " << benchmark_atof(numbers) << " seconds" << 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

Предыдущая статьяПодробно о технологии «Издатель-подписчик» Redis