Arithmetic extractors
Russell Davidson
russell@ehess.cnrs-mrs.fr
Wed Jun 14 12:36:00 GMT 2000
Sorry for the delay; it took me a lot longer than I expected to fix up the
code in _M_extract for extracting integer type and floating point numbers.
After much study of the standard, I think that what I have done is
conformant, except perhaps for a couple of points.
First, in most of the do_get() functions (which I have not altered), there
is a comment to the effect that leading zeros are eliminated. I have taken
care to do this in _M_extract, although the standard makes no mention of
it, except for the case of a leading '0' meaning octal if the basefield is
0, and "0x" or "0X" meaning hex in similar circumstances. So, am I right
to do this?
The other point I am not sure of is how to recognise white space. The
standard allows there to be white space after a sign. In order to
implement this, I just used the function isspace() (in namespace std::, I
guess - I #include bits/std_cctype.h). Is this appropriate, or should I
dig into the locale associated with the ios_base& argument __io in order
to find out what white space is? If indeed I should, can I be helped to
see how best to do so? Using only public functions, it looks as though one
has to call getloc(), and then use the ctype facet, etc. etc... If it is
needed, there is probably an implementation-specific way to do it more
easily.
Another couple of points where I think I'm right, but would be happy to
receive confirmation. As I read it, the standard does not allow for any
base other than 10 for floating point numbers. Thus worrying about the
base is appropriate only for integer types. In any case, I don't think
that the stdlib function strtod() does anything other than base 10.
Then, if the basefield in the ios_base is zero, rather than oct or dec or
hex, it seems that the conversion takes place (section 22.2.2.1.2 of the
standard) as though the format %i were used with stdio. This allows the 0
prefix for oct and 0x or 0X for hex. In addition, if the basefield is set
to hex, my reading is that 0x or 0X are admissible even though not
required.
This is how I have programmed things, and so, if there are disagreements
over my understanding of the standard, I should be told so.
I added a new test function, test10(), to be added to
27_io/istream_extractor_arith.cc, in order to do some testing of points
that were not tested in the existing test functions. These new tests
should show what I expect, and, if I am wrong, it should be easy to see
how and where.
Incidentally, even including the new test,
make check-target-libstdc++-v3
now gives complete success with my setup on i686-pc-linux, using the cvs
gcc/g++ compiler.
I attach the complete files bits/locale_facets.tcc and
testsuite/27_io/istream_extractor_arith.cc, since the patches, which
follow, will be difficult to follow. On the other hand, it is my fond hope
that the new code itself will be understandable.
Regards, Russell.
Russell Davidson email: russell@ehess.cnrs-mrs.fr
GREQAM,
Centre de la Vieille Charite, telephone: +33-4.91.14.07.40
F-13002 Marseille fax: +33-4.91.90.02.27
*** locale_facets.tcc.save Fri Jun 2 21:41:42 2000
--- locale_facets.tcc Wed Jun 14 21:04:56 2000
***************
*** 33,43 ****
#define _CPP_BITS_LOCFACETS_TCC 1
#include <bits/std_cerrno.h>
! #include <bits/std_cstdlib.h> // For strof, strtold
! #include <bits/std_limits.h> // For numeric_limits
#include <bits/std_vector.h>
! #include <bits/std_memory.h> // For auto_ptr
! #include <bits/sbuf_iter.h> // For streambuf_iterators
namespace std
{
--- 33,44 ----
#define _CPP_BITS_LOCFACETS_TCC 1
#include <bits/std_cerrno.h>
! #include <bits/std_cstdlib.h> // For strof, strtold
! #include <bits/std_limits.h> // For numeric_limits
#include <bits/std_vector.h>
! #include <bits/std_memory.h> // For auto_ptr
! #include <bits/sbuf_iter.h> // For streambuf_iterators
! #include <bits/std_cctype.h> // For isspace
namespace std
{
*************** namespace std
*** 297,428 ****
{
typedef _Format_cache<char> __cache_type;
// Stage 1: determine a conversion specifier.
ios_base::fmtflags __basefield = __io.flags() & ios_base::basefield;
! if (__basefield == ios_base::oct)
__base = 8;
else if (__basefield == ios_base::hex)
__base = 16;
else
__base = 10;
// Stage 2: extract characters.
__cache_type const* __fmt = __cache_type::_S_get(__io);
bool __valid = __beg != __end;
string __grp;
int __sep_pos = 0;
int __pos = 0;
- bool __testdec = false;
- bool __testEE = false;
- bool __testsign = false;
- bool __testEEsign = false;
const char* __lits = __fmt->_S_literals;
! while (__valid && __beg != __end)
! {
! __valid = false;
! char __c = *__beg;
! const char* __p = strchr(__fmt->_S_literals, __c);
!
! // NB: strchr returns true for __c == 0x0
! if (__p && __c)
! {
! // Check for sign and accept if appropriate.
! if ((__p == &__lits[__cache_type::_S_minus])
! || (__p == &__lits[__cache_type::_S_plus]))
{
! if (__testEE)
! {
! if (__testEEsign)
! break;
! __testEEsign = true;
! }
! else
! {
! if (__testsign)
! break;
! __testsign = true;
! }
}
! // Check for exponential part and accept if appropriate.
! else if ((__p == &__lits[__cache_type::_S_ee])
! || (__p == &__lits[__cache_type::_S_Ee]))
! {
! if (!__fp || __testEE || !__testsign)
! break;
! __testEE = true;
! }
! // Check for hexadecimal base parts.
! else if ((__p == &__lits[__cache_type::_S_x])
! || (__p == &__lits[__cache_type::_S_X]))
{
! if (__base != 16
! && __xtrc[__pos - 1] != __lits[__cache_type::_S_digits])
! break;
}
! // Check for appropriate digits. If found, too late for a sign
! else if ((__p >= &__lits[__cache_type::_S_digits]
! && __p < &__lits[__cache_type::_S_digits + __base])
! || (__p >= &__lits[__cache_type::_S_udigits]
! && __p < &__lits[__cache_type::_S_udigits + __base]))
! {
! __testsign = true;
! if (__testEE)
! __testEEsign = true;
! }
! // Nothing else will do
! else break;
!
! __xtrc[__pos] = __c;
! ++__pos;
! ++__sep_pos;
! __valid = true;
}
! else if (__c == __fmt->_M_thousands_sep
! && __fmt->_M_use_grouping && !__testdec)
{
! // NB: Thousands separator at the beginning of a string
! // is a no-no, as is two consecutive thousands
! // separators, as is thousands separator to the right of
! // a decimal point.
! if (__sep_pos && !__testdec)
! {
! __grp += static_cast<char>(__sep_pos);
! __sep_pos = 0;
__valid = true;
}
- else
- __err |= ios_base::failbit;
}
! else if (__c == __fmt->_M_decimal_point
! && __fp && !__testdec)
{
! __xtrc[__pos] = '.';
! ++__pos;
! if (__fmt->_M_use_grouping && !__grp.empty())
{
__grp += static_cast<char>(__sep_pos);
__sep_pos = 0;
}
! __testdec = true;
! __valid = true;
}
! if (__valid)
++__beg;
}
! __xtrc[__pos] = '\0';
! if (__beg == __end)
! __err |= ios_base::eofbit;
!
// Digit grouping is checked. If _M_groupings() doesn't
// match, then get very very upset, and set failbit.
if (__fmt->_M_use_grouping && !__grp.empty())
{
! // Add the ending grouping if the decimal point hasn't
! // already delineated the end of the sequence that grouping
! // cares about.
! if (!__testdec)
! __grp += static_cast<char>(__sep_pos);
// __grp is parsed L to R
// 1,222,444 == __grp of "/1/3/3"
--- 298,461 ----
{
typedef _Format_cache<char> __cache_type;
+ // Prepare for possible failure
+ __xtrc[0] = '\0';
+
// Stage 1: determine a conversion specifier.
ios_base::fmtflags __basefield = __io.flags() & ios_base::basefield;
! if (__basefield == ios_base::dec)
! __base = 10;
! else if (__basefield == ios_base::oct)
__base = 8;
else if (__basefield == ios_base::hex)
__base = 16;
else
+ __base = 0;
+ // As far as I can tell, bases other than 10 are not available for
+ // floating point types
+ if (__fp)
__base = 10;
// Stage 2: extract characters.
__cache_type const* __fmt = __cache_type::_S_get(__io);
bool __valid = __beg != __end;
+ // Fail quickly if !__valid
+ if (!__valid)
+ {
+ __err |= (ios_base::eofbit | ios_base::failbit);
+ return;
+ }
+
+ // Acceptable formats for numbers here are based on 22.2.3.1
string __grp;
int __sep_pos = 0;
int __pos = 0;
const char* __lits = __fmt->_S_literals;
+ char __c = *__beg;
! // Check first for sign
! bool __testsign = false;
! if ((__c == __lits[__cache_type::_S_minus])
! || (__c == __lits[__cache_type::_S_plus]))
! {
! __xtrc[__pos++] = __c;
! ++__beg;
! __testsign = true;
! // whitespace may follow a sign
! while ((__beg != __end) && (isspace(*__beg)))
! ++__beg;
!
! // There had better be more to come...
! if (__beg == __end)
! {
! __xtrc[__pos] = '\0';
! __err |= (ios_base::eofbit | ios_base::failbit);
! return;
! }
! }
!
! bool __testzero = false; // Has there been a leading zero?
!
! // Now check if first character is a zero
! __c = *__beg;
! if (__c == __lits[__cache_type::_S_digits])
! {
! __testzero = true;
! ++__beg;
!
! // We have to check for __beg == __end here. If so,
! // a plain '0' (possibly with a sign) can be got rid of now
! if (__beg == __end)
! {
! __xtrc[__pos++] = __c;
! __xtrc[__pos] = '\0';
! __err |= ios_base::eofbit;
! return;
! }
!
! // Figure out base for integer types only
! // Based on Table 55 of 22.2.2.1.2
! if (!__fp && __base != 10 && __base != 8)
! {
! // Here, __base == 0 or 16
! __c = *__beg;
! if ((__c == __lits[__cache_type::_S_x])
! || (__c == __lits[__cache_type::_S_X]))
{
! ++__beg;
! __base = 16;
! __testzero = false; // "0x" is not a leading zero
}
! else
! __base = 8;
! }
!
! // Remove any more leading zeros
! while (__beg != __end)
! {
! if (*__beg == __lits[__cache_type::_S_digits])
{
! ++__beg;
! __testzero = true;
}
! else
! break;
}
! }
! else if (__base == 0) // 1st character is not zero
! __base = 10;
!
! // We now seek "units", i.e. digits and thousands separators.
! // We may need to know if anything is found here. A leading zero
! // (removed by now) would count.
!
! bool __testunits = __testzero;
! while (__valid && __beg != __end)
! {
! __valid = false;
! __c = *__beg;
! const char* __p = strchr(__fmt->_S_literals, __c);
!
! // NB: strchr returns true for __c == 0x0
! if (__p && __c)
{
! // Try first for acceptable digit; record it if found
! if ((__p >= &__lits[__cache_type::_S_digits]
! && __p < &__lits[__cache_type::_S_digits + __base])
! || (__p >= &__lits[__cache_type::_S_udigits]
! && __p < &__lits[__cache_type::_S_udigits + __base]))
! {
! __xtrc[__pos++] = __c;
! ++__sep_pos;
__valid = true;
+ __testunits = true;
}
}
! else if (__c == __fmt->_M_thousands_sep
! && __fmt->_M_use_grouping)
{
! // NB: Thousands separator at the beginning of a string
! // is a no-no, as is two consecutive thousands
! // separators
! if (__sep_pos)
{
__grp += static_cast<char>(__sep_pos);
__sep_pos = 0;
+ __valid = true;
}
! else
! __err |= ios_base::failbit;
}
! if (__valid)
++__beg;
}
!
// Digit grouping is checked. If _M_groupings() doesn't
// match, then get very very upset, and set failbit.
if (__fmt->_M_use_grouping && !__grp.empty())
{
! // Add the ending grouping
! __grp += static_cast<char>(__sep_pos);
// __grp is parsed L to R
// 1,222,444 == __grp of "/1/3/3"
*************** namespace std
*** 438,444 ****
// numpunct::grouping string exactly, starting at the
// right-most point of the parsed sequence of elements ...
while (__test && __i < __n - 1)
! for (__j = 0; __test && __j < __len && __i < __n - 1; ++__j, ++__i)
__test &= __fmt->_M_grouping[__j] == __grp[__n - __i - 1];
// ... but the last parsed grouping can be <= numpunct
// grouping.
--- 471,477 ----
// numpunct::grouping string exactly, starting at the
// right-most point of the parsed sequence of elements ...
while (__test && __i < __n - 1)
! for (__j = 0; __test && __j < __len && __i < __n - 1; ++__j,++__i)
__test &= __fmt->_M_grouping[__j] == __grp[__n - __i - 1];
// ... but the last parsed grouping can be <= numpunct
// grouping.
*************** namespace std
*** 446,453 ****
__test &= __fmt->_M_grouping[__j] >= __grp[__n - __i - 1];
if (!__test)
! __err |= ios_base::failbit;
}
}
// NB: This is an unresolved library defect #17
--- 479,592 ----
__test &= __fmt->_M_grouping[__j] >= __grp[__n - __i - 1];
if (!__test)
! {
! __err |= ios_base::failbit;
! __xtrc[__pos] = '\0';
! if (__beg == __end)
! __err |= ios_base::eofbit;
! return;
! }
}
+
+ // If there was nothing but zeros, put one in the output string
+ if (__testzero && (__pos == 0 || (__pos == 1 && __testsign)))
+ __xtrc[__pos++] = __lits[__cache_type::_S_digits];
+
+ // That's it for integer types. Remaining code is for floating point
+ if (__fp && __beg != __end)
+ {
+ __c = *__beg;
+ // Check first for decimal point. There MUST be one if
+ // __testunits is false.
+ bool __testdec = false; // Is there a decimal point
+ // with digits following it?
+ if (__c == __fmt->_M_decimal_point)
+ {
+ __xtrc[__pos++] = '.';
+ ++__beg;
+ // Now we get any digits after the decimal point
+ // There MUST be some if __testunits is false.
+ while (__beg != __end)
+ {
+ __c = *__beg;
+ const char* __p = strchr(__fmt->_S_literals, __c);
+ if ((__p >= &__lits[__cache_type::_S_digits]
+ && __p < &__lits[__cache_type::_S_digits + __base])
+ || (__p >= &__lits[__cache_type::_S_udigits]
+ && __p < &__lits[__cache_type::_S_udigits + __base]))
+ {
+ __xtrc[__pos++] = __c;
+ ++__beg;
+ __testdec = true;
+ }
+ else
+ break;
+ }
+ }
+ if (!__testunits && !__testdec) // Ill formed
+ {
+ __err |= ios_base::failbit;
+ __xtrc[__pos] = '\0';
+ if (__beg == __end)
+ __err |= ios_base::eofbit;
+ return;
+ }
+
+ // Now we may find an exponent
+ if (__beg != __end)
+ {
+ __c = *__beg;
+ if ((__c == __lits[__cache_type::_S_ee])
+ || (__c == __lits[__cache_type::_S_Ee]))
+ {
+ __xtrc[__pos++] = __c;
+ ++__beg;
+ // Now there may be a sign
+ if (__beg != __end)
+ {
+ __c = *__beg;
+ if ((__c == __lits[__cache_type::_S_minus])
+ || (__c == __lits[__cache_type::_S_plus]))
+ {
+ __xtrc[__pos++] = __c;
+ ++__beg;
+ // whitespace may follow a sign
+ while ((__beg != __end) && (isspace(*__beg)))
+ ++__beg;
+
+ }
+ }
+ // And now there must be some digits
+ if (__beg == __end)
+ {
+ __xtrc[__pos] = '\0';
+ __err |= (ios_base::eofbit | ios_base::failbit);
+ return;
+ }
+ while (__beg != __end)
+ {
+ __c = *__beg;
+ const char* __p = strchr(__fmt->_S_literals, __c);
+ if ((__p >= &__lits[__cache_type::_S_digits]
+ && __p < &__lits[__cache_type::_S_digits + __base])
+ || (__p >= &__lits[__cache_type::_S_udigits]
+ && __p < &__lits[__cache_type::_S_udigits + __base]))
+ {
+ __xtrc[__pos++] = __c;
+ ++__beg;
+ }
+ else
+ break;
+ }
+ }
+ }
+ // Finally, that's it for floating point
+ }
+
+ // Finish up
+ __xtrc[__pos] = '\0';
+ if (__beg == __end)
+ __err |= ios_base::eofbit;
}
// NB: This is an unresolved library defect #17
*** istream_extractor_arith.cc.save Thu Jun 1 04:55:30 2000
--- istream_extractor_arith.cc Wed Jun 14 20:24:02 2000
*************** bool test09()
*** 378,383 ****
--- 378,487 ----
return test;
}
+ bool test10() {
+ std::string str_01("0 00 000 +0 + 0 - 0");
+ std::stringbuf isbuf_01(str_01);
+ std::istream is_01(&isbuf_01);
+
+ bool test = true;
+
+ int n = 365;
+ is_01 >> n;
+ test &= n == 0;
+ n = 364;
+ is_01 >> n;
+ test &= n == 0;
+ n = 363;
+ is_01 >> n;
+ test &= n == 0;
+ n = 362;
+ is_01 >> n;
+ test &= n == 0;
+ n = 361;
+ is_01 >> n;
+ test &= n == 0;
+ n = 360;
+ is_01 >> n;
+ test &= n == 0;
+ test &= is_01.rdstate() == std::ios_base::eofbit;
+
+ std::string str_02("0x32 0X33 033 33");
+ std::stringbuf isbuf_02(str_02);
+ std::istream is_02(&isbuf_02);
+ is_02.unsetf(std::ios_base::basefield);
+ is_02 >> n;
+ test &= n == 50;
+ is_02 >> n;
+ test &= n == 51;
+ is_02 >> n;
+ test &= n == 27;
+ is_02 >> n;
+ test &= n == 33;
+ test &= is_02.rdstate() == std::ios_base::eofbit;
+
+ std::stringbuf isbuf_03(str_02);
+ std::istream is_03(&isbuf_03);
+ char c;
+ int m;
+
+ is_03 >> std::dec >> n >> c >> m;
+ test &= n == 0;
+ test &= c == 'x';
+ test &= m == 32;
+
+ is_03 >> std::oct >> m >> c >> n;
+ test &= m == 0;
+ test &= c == 'X';
+ test &= n == 27;
+
+ is_03 >> std::dec >> m >> n;
+ test &= m == 33;
+ test &= n == 33;
+ test &= is_03.rdstate() == std::ios_base::eofbit;
+
+ std::string str_04("3. 4.5E+ 2a5E-3 .6E1");
+ std::stringbuf isbuf_04(str_04);
+ std::istream is_04(&isbuf_04);
+
+ double f;
+ is_04 >> f;
+ test &= f == 3.0;
+ is_04 >> f;
+ test &= f == 450.0;
+ is_04.ignore();
+ is_04 >> f;
+ test &= f == 0.005;
+ is_04 >> f;
+ test &= f == 6;
+ test &= is_03.rdstate() == std::ios_base::eofbit;
+
+ std::string str_05("0E20 5Ea E16");
+ std::stringbuf isbuf_05(str_05);
+ std::istream is_05(&isbuf_05);
+
+ is_05 >> f;
+ test &= f == 0;
+ is_05 >> f;
+ test &= f == 0;
+ test &= is_05.rdstate() == std::ios_base::failbit;
+ is_05.clear();
+ is_05 >> c;
+ test &= c == 'a';
+ is_05 >> f;
+ test &= f == 0;
+ test &= is_05.rdstate() == std::ios_base::failbit;
+ is_05.clear();
+ is_05.ignore();
+ is_05 >> n;
+ test &= n == 16;
+
+ #ifdef DEBUG_ASSERT
+ assert(test);
+ #endif
+
+ return test;
+ }
+
int main()
{
test01();
*************** int main()
*** 388,393 ****
--- 492,498 ----
test07();
test08();
test09();
+ test10();
return 0;
}
More information about the Libstdc++
mailing list