1

To transport data over the network I convert a double to string , send it and on the receiver side convert it back to double. so far so good. But I stumbled over some weird behaviour which I'm not able to explain

The whole example code can be found here. what i do: Write a double to string via ostringstream, afterwards read it in with istringstream the value changes But if i use the function "strtod(...) " it works. (with the same outstring)

Example (the whole code can be found here):

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 

I wonder why this happens.

The question is: "Why is 'istream >>' leading to another resulst as 'strtod'" Please don't answer the question why IEEE 754 is no exact.

nobs
  • 708
  • 6
  • 25
  • 2
    You don't show where you get "test1" from. – Puppy Oct 12 '11 at 15:58
  • 1
    Nor do you show what `Prec` is set to. – MSN Oct 12 '11 at 15:59
  • 1
    Also, it's a bit late to set Prec when reading in but not when writing out. Also, -1 for terrible too many question marks, and caps. – Puppy Oct 12 '11 at 16:02
  • 1
    `0.0070000000000000001` is NOT representable as a fraction in the form of x/pow(2,y), and therefore is a non-terminating number when represented in binary format. That means it cannot be represented in a finite number of binary digits. – DavidO Oct 12 '11 at 16:09
  • 1
    I ran this code using VS2010 and I get `d0_=0.0070000000000000010`, `d0x_=0.0070000000000000001` where `test1="0.0070000000000000001"` and `in="0.0070000000000000001 0.007000..."`. So, maybe there's a bug in `operator>>` – Skizz Oct 12 '11 at 16:25
  • 1
    The code in the pastebin works for me on Fedora 11 with GCC 4.4.1 (also ICC 12.1, and Clang from SVN though I expect those use the same libs.) – Boojum Oct 12 '11 at 16:28
  • Moreover, the literal in the first line may already exceed the precision of a IEEE 64-bit float (I'm not entirely sure, it's borderline), so you might already be feeding the problem a different value than you think you do. – Kerrek SB Oct 12 '11 at 16:56
  • the values I receive `d0 =0.0070000000000000001` `d0X_=0.0070000000000000001` `d0_ =0.0070000000000000010` @Skizz thanks for testing it @Boojum thanks for testing it on GCC I think I go with this problem to the MSDN forums it lookls like a MS problem – nobs Oct 13 '11 at 17:34

1 Answers1

2

Why are they might be different:
http://www.parashift.com/c++-faq-lite/newbie.html#faq-29.16

Floating point is an approximation...

http://www.parashift.com/c++-faq-lite/newbie.html#faq-29.17

The reason floating point will surprise you is that float and double values are normally represented using a finite precision binary format. In other words, floating point numbers are not real numbers. For example, in your machine's floating point format it might be impossible to exactly represent the number 0.1. By way of analogy, it's impossible to exactly represent the number one third in decimal format (unless you use an infinite number of digits).... The message is that some floating point numbers cannot always be represented exactly, so comparisons don't always do what you'd like them to do. In other words, if the computer actually multiplies 10.0 by 1.0/10.0, it might not exactly get 1.0 back.

How to compare floating point:
http://c-faq.com/fp/strangefp.html

...some machines have more precision available in floating-point computation registers than in double values stored in memory, which can lead to floating-point inequalities when it would seem that two values just have to be equal.

http://www.parashift.com/c++-faq-lite/newbie.html#faq-29.17
Here's the wrong way to do it:

 void dubious(double x, double y)
 {
   ...
   if (x == y)  // Dubious!
     foo();
   ...
 }

If what you really want is to make sure they're "very close" to each other (e.g., if variable a contains the value 1.0 / 10.0 and you want to see if (10*a == 1)), you'll probably want to do something fancier than the above:

 void smarter(double x, double y)
 {
   ...
   if (isEqual(x, y))  // Smarter!
     foo();
   ...
 }

There are many ways to define the isEqual() function, including:

 #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
 }

Note: the above solution is not completely symmetric, meaning it is possible for isEqual(x,y) != isEqual(y,x). From a practical standpoint, does not usually occur when the magnitudes of x and y are significantly larger than epsilon, but your mileage may vary.

Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
  • 2
    This doesn't answer the question. The question was asking why `istringstream::operator>>(double&)` generated a different value to `strtod` – Skizz Oct 12 '11 at 16:21
  • oh, I focused on the `//this fails` – Mooing Duck Oct 12 '11 at 16:24
  • This test assumes that you want to measure relative error. I think it really depends on what the values represent and how the value are computed. There is no general answer. – curiousguy Oct 14 '11 at 16:35
  • @curiousguy: That's exactly the reason that the isEqual function does not define `epsilon`. `epsilon` depends on where the numbers came from. – Mooing Duck Oct 14 '11 at 16:44