[lexical_cast] float-to-string rounding error (not found with cout)
I'm seeing unexpected behavior when using lexical_cast<string>. This code: const float f = 00123.12300; cout << f << endl; cout << boost::lexical_caststd::string(f) << endl; gives the result: 123.123 123.123001 Two questions: (1) Is there a way to eliminate this trailing '1' with lexical_cast (presumably some wierd rounding error)? (2) When casting float-to-string, given this behavior, why should I use lexical_cast instead of, say, stringstream? Thanks -Grant
Grant Birchmeier:
I'm seeing unexpected behavior when using lexical_cast<string>.
This code: const float f = 00123.12300; cout << f << endl; cout << boost::lexical_caststd::string(f) << endl; gives the result: 123.123 123.123001
Two questions:
(1) Is there a way to eliminate this trailing '1' with lexical_cast (presumably some wierd rounding error)?
Try this code: float f = 00123.12300; std::cout << f << std::endl; std::cout << boost::lexical_caststd::string(f) << std::endl; float f2 = 123.123001; std::cout << f2 << std::endl; std::cout << boost::lexical_caststd::string(f2) << std::endl; std::cout << f - f2 << std::endl; which will hopefully answer your question.
On Thu, Apr 30, 2009 at 12:20 PM, Peter Dimov
(1) Is there a way to eliminate this trailing '1' with lexical_cast (presumably some wierd rounding error)?
Try this code:
std::cout << f2 << std::endl; std::cout << boost::lexical_caststd::string(f2) << std::endl;
std::cout << f - f2 << std::endl;
which will hopefully answer your question.
The resulting output from your code, Peter, is: 123.123 123.123001 123.123 123.123001 0 That's just great. So internally (on my system, anyway) the two floats are equal, and the two methods of converting it to string just round it differently. Curses. Thanks for the help -Grant
On Thu, Apr 30, 2009 at 11:49 AM, Grant Birchmeier
On Thu, Apr 30, 2009 at 12:20 PM, Peter Dimov
wrote: (1) Is there a way to eliminate this trailing '1' with lexical_cast (presumably some wierd rounding error)?
Try this code:
std::cout << f2 << std::endl; std::cout << boost::lexical_caststd::string(f2) << std::endl;
std::cout << f - f2 << std::endl;
which will hopefully answer your question.
The resulting output from your code, Peter, is: 123.123 123.123001 123.123 123.123001 0
That's just great. So internally (on my system, anyway) the two floats are equal, and the two methods of converting it to string just round it differently. Curses.
It might help to read up on the IEEE floating point standards. Basically 'most' floating point numbers you will give a computer are incapable of being representing accurately by the CPU in just 32-bits (there are a couple bignum floating point libraries that retain accuracy quite well, obviously much slower). std::cout was just truncating the printed value (which you can control by passing an attribute to the stream to, say, not do any rounding/truncation). Boost.Lexical_cast uses StreamStream internally, and rounding seems to be off, which is good because then it represents the internal number accurately when printed. This is also why you *NEVER* (and this is important enough to say again, ***NEVER***) directly compare floats for equality, there can be arbitrary accuracy in the number. Example: bool FloatsEqual(float f1, float f2) { return f1=f2; } A more proper implementation would be: bool FloatsEqual(float f1, float f2, float eps=0.00001) { return eps>(f1-f2); } The second implementation does a subtraction first to get them to as close to zero as possible if they are near equal. And if the floats had a lot of operations on them and their representations got off (try comparing ((1-0.33333)+0.66666)==1.0 without constant folding... it fails, the left ends up being 0.999998 or so), this will let you pass an 'accuracy' value, the epsilon (there is also an epsilon calculation function in the std namespace). So instead of if(f1==f2) {... You should do something like (with whatever accuracy you want): if(0.00001<(f1-f2)) {...
On Thu, Apr 30, 2009 at 2:33 PM, OvermindDL1
It might help to read up on the IEEE floating point standards.
[DD] Wikipedia has a decent overview.
This is also why you *NEVER* (and this is important enough to say again, ***NEVER***) directly compare floats for equality, there can be arbitrary accuracy in the number.
[DD] Rarely, not never ;-)
Example: bool FloatsEqual(float f1, float f2) { return f1=f2; }
A more proper implementation would be: bool FloatsEqual(float f1, float f2, float eps=0.00001) { return eps>(f1-f2); }
I'm no expert, but the above does not take into account the "magnitude" of the numbers. Comparing 0.000001 and 0.0000011 with 0.000001 and 999999.0 and 999999.1 with 0.00001 don't mean the same thing at all. By dividing with the sum (when non-null), e then represents the "significant digits" fuzzy compare. if( (x + y) != 0 ) { return tiny( (x - y) / (x + y), e); } else { // x == -y return tiny( x, e ); } inline bool tiny(float x, float e) { // and double overload return abs(x) <= e; // abs if our abs } But again, I'm no numerical expert. I'm sure someone will correct the above ;-) --DD
On Thu, Apr 30, 2009 at 1:57 PM, Dominique Devienne
[DD] Rarely, not never ;-) Never for people who do not know how it actually works internally :P
On Thu, Apr 30, 2009 at 1:57 PM, Dominique Devienne
A more proper implementation would be: bool FloatsEqual(float f1, float f2, float eps=0.00001) { return eps>(f1-f2); }
I'm no expert, but the above does not take into account the "magnitude" of the numbers. Comparing 0.000001 and 0.0000011 with 0.000001 and 999999.0 and 999999.1 with 0.00001 don't mean the same thing at all.
Which is why eps was a default value that can be overridden with whatever might be good for the time. And yea, forgot the absolute, been a long day. :)
participants (4)
-
Dominique Devienne
-
Grant Birchmeier
-
OvermindDL1
-
Peter Dimov