Compiling and executing the following code under MSVC 7.0 :
//******************************
#include <iostream>
#include <boost/lexical_cast.hpp>
int main(int argc, char* argv[])
{
try
{
ulong u = boost::lexical_cast<ulong>("-1");
cout<<"Conversion"<<endl;
}
catch(const std::exception&)
{
cout<<"Exception"<<endl;
}
}
//******************************
Output :
>Conversion
//******************************
The Boost lexical_cast<> template function uses a temporary
std::stringstream (or an equivalent class depending on the available STL) to
perform the conversion, like (all the error management code being removed) :
std::stringstream stream;
stream<<input;
stream>>output;
Under MSVC7, the output operator :
std::ostream& operator>>(std::ostream& o, unsigned long l);
calls the C function strtoul(...) to make the conversion. strtoul has a
suprising (at least for me) behaviour concerning negative number string
representation conversion, since it eats the whole string (instead of
stopping on the minus "-" character) and returns ULONG_MAX (and sets "errno"
to ERANGE). The MSVC7 implementation does not check for these overflow
cases, and when converting a negative number string to unsigned long,
std::stringstream therefore returns ULONG_MAX and remain in a valid state.
Is this the expected behaviour or an implementation flaw ?
In the first case, how can you tell an overflow from a successful conversion
of the ULONG_MAX string representation ?
Thanks,
Patrick M�zard.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Patrick
|
8/12/2003 12:55:34 AM |
|
On 11 Aug 2003 20:55:34 -0400, "Patrick M�zard"
<patrick.mezard@ifrance.com> wrote in comp.lang.c++.moderated:
> Compiling and executing the following code under MSVC 7.0 :
>
> //******************************
>
> #include <iostream>
> #include <boost/lexical_cast.hpp>
>
> int main(int argc, char* argv[])
> {
> try
> {
> ulong u = boost::lexical_cast<ulong>("-1");
> cout<<"Conversion"<<endl;
> }
> catch(const std::exception&)
> {
> cout<<"Exception"<<endl;
> }
> }
>
> //******************************
> Output :
>
> >Conversion
> //******************************
>
> The Boost lexical_cast<> template function uses a temporary
> std::stringstream (or an equivalent class depending on the available STL) to
> perform the conversion, like (all the error management code being removed) :
>
> std::stringstream stream;
> stream<<input;
> stream>>output;
>
> Under MSVC7, the output operator :
> std::ostream& operator>>(std::ostream& o, unsigned long l);
> calls the C function strtoul(...) to make the conversion. strtoul has a
> suprising (at least for me) behaviour concerning negative number string
> representation conversion, since it eats the whole string (instead of
> stopping on the minus "-" character) and returns ULONG_MAX (and sets "errno"
> to ERANGE). The MSVC7 implementation does not check for these overflow
> cases, and when converting a negative number string to unsigned long,
> std::stringstream therefore returns ULONG_MAX and remain in a valid state.
There is an error in the VC++ implementation of strtoul() if it works
as you describe, because it should not set errno to ERANGE, although
it should return the value ULONG_MAX.
The C library (inherited by the C++ library) strto... functions, even
the unsigned ones, accept a string which (after skipping any leading
white space) may begin with a plus or minus sign. Assuming a leading
minus sign, as in your case, the operation is rather like that of a
negative numeric literal in source code, that is the value of the
numeric digits is generated, then negated by applying the unary minus
operator to it.
Here is the relevant quotation from the current (1999) C standard,
although it is substantially the same as in the earlier C standard
that the C++ standard is based on:
"If the subject sequence has the expected form and the value of base
is zero, the sequence of characters starting with the first digit is
interpreted as an integer constant according to the rules of 6.4.4.1.
If the subject sequence has the expected form and the value of base
is between 2 and 36, it is used as the base for conversion, ascribing
to each letter its value as given above. If the subject sequence
begins with a minus sign, the value resulting from the conversion is
negated (in the return type). A pointer to the final string is stored
in the object pointed to by endptr, provided that endptr is not a null
pointer."
The key here is the phrase "...negated (in the return type)".
strtoul() first converts the numeric substring "1" to the unsigned
long value 1UL. Then, because of the leading '-', the value of that
unsigned long is negated. Negating an unsigned integer type is always
safe and cannot overflow, due to the defined characteristics of
unsigned integer types in C and C++.
The result is exactly the same as writing this in source code:
unsigned long ul = -1;
Assigning -1 to any unsigned integral type results in the value of
Utype_MAX.
The point is that ERANGE should not be produced by this conversion, as
the result of converting -1 to an unsigned long is within the range of
values for an unsigned long.
ERANGE should only be produced if (for your implementation), the
string of digits represented a value outside the range of -4294967295
(or possible -4294967296) to +4294967295.
> Is this the expected behaviour or an implementation flaw ?
> In the first case, how can you tell an overflow from a successful conversion
> of the ULONG_MAX string representation ?
>
> Thanks,
> Patrick M�zard.
If it weren't for the possible defect in your compiler library's
implementation of strtoul(), you could tell the difference between a
valid input representing ULONG_MAX and an out of range value by
testing errno.
I say possible defect because it is possible that errno might have
already been set to ERANGE for some other reason before the call to
strtoul(). No standard library function clears errno to 0 after a
successful call, and any library function not specified to set errno
to specific values in specific situations may set errno to any other
non-zero value at any time.
There are two ways to test this. Try your code on strings like "-2"
and "-3". If you get ULONG_MAX - 1 and ULONG_MAX - 2, respectively,
the problem may not lie in your compiler's strtoul() implementation.
Alternatively, call strtoul() directly on the string "-1", immediately
after setting errno to 0.
If the tests above fail, I'd suggest checking Microsoft's web site to
find out the procedure for submitting a defect report.
--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++ ftp://snurse-l.org/pub/acllc-c++/faq
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Jack
|
8/12/2003 10:00:01 AM
|
|
"Patrick M�zard" <patrick.mezard@ifrance.com> wrote in message
news:bh8mri$p8s$1@news-reader1.wanadoo.fr...
> Compiling and executing the following code under MSVC 7.0 :
>
> //******************************
>
> #include <iostream>
> #include <boost/lexical_cast.hpp>
>
> int main(int argc, char* argv[])
> {
> try
> {
> ulong u = boost::lexical_cast<ulong>("-1");
> cout<<"Conversion"<<endl;
> }
> catch(const std::exception&)
> {
> cout<<"Exception"<<endl;
> }
> }
>
> //******************************
> Output :
>
> >Conversion
> //******************************
>
> The Boost lexical_cast<> template function uses a temporary
> std::stringstream (or an equivalent class depending on the available STL) to
> perform the conversion, like (all the error management code being removed) :
>
> std::stringstream stream;
> stream<<input;
> stream>>output;
>
> Under MSVC7, the output operator :
> std::ostream& operator>>(std::ostream& o, unsigned long l);
> calls the C function strtoul(...) to make the conversion. strtoul has a
> suprising (at least for me) behaviour concerning negative number string
> representation conversion, since it eats the whole string (instead of
> stopping on the minus "-" character) and returns ULONG_MAX (and sets "errno"
> to ERANGE). The MSVC7 implementation does not check for these overflow
> cases, and when converting a negative number string to unsigned long,
> std::stringstream therefore returns ULONG_MAX and remain in a valid state.
>
> Is this the expected behaviour or an implementation flaw ?
> In the first case, how can you tell an overflow from a successful conversion
> of the ULONG_MAX string representation ?
1) A minus sign is perfectly valid in an unsigned long field. Negating
an unsigned value is well defined in C/C++.
2) (unsigned long)(-1) is equivalent to ULONG_MAX.
3) Since the converted value is properly represented as an unsigned long,
the extraction should succeed and the value should be stored.
4) I dunno about strtoul setting errno in this case, but the V7 code should
set failbit and not store the converted value is errno were set.
5) V7.1 behaves the same, but future versions should be less dependent
on any idiosyncracies of strtoul, and won't rely on errno testing.
Summary: you're getting the right result, but perhaps not for all the
right reasons in this case.
P.J. Plauger
Dinkumware, Ltd.
http://www.dinkumware.com
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
P
|
8/12/2003 10:13:59 AM
|
|
|
2 Replies
600 Views
(page loaded in 0.344 seconds)
|