Двойная> строка> двойное преобразование

Для передачи данных по сети я конвертирую значение 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 значительно больше, чем эпсилон, но ваш пробег может отличаться.

Другие вопросы по тегам