4

There is something strange I noticed when comparing boost::lexical_cast and boost spirit parsing. I'm trying to parse a string into float. for some reason spirit gives very imprecise result. for example: when parsing string "219721.03839999999" using lexical_cast i get 219721.03 which is more or less OK. but when I use spirit (see code below) I get "219721.11" which is far from bein OK. Any idea why it happens?

template<>
inline float LexicalCastWithTag(const std::string& arg)
{
    float result = 0;

    if(arg.empty())
    {
        throw BadLexicalCast("Cannot convert from to std::string to float");
    }

    auto itBeg = arg.begin();
    auto itEnd = arg.end();

    if(!boost::spirit::qi::parse(itBeg, itEnd, boost::spirit::qi::float_, result) || itBeg != itEnd)
    {
        throw BadLexicalCast("Cannot convert from to std::string to float");
    }

    return result;
}
kreuzerkrieg
  • 3,009
  • 3
  • 28
  • 59
  • 1
    To be more specific, lexical_cast gives you the correct `219721.03125`, which is the closest valid `float` to `219721.03839999999`, and `qi::float_` gives you `219721.109375`, which indeed doesn't seem right. – Cubbi Jun 30 '13 at 17:30
  • 1
    The integral part of the number almost saturates the capacity. The way the result is calculated in qi::float_type::parse() is such that the 'smallish' errors that occur in 'adding' each fractional digit compounds. Indeed this looks like a bug, as smarter algorithms don't have this suboptimal behaviour. – sehe Jun 30 '13 at 20:24
  • how do we communicate this problem to spirit maintainers? – kreuzerkrieg Jul 01 '13 at 07:15
  • http://www.boost.org/development/bugs.html – G. Civardi Jul 01 '13 at 16:37
  • http://news.gmane.org/gmane.comp.parsers.spirit.general looks like it goes straight to the spirit developers, not the general boost buglist – kreuzerkrieg Jul 02 '13 at 10:39
  • 2
    I just added my $0.019999999552965164 over at [spirit-general](http://boost.2283326.n4.nabble.com/Boost-spirit-floating-number-parser-precision-td4649158.html) – sehe Jul 10 '13 at 09:11
  • yeah, I saw it :) Joel de Guzman added a reply: "This looks like a bug indeed, but I am not sure. It might be that we're seeing the limits of real number parser being designed to be generic. E.g. you can use an infinite precision type and the algorithm should still work. Other algorithms assume float or double. That said, it is still possible to take advantage of customizations for built-in floating point types. It's good that I am currently working on X3 real numbers, so I can use this as a test case to improve precision. I'll port back the results if I have something better to offer." – kreuzerkrieg Jul 10 '13 at 13:57
  • 1
    As was just announced on the [spirit-general] mailing list, looks like this bug has been fixed in the next boost release: http://boost.2283326.n4.nabble.com/Symbol-table-tp4668838p4668842.html /cc @G.Civardi – sehe Nov 09 '14 at 23:03

1 Answers1

6

So it will be probably limitation/bug of "float" type parser. Try to use double_ parser.

#include<iostream>
#include<iomanip>
#include<string>
#include<boost/spirit/include/qi.hpp>

int main()
{
    std::cout.precision(20);

    //float x=219721.03839999999f;  
    //std::cout << x*1.0f << std::endl;  
    //gives 219721.03125  

    double resultD;
    std::string arg="219721.03839999999";

    auto itBeg = arg.begin();
    auto itEnd = arg.end();
    if(!boost::spirit::qi::parse(itBeg, itEnd,boost::spirit::qi::double_,resultD) || itBeg != itEnd)
        std::cerr << "Cannot convert from std::string to double" << std::endl;
    else
        std::cout << "qi::double_:" << resultD << std::endl;

    float resultF;
    itBeg = arg.begin();
    itEnd = arg.end();
    if(!boost::spirit::qi::parse(itBeg, itEnd,boost::spirit::qi::float_,resultF) || itBeg != itEnd)
        std::cerr << "Cannot convert from std::string to float" << std::endl;
    else
        std::cout << "qi::float_ :" << resultF << std::endl;

    return 0;
}

Output:
qi::double_:219721.03839999999036
qi::float_:219721.109375

G. Civardi
  • 667
  • 5
  • 12
  • looks reasonable, I've parsed into double and then just cast the return value into float, worked like a charm :) – kreuzerkrieg Jul 01 '13 at 07:10
  • I think you can just pass a float variable in the parser and the down-casting will be done by the parser. – G. Civardi Jul 01 '13 at 16:40
  • yet another possibility to get the thing done, however, I will prefer to stay in control to things happen to my variables. As you can see noone can be trusted :) – kreuzerkrieg Jul 02 '13 at 10:38
  • That's very pragmatic view unless you can stay with double representation altogether :-) – G. Civardi Jul 02 '13 at 20:12
  • I cant, this is the idea of my lexical cast. There are cases when you want to cast to double and there are when you prefer float. especialy when we talk about tens of millions of structs we hold in memory, each bit counts – kreuzerkrieg Jul 03 '13 at 07:51