Newsgroup: it.comp.lang.c++
Titolo: parse numerico con tipo automatico

Salve,
sto cercando una libreria/classe/componente che mi faccia il parsing di
una string/const char*/iteratori esattamaente come istream o
boost::lexical_cast ma che sia in grado di darmi il tipo, via
type_info, più adatto a contenerne il valore. Per adatto, al momento
intendo con il sizeof del tipo più piccolo.
Brevemente, se avessi "1E3" mi basterebbe uno short che ha sizeof 2
sulla mia piattaforma che un float da 4.
Sto scrivendomela, funziona per molti casi, ma non riesco a chiudere
tutte le discrepanze che vengono fuori dai test.
Saluti,
alef


Non sono a conoscenza di qualcosa di preconfezionato, ma dovrebbe
comunque essere piuttosto semplice. Perche' non posti quello che hai
gia' scritto?
Ciao,
Nicola

 sulla mia piattaforma che un float da 4.
posso suggerire un approccio complementare?  se hai un'idea del range
della variabile, la definisci tu come ti pare; e poi la inizializzi a
runtime.  io ho gia' una piccola suite che legge files con istruzioni
del tipo "a = 10" (scalari) o "a = 1 : 2 : 3" (arrays) e funziona con
qualunque ADT che posseggaun estrattore da istream, e se vuoi te la
passo...
-- 
Maurizio Loreti                         
http://www.pd.infn.it/~loreti/mlo.html
Dept. of Physics, Univ. of Padova, Italy              ROT13: 
ybe...@cq.vasa.vg

 gia' scritto?
Sono tre file. Qui li ho ridotti, tolti include, commenti, ripetizioni.
Il namespace pod contiene i nomi dei tipi ma la funzione che intendo e
la auto_lexical_cast.
E' molto stupida, non ci sono controlli, fa il parse via Spirit del
formato float cercando di capire se può ottimizzare a intero.
Perdonate il casino nel codice.
Saluti,
alef
--- podname.hpp ---
namespace pod {
    template<typename T> struct name_t ;
#   define PODNAME_DECL(TYPE) \
	template<> struct name_t<TYPE> { \
	    static const char* str; \
	    operator const char*() { return str; } \
	};
    // thanks Kiuhnm from it.comp.lang.c++
    PODNAME_DECL(bool);
    PODNAME_DECL(signed char);
    ...
#   undef PODNAME_DECL
    extern std::map<const std::type_info*, const char*> names;
} // ns pod
--- podname.cpp ---
namespace pod {
#define PODNAME_DEF(TYPE) \
    const char* name_t<TYPE>::str = #TYPE ;
    PODNAME_DEF(bool);
    PODNAME_DEF(signed char);
    ...
#undef PODNAME_DEF
typedef std::pair<const std::type_info*, const char*> name_pair_t;
template<typename T>
struct name_pair_ctor {
    operator const name_pair_t () {
	return name_pair_t( &typeid(T), name_t<T>::str );
    }
};
name_pair_t name_pairs[] = {
     name_pair_ctor<bool>()
    ,name_pair_ctor<signed char>()
    ...
};
std::map<const std::type_info*, const char*> names(
     &name_pairs[0]
    ,&name_pairs[ sizeof(name_pairs)/sizeof(name_pairs[0]) ]
);
} // ns pod
-- auto_lexical_cast.cpp ---
template<typename T>
struct has_lexical_cast {
    has_lexical_cast(const string &s) : fail(false) {
	try {
	    _value = lexical_cast<T>(s);
	} catch (bad_lexical_cast&) {
	    fail = true;
	}
    }
    const T& value() const { return _value; }
    const char* name() const { return pod::name_t<T>() ; }
    const size_t size() const { return sizeof( T ); }
    const bool good() const { return !fail; }
    }
private:
    T _value;
    bool fail;
};
template<> struct has_lexical_cast<bool> {
    has_lexical_cast(const string &s) : fail(false) {
	istringstream is(s);
	is >> boolalpha >> _value;
	fail = is.fail();
    }
    const bool value() const { return _value; }
    const char* name() const { return pod::name_t<bool>() ; }
    const size_t size() const { return sizeof( bool ); }
    const bool good() const { return !fail; }
    }
private:
    bool _value ;
    bool fail ;
};
//  parse a number string and find the smallest type
boost::any auto_lexical_cast(const string& s) {
    unsigned short
	trailing_zeros = 0 // needed for negative exponent
	,whole_fractional_zeros = 0 // for positive exponent
        ,fractional_digits = 0 // for pos/neg exponent
        ;
    char sign;
    bool try_integer = false;
    int exponent = 0;
    string // with sign
        mantissa_str
        ,fractional_str
        ;
    if (s.empty())
	return any();
    if (s == "0" || s == "0.0")
	return any(false);
    if (s == "1" || s == "1.0")
	return any(true);
    { has_lexical_cast<bool> b(s);
      if (b.good()) { return any(b); }
    }
    rule<> zs = +ch_p('0') // zeros
	;
    rule<> nzs = +(digit_p - ch_p('0')) // non zero digits
	;
    // trailing mantissa zeros
    rule<> trailing_zs = +( ch_p('0')[ increment_a(trailing_zeros) ] )
	;
    // leading mantissa non zero digits
    rule<> leading_nzs =
	longest_d[ +(zs >> nzs) | nzs | eps_p ]
	;
    // integer part of the number
    rule<> mantissa =
	 ( nzs | zs )
	 >> !leading_nzs
	 >> !trailing_zs // trailing zeros
	;
    // the dot
    rule<> dot = ch_p('.') // dot
	;
    // the exponent
    rule<> E = (ch_p('e') | ch_p('E')) // e|E
	;
    // the exponent number
    rule<> exp = E >> int_p[ assign_a(exponent) ];
	;
    // if fractional digits are all zeros
    rule<> whole_fractional_zs = // .000
	+( ch_p('0')[ increment_a(whole_fractional_zeros) ] )
	;
    // digits in the fractional part if are not all zeros
    rule<> fractional_dgs = // .012
        +( digit_p[ increment_a(fractional_digits) ] )
        ;
    // the fractional part
    rule<> fractional = // .000 | .012
	longest_d[whole_fractional_zs | fractional_dgs]
	;
    // the number at whole
    rule<> number =
	(
	    !sign_p[ assign_a(sign) ]
	    >> !zs // strip leading zeroes in mantissa
	    >> mantissa[ assign_a( mantissa_str ) ]
	)
	>> !( dot >> !(fractional)[ assign_a(fractional_str) ] )
	>> !exp
	;
    // conflicting cases
    if (fractional_digits > 0 && whole_fractional_zeros > 0)
	fractional_digits = 0;
    parse(s.c_str(), number);
    ostringstream os ;
    // integer type ?
    if (
	 fractional_digits > 0
      && exponent > 0
      && fractional_digits <= exponent
    ) { // 10.123e3 -> 10123 ,  10.1e4 -> 101000
        os  << mantissa_str
	    << fractional_str
            << string(exponent-fractional_digits, '0') ;
	try_integer = true;
    }
    else if (
         whole_fractional_zeros > 0
      && exponent >= 0
    ) { // 10.0e3 -> 10000 , 10000.0e0 -> 10000
        os  << mantissa_str
            << string(exponent, '0') ;
	try_integer = true;
    }
    else if (
	 trailing_zeros > 0
      &&  exponent < 0
      && -exponent <= trailing_zeros
    ) { // 200e-1 -> 20 , 300e-2 -> 3
        os  << mantissa_str.substr(0, mantissa_str.length() -
-exponent) ;
	try_integer = true;
    } else if (
	exponent > 0
    ) { // 1e10 -> 10000000000
        os  << mantissa_str << string(exponent, '0') ;
	try_integer = true;
    } else if (
	fractional_digits == 0
     && exponent == 0
    ) {
	os << mantissa_str ;
	try_integer = true;
    } else {
        os << s ;
    }
    const string result = os.str() ;
    any value;
    for (;;) {
#define ASSIGN_AND_TEST(TYPE) \
	    { has_lexical_cast<TYPE> test( result ); \
	      if ( test.good() ) { value = test.value() ; break; } \
	    }
	    if (try_integer) {
		ASSIGN_AND_TEST(unsigned short);
		...
	    }
	    if (value.empty()) // all previous failed
	    {
		ASSIGN_AND_TEST(float);
		ASSIGN_AND_TEST(double);
		ASSIGN_AND_TEST(long double)
	    }
#undef ASSIGN_AND_TEST
	break;
    }
    return value; // can be empty
}

 #undef PODNAME_DEF
Una domanda sorge spontanea: perche' non usi direttamente
type_info::name() invece di tutto 'sto pasticcio?
Altra domanda: name_t<>::str la usi anche direttamente o solo tramite
l'operatore di conversione? Nel secondo caso tanto vale omettere il
membro dato statico e mettere la stringa direttamente nella funzione...
E poi: non e' molto piu' semplice leggere un long double e poi
ragionare sulla sua grandezza?
Ciao,
Nicola

 ragionare sulla sua grandezza?
Su questo ho dei dubbi, ma magari potreste aiutarmi.
Leggendo questo:
http://boost.org/libs/numeric/conversion/doc/definitions.html
ho avuto l'impressione che esistano valori float che anche essendo nel
range di un intero non siano rappresentabili esattamante.
O meglio, che possano esistere float che arrotondano a int in maniera
errata rispetto alla stringa interpretata in origine.
Per esempio, se leggessi "1.234e10" come floating, lo portassi a un
intero, potrei ottenere 
12399999999
 ?
Per fare un test di ciò, avevo bisogno di sapere se il cast di un
valore sia "esatto" (termine in Definitions), cioé se il rounding c'è
stato. Ma non ho trovato come ottenere quest'informazione; anzi,
probabilmente non c'è.
Però quello che dici tu lo potrei usare per il passo successivo.
Ora che auto_lexical_cast pare funzionare, devo fare una statistica
min/max su una serie di quei numeri. Siccome ottengo un boost::any,
devo
trovare o fare un bool operator<(any,any). Per evitare tutte le
combinazioni di una decina di tipi base, potrei farne una ventina a
long
long per interi e qualcuna long double per i float.
Ora la domanda però nasce spontanea a me: facendo intero < float, a
cosa
vado incontro in termini di promozione, arrotondamendo, errori ?

 intero, potrei ottenere 
12399999999
 ?
Non sono un esperto, ma a naso direi che questo non succede a patto che
il numero sia rappresentabile sulla mantissa del tipo che consideri. Se
il numero e' poi divisibile per l'n-esima potenza di due il vincolo si
riduce alla rappresentabilita' del numero diviso 2 elevato alla n.
Ciao,
Nicola

Mi sa che mi faccio un giro con la GNU MP.
http://www.swox.com/gmp/manual/C_002b_002b-Formatted-Input.html

 
Basta considerare che, nella maggior parte dei casi, sia int che float
sono rappresentati in 32 bits.
Saluti  Manlio Perillo