Двойная> строка> двойное преобразование
Для передачи данных по сети я конвертирую значение double в строку, отправляю его и на стороне получателя преобразую обратно в значение double. Все идет нормально. Но я наткнулся на какое-то странное поведение, которое я не могу объяснить
Весь пример кода можно найти здесь. что я делаю: написать двойную строку через ostringstream
, затем прочитайте это с istringstream
значение меняется, но если я использую функцию "strtod(...) ", это работает. (с тем же outstring
)
Пример (весь код можно найти здесь):
double d0 = 0.0070000000000000001;
out << d0;
std::istringstream in (out.str());
in.precision(Prec);
double d0X_ = strtod(test1.c_str(),NULL);
in >> d0_;
assert(d0 == d0X_); // this is ok
assert(d0 == d0_); //this fails
Интересно, почему это происходит?
Вопрос: "Почему istream >> приводит к другому результату как strtod?" Пожалуйста, не отвечайте на вопрос, почему IEEE 754 не является точным.
1 ответ
Почему они могут отличаться
http://www.parashift.com/c++-faq-lite/newbie.html
Плавающая точка является приближением...
http://www.parashift.com/c++-faq-lite/newbie.html
Причина того, что число с плавающей точкой удивит вас, заключается в том, что значения типа float и double обычно представлены в двоичном формате с конечной точностью. Другими словами, числа с плавающей точкой не являются действительными числами. Например, в формате с плавающей запятой на вашем компьютере может быть невозможно точно представить число 0,1. По аналогии, невозможно точно представить число одна треть в десятичном формате (если вы не используете бесконечное число цифр).... Сообщение состоит в том, что некоторые числа с плавающей запятой не всегда могут быть представлены точно, поэтому сравнения не всегда делай то, что ты от них хочешь. Другими словами, если компьютер действительно умножит 10,0 на 1,0 / 10,0, он может не получить обратно 1,0.
Как сравнить с плавающей запятой:
http://c-faq.com/fp/strangefp.html
... некоторые машины имеют большую точность в регистрах вычислений с плавающей точкой, чем в двойных значениях, хранящихся в памяти, что может привести к неравенствам с плавающей точкой, когда может показаться, что два значения просто должны быть равны.
http://www.parashift.com/c++-faq-lite/newbie.html
Вот неправильный способ сделать это:
void dubious(double x, double y)
{
...
if (x == y) // Dubious!
foo();
...
}
Если вы действительно хотите убедиться, что они "очень близки" друг к другу (например, если переменная a содержит значение 1.0/10.0 и вы хотите увидеть, если (10*a == 1)), вы будете наверно захочется сделать что-то более причудливое, чем указано выше:
void smarter(double x, double y)
{
...
if (isEqual(x, y)) // Smarter!
foo();
...
}
Есть много способов определить функцию isEqual(), включая:
#include <cmath> /* for std::abs(double) */
inline bool isEqual(double x, double y)
{
const double epsilon = /* some small number such as 1e-5 */;
return std::abs(x - y) <= epsilon * std::abs(x);
// see Knuth section 4.2.2 pages 217-218
}
Примечание: приведенное выше решение не является полностью симметричным, то есть возможно для isEqual(x,y)!= IsEqual(y,x). С практической точки зрения, это обычно не происходит, когда величины x и y значительно больше, чем эпсилон, но ваш пробег может отличаться.