Boost C++ Libraries: Ticket #9177: Improved serialization of floating point values https://svn.boost.org/trac10/ticket/9177 <p> Currently there are several weaknesses with floating point serialization: </p> <p> 1) There's no handling of <code>long double</code> or user-defined floating point types that have been declared primitives. 2) The current code for float and double may fail to print sufficient digits to round trip the value, when the value is less than 1, but not so small as to trigger an automatic switch to scientific format (which from memory occurs around 10<sup>-5). </sup></p> <p> The attached patch addresses both of these issues, and coincidentally simplifies the code as well. </p> en-us Boost C++ Libraries /htdocs/site/boost.png https://svn.boost.org/trac10/ticket/9177 Trac 1.4.3 John Maddock Sun, 29 Sep 2013 17:08:31 GMT attachment set https://svn.boost.org/trac10/ticket/9177 https://svn.boost.org/trac10/ticket/9177 <ul> <li><strong>attachment</strong> → <span class="trac-field-new">basic_text_oprimitive.hpp.patch</span> </li> </ul> Ticket Robert Ramey Sat, 26 Oct 2013 22:12:21 GMT status changed; resolution set https://svn.boost.org/trac10/ticket/9177#comment:1 https://svn.boost.org/trac10/ticket/9177#comment:1 <ul> <li><strong>status</strong> <span class="trac-field-old">new</span> → <span class="trac-field-new">closed</span> </li> <li><strong>resolution</strong> → <span class="trac-field-new">fixed</span> </li> </ul> <p> I've incorporated this patch into the code. A couple of observations though. </p> <p> a) user types such as a multi-precision float would need to implement the &lt;&lt; operator or trigger a compile time error. </p> <p> b) the serialization_trait for these types must be marked "primitive" in order to get to the correct code. </p> <p> c) and of course these special types need to corresponding members defined in numeric_limits. </p> <p> Failure to get the above right - will like create a compile error which is pretty confusing. Oh well. </p> <p> One thing I asked for some time ago was for the is_float to be defined in terms of numeric_limits - more or less as you have done here. At least that would keep things in one place. Actually it's not clear what the designers of numeric limits had in mind by not explicitly specifying is_float. </p> <p> Question: what does this mean for someone who synthesizes a floating point decimal? What will this code do? Also what about a fixed point decimal. I'm generally concerned about undefined behavior and unintended consequences when we are "too clever" as I'm thinking we might be here. </p> <p> Anyway, I gave credit to you in the comments for the fix. </p> <p> Robert Ramey </p> <p> I remember some special types - like int64 in microsoft failed to support stream &lt;&lt; and &gt;&gt; operators. I don't know if that's still true. </p> Ticket anonymous Sun, 27 Oct 2013 08:45:08 GMT <link>https://svn.boost.org/trac10/ticket/9177#comment:2 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/9177#comment:2</guid> <description> <p> For decimal floating point types it *may* stream out 2 digits too many - we should really be using C++11 max_digits10 and not digits10+2 - actually that could be fixed by checking for BOOST_NO_CXX11_NUMERIC_LIMITS and using max_digits10 when not defined and digits10+2 when it is. </p> <p> For fixed point decimal the floating point code will <strong>not</strong> be triggered: these don't have a floating point (max_exponent is zero), and are generally regarded as "exact" in the same sense that integers are exact albeit truncating. </p> <p> You're right about special types often not having std lib support - though typically that's a transitional thing when they're first introduced? </p> </description> <category>Ticket</category> </item> <item> <dc:creator>anonymous</dc:creator> <pubDate>Mon, 28 Oct 2013 01:11:05 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/9177#comment:3 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/9177#comment:3</guid> <description> <p> Hmmm - what about floating point decimal - this has been proposed as a library </p> </description> <category>Ticket</category> </item> <item> <dc:creator>John Maddock</dc:creator> <pubDate>Mon, 28 Oct 2013 12:08:33 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/9177#comment:4 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/9177#comment:4</guid> <description> <p> As mentioned above: their fine, especially if you use numeric_limits&lt;&gt;::max_digits10 rather than digits10+2 when BOOST_NO_CXX11_NUMERIC_LIMITS is not defined. Note that since we're talking about class types here, none of this code will be triggered by default, it requires the user to mark the type as fundamental (using your traits classes). I guess using a bit more introspection we could probably figure out if a type has a serialize member or not, and if not then forward to the operator&lt;&lt; code... but that may be too clever, or not? </p> </description> <category>Ticket</category> </item> <item> <dc:creator>John Maddock</dc:creator> <pubDate>Mon, 28 Oct 2013 12:47:25 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/9177#comment:5 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/9177#comment:5</guid> <description> <p> Ah, just looked at your updated code and there are a number of mistakes: </p> <p> a) You mispelled my name. b) It uses numeric_limits&lt;float&gt; which will clearly only work for type <code>float</code> and not double, long double etc. c) The formula for number of digits you use is suitable for base2 only. d) You're still not outputting in scientific format - that results in one less significant digit being printed (strange but true), and as a result values do not always round trip. e) I see you also removed the save-and-restore formatting options - that may not matter frankly, but I thought it was a nicety to reset these at the end of the procedure so that classes that have their own serialize functions see whatever iostream flags were originally set in the stream by the user. </p> <p> I'm attaching a patch for these, plus to use numeric_limits&lt;&gt;::max_digits10 when available. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>John Maddock</dc:creator> <pubDate>Mon, 28 Oct 2013 12:47:38 GMT</pubDate> <title>status changed; resolution deleted https://svn.boost.org/trac10/ticket/9177#comment:6 https://svn.boost.org/trac10/ticket/9177#comment:6 <ul> <li><strong>status</strong> <span class="trac-field-old">closed</span> → <span class="trac-field-new">reopened</span> </li> <li><strong>resolution</strong> <span class="trac-field-deleted">fixed</span> </li> </ul> Ticket John Maddock Mon, 28 Oct 2013 12:48:18 GMT attachment set https://svn.boost.org/trac10/ticket/9177 https://svn.boost.org/trac10/ticket/9177 <ul> <li><strong>attachment</strong> → <span class="trac-field-new">basic_text_oprimitive.hpp.2.patch</span> </li> </ul> Ticket John Maddock Mon, 28 Oct 2013 13:23:23 GMT <link>https://svn.boost.org/trac10/ticket/9177#comment:7 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/9177#comment:7</guid> <description> <p> I'm also attaching a test case - I'll let you pick a better name for the file, you have to build and run it 3 times with TEST_FLOAT, TEST_DOUBLE and TEST_LONG_DOUBLE defined. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>John Maddock</dc:creator> <pubDate>Mon, 28 Oct 2013 13:23:44 GMT</pubDate> <title>attachment set https://svn.boost.org/trac10/ticket/9177 https://svn.boost.org/trac10/ticket/9177 <ul> <li><strong>attachment</strong> → <span class="trac-field-new">t.cpp</span> </li> </ul> Ticket Robert Ramey Mon, 28 Oct 2013 17:31:09 GMT <link>https://svn.boost.org/trac10/ticket/9177#comment:8 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/9177#comment:8</guid> <description> <p> Thanks for the interesting comments. </p> <p> a) sorry about misspelling your name, </p> <p> b) I noticed that float vs double after checkin - fixed now </p> <p> c) usage of the kahan formula. In my own tests I found it didn't work. It seemed to me that it should work in our case given that the radix trait of our floating point types should be 2. If understand this correctly ( which I doubt) the code in your patch should work the same as mine. When I got these results, changed to the current version you see now in the repository which used digits10+2. This gave results without round trip errors in gcc and max round trip error of 1 with mdvc 9.0. </p> <p> d) I fixed the scientific format issue. </p> <p> e) i implements save/restore state using io state saver for both the precision and format (scientific). This should leave the stream in the same state it was before. I haven't uploaded this yet. </p> <p> I used digits10 + 2. I have no problem incorporating your patch. I was reluctant to do this as I didn't find BOOST_NO_CXX11_NUMERIC_LIMITS in the 1.54 documentation. I'll presume it's in the next release. </p> <p> I'll incorporate your test in the future. </p> <p> One big problem is the usage of BOOST_CHECK_EQUAL. Previously I had used my own ad-hoc method to test whether values were "close enough". Of course now that we're looking at this more carefully it became clear that my test wasn't really correct. I moved to your float_distance function which addressed these issue. I set a tolerance for 1 or 2 bits difference. Now I'm wondering if I should set that to zero - I'll look into that later. I really don't have the confidence that the wide variety of platforms which I would like the serialization library to target all have floating point correctly implemented. I'll think about this. </p> <p> Thanks for you help with all this. </p> <p> Robert Ramey </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Robert Ramey</dc:creator> <pubDate>Mon, 02 Dec 2013 22:18:45 GMT</pubDate> <title>status changed; resolution set https://svn.boost.org/trac10/ticket/9177#comment:9 https://svn.boost.org/trac10/ticket/9177#comment:9 <ul> <li><strong>status</strong> <span class="trac-field-old">reopened</span> → <span class="trac-field-new">closed</span> </li> <li><strong>resolution</strong> → <span class="trac-field-new">fixed</span> </li> </ul> Ticket boost@… Mon, 09 Jun 2014 21:24:45 GMT <link>https://svn.boost.org/trac10/ticket/9177#comment:10 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/9177#comment:10</guid> <description> <p> This is still a problem for floating point in 1.55. The value 2.23783695e+038 does not round trip. </p> <p> I can't tell from Trac if this was supposed to be fixed in 1.55 or not. If it isn't apologies in advance. </p> <p> Please take a look at Bruce Dawson's page on portably serializing floats. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Robert Ramey</dc:creator> <pubDate>Wed, 25 Jun 2014 16:44:26 GMT</pubDate> <title>status changed; resolution deleted https://svn.boost.org/trac10/ticket/9177#comment:11 https://svn.boost.org/trac10/ticket/9177#comment:11 <ul> <li><strong>status</strong> <span class="trac-field-old">closed</span> → <span class="trac-field-new">reopened</span> </li> <li><strong>resolution</strong> <span class="trac-field-deleted">fixed</span> </li> </ul> Ticket