 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Pete Becker Guest
|
Posted: Sun Jun 05, 2005 10:22 am Post subject: Re: type conversion question,pls help!!! |
|
|
Alf P. Steinbach wrote:
| Quote: | * Pete Becker:
Alf P. Steinbach wrote:
6th don't: don't use C casts, they're unsafe and unpredictable.
They're not unpredictable. It's certainly true that many people don't
know what they'll do, but that's a different problem.
Well, I call that unpredictable. :-)
That's a sad commentary on the skill of the programmers you've been |
exposed to.
--
Pete Becker
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! ]
|
|
| Back to top |
|
 |
kanze@gabi-soft.fr Guest
|
Posted: Mon Jun 06, 2005 11:20 am Post subject: Re: type conversion question,pls help!!! |
|
|
Alf P. Steinbach wrote:
| Quote: | * James Kanze:
* Alf P. Steinbach:
1st don't: don't use Hungarian notation, it's Evil.
I would agree, but we're talking about programming here;
somehow, "Evil" doesn't seem an appropriate word. I'm not
sure about others, but for me, "Evil" connotates not just an
act, but the intention behind it.
See FAQ item 6.15,
url: http://www.parashift.com/c++-faq-lite/big-picture.html#faq-6.15>,
‹What does the FAQ mean by "such and such is evil"?›
|
OK. It wasn't apparent from your posting that you meant it in
this sense (at least to me). In that respect, of course, item
6.16 is also significant.
Taken from this point of view, we're probably mostly in
agreement.
| Quote: | char field[40];
2nd don't: as a newbie, don't use raw arrays, they're unsafe.
That depends on the context. An std::string would certainly
be more appropriate here, but there are contexts where it is
unsafe, and the char[] isn't. (Think of order of
initialization issues.)
Example?
|
extern std::string someText ;
Then using someText in the constructor of a static object.
Of course, "global variables are evil", so this example isn't
very realistic. But anytime something can be used in the
constructor of a static object, it's worth considering order of
construction issues. The simplest way to ensure 100% safety is
to use static initialisation; all static initialization takes
place on program load, before the first constructor is run.
Obviously, there are other ways to solve the problem --
singletons, or putting the declarations in the same translation
unit -- and sometimes, they're a lot easier than forcing
something to have static initialization (which means POD). But
I've found small classes along the lines of:
typedef std::map< std::string, std::complex< double,> >
Map ;
struct MapInit
{
char const* key ;
double real ;
double imag ;
operator Map::value_type() const
{
return Map::value_type(
key,
std::complex< double >( real, imag ) ) ;
}
} ;
to be useful more than once. It's a trade-off. Sometimes,
using an array of such a class is the easier solution; other
times, I'll still use some other technique to ensure order of
initialization. (Even when order of initialization isn't
strictly an issue, but I find writing something like:
MapInit const initTbl[] =
{
{ "first", 1.0, 0.0 },
{ "next" , 0.0, 0.1 },
// ...
} ;
more intuitive than the alternatives. But this may be simply
because I'm so used to it.)
Note that a large part of my reaction, however, was due to my
misunderstanding the tone of your posting. The general advice
of not using raw arrays is good, especially for a newbie.
| Quote: | Regretfully, even a newbie is confronted all too soon with
these types of problems.
memset(field,0,40);
3rd don't: don't use raw pointers, they're unsafe.
Nonsense. I use them all the time. In the absense of garbage
collection, any C++ program will generally use a mixture of
pointer types. Including raw pointers. (I find that well
over half of my pointers are raw pointers.)
You're an old hand at C++ and presumably know how to handle
sharp pointers without getting hurt.
See FAQ items 17.4,
url: http://www.parashift.com/c++-faq-lite/exceptions.html#faq-17.4>,
‹How should I handle resources if my constructors may throw exceptions?›
and item 23.4,
url:
http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.4
‹Okay, but is there a way to simulate that behavior as if dynamic binding
worked on the this object within my base class's constructor?›
|
I'm not saying that you should only use raw pointers. In
practice, I've found that no one pointer Type really covers all
use; you need several different types of pointers. And most of
the time, one of those types will have a semantic so close to
that of a raw pointer that there's really no point of dressing
it up.
Thinking about it, this may partially depend on my applications.
Mostly large scale servers, which ensure transactional integrity
for individual requests. In such applications, the pointers to
the objects you are updating belong to the transaction manager,
by definition, or to the data contains, and there's really no
point in using anything other than raw pointers for these
objects outside of the transaction manager. I still pretty much
use smart pointers for most other things.
| Quote: | 4rth don't: don't use 'memset'; it's unsafe.
Why? It's a low level function, for doing low level things,
so a beginner probably won't need it very often. But it
does what it does correctly, and when what it does is what
you want, you should use it.
Since the OP uses 'memset' where it's unnecessary, it's IMHO
not a good idea to counter my advice here.
|
Not so much countering it, but pointing out that it was
incomplete. The reason not to use memset here is not that it is
unsafe, but that it is totally unnecessary.
| Quote: | If you want to criticize the code in detail, you might point
out that the memset is totally unnecessary here.
I did: my alternative C++ code didn't use it.
strcpy(field,"8.030");
5th don't: don't use 'strcpy'; it's unsafe. This
particular use of it is probably safe (I never recall the
argument order and won't check it). But the code may not
hold up when it's maintained.
Why not?
Because it's a main cause of buffer overflow, which not only
can screw up the program but also can be a severe security
risk.
Instead of 'strcpy', use 'strncpy' if it's absolutely
necessary, but better, use a safe string class such as
'std::string'.
Now I remember you once used as an example, in a discussion
with me about the (non-) virtues of modern C++ streams, that
you once did a floating point sprintf that either did or could
have resulted in a severe buffer overflow, the point being
that even char[40] would not necessarily be enough when one
didn't specify the field. Of course I have, too, done things
like that, and so have we all. And I find that inconsistent
with the question above: is it just argumentative?
|
A bit argumentative, I'll admit. As I said, I mistook the tone
of your posting, because I missed the humourous intent of
"evil".
In practice, I used strcpy a lot, back in my C days. I'm not
sure if the reasoning makes sense, but I somehow feel that it
fits in with char[]. And I can't explain why it doesn't bother
me, where as sprintf does; I guess I'm not perfectly rational
either. (On the other hand, strncpy isn't a panacea either; it
doesn't necessarily append a final ' ', which can lead to
difficult to resolve problems later on.)
Anyway, like most people of my generation, my very first class
was String, and I've not used strcpy since I got it working.
(Today, of course, I use std::string, rather than my own home
brew.) While I think that the order of initialization issue may
sometimes lead to preferring a char[] to a std::string, and I'm
not even afraid of using char const* as a parameter, when I know
that all reasonable uses will involve a string literal, I'll
have converted to std::string long before I'll need a strcpy.
| Quote: | I agree that he should be using std::string. But if you're
not using std::string, for whatever reasons, strcpy is
unavoidable.
Nope, see above.
As for the order of the arguments, that's potentially a
problem with any function -- I always have to look up the
functions in the STL for it, for example. Strcpy happens to
use a natural order (corresponding to that of operator =),
so it is easy to remember.
I guess the argument order is forced by one being 'const', but
I haven't checked lately: it's much easier to Just Say No.
|
But it's not, in itself, an argument against strcpy, any more
than it is an argument against std::copy. Maybe even a little
less: <string.h> is far more consistent in this regard than is
<algorithm>, which sometimes puts the target first (generate or
fill), sometimes last (copy), and sometimes in the middle
(transform).
| Quote: | dwLastClose = (unsigned long)(1000 * strtod(field,(char * *)0));
6th don't: don't use C casts, they're unsafe and unpredictable.
They're quite predictable, in the sense that it is well
defined what they do.
They're unpredictable in most other senses.
Not only it is hard to see which combination of named casts
are invoked, but a C cast may invoke casting that has no
corresponding named cast combination, _and_ it may continue to
compile, and do a completely unintended cast, when the types
involved are changed during maintenance.
In short, they're both unsafe and unpredictable.
|
The arguments against the C style casts only apply to pointers
and references. I have no compunction about writing functional
style casts; I think it is even the most common practice when
converting to a class type -- do you write "MyClass( i )", or
"static_cast< MyClass >( i )"? Use the name of a built-in type
which consists of several words, and you're obliged to put it
into parentheses. And bang! you have a C style cast.
The results of any of the casts (supposing the cast is not to a
reference type) is a new object, but in practice, when dealing
with pointer casts, one tends to think in terms of the object
being pointed to (which is always the same object). When I'm
logically thinking in terms of the object being pointed to, I
rigorously use the new style casts. When I'm really thinking in
terms of a new object (e.g. an int, or a MyClass), I'm not
really consistent, but I tend to favor C style or functional
casts (which look fairly similar); partially, of course, because
if I need more than one parameter to construct the new object,
functional casts are the only legal means, and why do it
differently when I only need a single parameter. As I said,
when dealing with a built-in type like unsigned long, functional
casts aren't legal. But a C style cast looks enough like one to
satisfy me.
| Quote: | 7th don't: don't use unnecessary casts.
I'm not sure about your point here. It's better to be
explicit; if you know that there is a type conversion
involved, then why hide the fact from the user.
I didn't say one should try to hide type conversions.
Just avoid unnecessary casts.
They clutter up the code, and they mean that harmful changes
during maintainance may go unnoticed (compile cleanly).
|
But the question remains: which casts are "unnecessary"? C++
has a lot (far too many, IMHO) of implicit type conversions. If
I'm assigning a double to a char, the cast is unnecessary, as
far as the compiler is concerned. But I'm sure going to put it
in there, if only so that whoever reads the code realizes that
the conversion is intentional, and not a careless error.
This is actually a serious question. I don't specify all
conversions, either. But now that I think of it, I'm not sure
that I could actually explain the rules I use for deciding. In
this particular case, given that the cast is "lossy", and that
the "conversion" is really what the entire line is about, I'd
definitely write it out intentionally. Maybe even with a new
style cast, to be sure that the reader sees it. At the other
extreme, I definitly wouldn't spell out the conversion involved
when initializationg a short with the constant 0 (which has type
int).
| Quote: | i don't know why dwLastClose i got is "8029"
Assuming that the code "works", it's due probably to the
granularity of floating point values, i.e. that on your
implementation there's no floating point value that's
exactly 8.03; read up on floating point arithemetic in
your textbook.
The main problem with this code is however the presence of
seven dont's; it's really bad code when regarded as C++
code.
Not really. It's not particularly idiomatic, but other than
that, I don't see any problem other than the unnecessary use
of memset. I'd encourage the author to use std::string, but
I wouldn't consider that the "main problem", nor is the code
necessarily "bad", per se.
Well, to each his own medicine: if you think C-style is less
error-prone (especially for a newbie) than C++ style, who am I
to dispute your claims?
|
It's not really a question of which is less error-prone. I
definitly prefer C++ to C. But in this case: the poster had a
specific problem, which has nothing to do with C-style or
C++-style. C-style is also a coherent style; like you, I find
that C++ style leads to more maintainable code, but I've also
written enough C in my day to know that you can write robust
code in it as well. There's nothing wrong with pointing out the
C++ has better tools, or that there are more idiomatic ways of
doing this in C++, but without knowing the entire context, I
don't agree with the idea of insisting that the C-style is a
"don't", without answering his original question.
While I doubt that this is the case of the original poster,
there are, in fact, contexts where std::string cannot be used --
embedded systems which don't allow dynamic allocation, for
example. It's a question of tone: suggesting that there may be
better solutions is one thing; saying that the main problem with
the code is that it doesn't correspond to the coding guidelines
where you work is another (unless, of course, the poster is
working for you); neither you nor I really know for sure what
his environment is, and while we can guess that he would
probably be better off using std::string, we can't be sure that
there aren't some overpowering reasons not mentionned in his
post that means that char[] is (exceptionally) the best
solution.
I don't want to pick on you about it, particularly since I
missed part of the tone of your posting, in misunderstanding
your use of the word evil, but I find that there is often an
arrogance in some of the postings here that ticks me off. There
is a great difference between politely pointing out that there
are useful features which the poster may have missed, or that
the language has changed, and that what he is doing may be
somewhat outdated, and treating such things as the main problem,
or as "errors", when they obviously have nothing to do with the
problem he is asking about. I don't see any use in jumping all
over someone and treating him as an idiot just because he's used
<iostream.h> or "void main". You've posted enough otherwise
that I know it's not the case for you, but it sometimes gives
the impression that the person is posting just to show off; it's
a simple criticism, and doesn't require actually understanding
the issues the original poster is asking about.
| Quote: | Thus, if it's part of some program and that program is in
the same style, the program probably doesn't work.
Nonsense. I've written lots of programs like that, for
critical applications (like locomotive brake systems, or
telephone standards). You can't say a program "probably
doesn't work" just because you don't like its style. (What
you can say, of course, is that there will probably be less
problems, and it will be easier to make the code work, if he
uses std::string most of the time.)
If you rewrite the (assumed) program to use C++ standard
library types it's possible that it will work;
Are you kidding? In this case, rewriting it to use the
standard C++ library types will change absolutely nothing
with regards to the output of the program.
Are you kdding? You don't know anything about that program
other than that is, by hypothesis, written in the same
low-level C style.
|
The specific problem with his program was due to a problem of
rounding, and the finite representation of double. That's one
thing that is exactly the same in C and in C++.
There were no other errors in the code he posted. One might not
approve of the style, but the code was correct.
| Quote: | I suggest "Accelerated C++" to learn about that way of
programming.
Here's an example of using C++ instead of C:
std::string const field = "8.030";
unsigned long const lastClose = static_cast<unsigned long>(
1000*std::strtod( field.c_str(), 0 )
);
That's not very idiomatic C++. And IMHO, It's pretty bad
style to put a constant string in an std::string, just to
call c_str() on the string. Constant strings which
interface to C library functions are one case where the
classical char[] is probably more appropriate.
I'm not amused.
|
I wasn't being funny. In my own code, I don't normally create
std::string's from string literals just to be able to call
c_str() on them.
| Quote: | But of course, idiomatic C++ would use an istringstream,
rather than strtod. A bit more complicated, but much more
control and easier error handling.
(If we consider that in real life, his string probably
isn't a constant, but comes from some external source, then
the lack of error handling is a serious error in the code.
And of course, the C++ idioms, std::string and
std::istringstream, do make error handling a lot simpler.)
Have you forgotten that you yourself pointed out that
istringstream is Undefined Behavior in cases like this,
because it relies on sscanf?
Or am I confusing you with someone else?
|
Maybe. I don't think that there is any undefined behavior
concerning the conversion of "8.030" by istringstream. More
generally, it is the "idiomatic" way of converting from strings
in C++, regardless of its weaknesses.
| Quote: | To refresh your (or as it may happen, my own) memory: it was
in a thread about failed istringstream-based conversion from
hex user input.
|
I'm afraid I don't remember it.
There are a number of issues involved, and depending on the
case, I may use stdtol, or even write my own conversion
routines, because the error handling in istream (or FILE*)
doesn't correspond to what I need. The point is: the idiomatic
general solution uses istringstream.
| Quote: | And here's an example of rounding that floating point value:
std::string const field = "8.030";
unsigned long const lastClose = static_cast<unsigned long>(
0.5 + 1000*std::strtod( field.c_str(), 0 )
);
Regretfully, the example is not correct. Think of negative
values. And overflow.
Have you forgotten the context here? I won't use harsher
words. I'm a nice person.
|
The context is that you have seriously slammed his code right
and left, considering almost every line to violate a major
don't. Given that, you'd better make sure that your code is
perfect, because I'm not going to go easy on it. (As I said, I
obviously misinterpreted the tone of your posting. But I read
arrogance, and arrogance is something that ticks me off. None
of us are perfect.)
Having said that, I think that the issue of negative values is
an important one. Overflow probably less, although in robust
code, I'd check for it too.
| Quote: | Again, I am supposing that the use of a constant string is
just for the example -- if the value is really constant, of
course, the simplest way to write the code is:
unsigned long x = 8030 ;
Without any string at all.
Actually since there's no output or stated effect on other code,
the simplest implementation is
i.e. nothing at all.
James, James...
|
.
(Anyway, I'm glad you're taking my comments in stride. I was
kind of rough on you. Much more than was due, due to my
misinterpreting your tone.)
| Quote: | To be more safe you could use the Boost library's
'lexical_cast' (I think it was called) instead of the
built-in 'static_cast'.
They don't do the same thing, and you can't use the
lexical_cast where the static_cast is used here, the same as
you can't use the static_cast where you might use the
lexical_cast (as a remplacement for stdtod).
You're right here. I indicated uncertainty about what the
name of the Boost range checking cast notation was by using
the phrase "(I think it was called)". I'm sorry that wasn't
clear enough.
To make up for that:
#include <boost/numeric/conversion/converter.hpp
#include
#include
template< typename Result, typename Arg
Result convert( Arg a )
{
return boost::numeric::converter
}
int main()
{
std::string const field = "8.030";
unsigned long const lastClose = convert<unsigned long>(
0.5 + 1000*std::strtod( field.c_str(), 0 )
);
std::cout << lastClose << std::endl;
}
It wouldn't surprise me if there's already a utility function
like 'convert', but it's faster to just write it than to
search for it.
A potential problem with lexical_cast here is error
handling. You have more options if you use istringstream
directly. On the other hand, if you've pre-vetted the
string (using, say, boost::regex), this isn't a problem.
Anyhow, a "correct" C++ solution would probably look
something like:
std::string field = "8.030" ; // But could be anything.
std::istringstream s( field ) ;
double d ;
s >> d >> std::ws ;
if ( ! s || s.get() != EOF ) {
// Error handling...
} else {
d = round( d ) ; // Supposing the
// presence of a C99 library.
if ( d > std::numeric_limits< unsigned long >::max()
|| d < 0 ) {
// More error handling...
} else {
unsigned long result = static_cast< unsigned long >( d ) ;
}
}
*A* correct C++ solution, of course ... Other solutions are
also possible.
Uhm, well, it's UB if the string isn't a valid spec...
|
The only case of undefined behavior would be overflow, I think.
And a quality implementation will handle this correctly. (For
the life of me, I can't think of why the standard doesn't
require it, given that it requires it for strtod.)
Ignoring, of course, the fact that the program may exceed the
resource limits of the implementation (which is also undefined
beahavior).
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Alf P. Steinbach Guest
|
Posted: Mon Jun 06, 2005 7:51 pm Post subject: Re: type conversion question,pls help!!! |
|
|
* James Kanze:
| Quote: | * Alf P. Steinbach:
* James Kanze:
* Alf P. Steinbach:
* [email]lfeiman888 (AT) gmail (DOT) com[/email]:
|
[snip]
| Quote: | char field[40];
2nd don't: as a newbie, don't use raw arrays, they're unsafe.
That depends on the context. An std::string would certainly
be more appropriate here, but there are contexts where it is
unsafe, and the char[] isn't. (Think of order of
initialization issues.)
Example?
extern std::string someText ;
Then using someText in the constructor of a static object.
|
I don't see what you mean. Is there an example where a raw character
array is safe and a std::string isn't? Ignoring memory exhaustion.
[snip]
| Quote: | The reason not to use memset here is not that it is
unsafe, but that it is totally unnecessary.
|
I think "legitimate" uses of 'memset' are so few and far between
that they don't contradict not using 'memset' as a guideline/newbie law.
For initialization we have C++ initialization.
For zeroing and copying we have standard library functions.
[snip]
| Quote: | I guess the argument order is forced by one being 'const', but
I haven't checked lately: it's much easier to Just Say No. ;-)
But it's not, in itself, an argument against strcpy, any more
than it is an argument against std::copy.
|
Right. I just mentioned why I didn't bother to check whether the
OP's code was correct in that regard. Anyway, 'strncpy'.
[snip]
| Quote: | The arguments against the C style casts only apply to pointers
and references.
|
Now I've snipped so much that I'm not sure whether the above
quote is out of context.
Consider
typedef ... Integer;
void foo( double x )
{
Integer i = (Integer)x;
}
Then the argument type is changed to double*, the function body is not
updated accordingly, and the code may still compile with the implied
static_cast changed to an implied reinterpret_cast.
"Only" pointers and references is enough... ;-)
[snip}
| Quote: | But the question remains: which casts are "unnecessary"?
|
As I see it, (char**)0 instead of NULL or 0, just clutters the code.
In other cases it's more of a judgement call, IMO.
[snip]
| Quote: | It's not really a question of which is less error-prone. I
definitly prefer C++ to C. But in this case: the poster had a
specific problem, which has nothing to do with C-style or
C++-style.
|
On the contrary, I believe all that clutter gave the problem
a place to live.
Since the OP posted all that code I think we can be reasonably
sure that where the problem was had not been narrowed down.
Instead, there was probably some suspicion that it might have to
do with the posted (irrelevant) C-style code, and I think it would
not have been so with C++ style code which is more trustworthy in
not modifying things unexpectedly.
[snip]
[about arrogance in connection with my advice "as a newbie, don't
use raw arrays, they're unsafe"]
| Quote: | I don't see any use in jumping all over someone and treating
him as an idiot just because he's used
iostream.h> or "void main". You've posted enough otherwise
that I know it's not the case for you, but it sometimes gives
the impression that the person is posting just to show off; it's
a simple criticism, and doesn't require actually understanding
the issues the original poster is asking about.
|
I think we agree there. I was merely trying to give advice tuned
to the needs of the OP. And although not the case here, sometimes
<iostream.h> or "void main" can give strong hints about what's
needed first of all, such as, "avoid the In 24 Hours book"; as I
recall it's been argued by a committee member that we really should
keep the 'int' requirement for 'main' for just that reason... ;-)
[snip]
| Quote: | Thus, if it's part of some program and that program
is in the same style, the program probably doesn't work.
If you rewrite the (assumed) program to use C++ standard
library types it's possible that it will work;
Are you kidding? In this case, rewriting it to use the
standard C++ library types will change absolutely nothing
with regards to the output of the program.
Are you kdding? You don't know anything about that program
other than that is, by hypothesis, written in the same
low-level C style.
The specific problem with his program was due to a problem of
rounding, and the finite representation of double.
|
Hm.
[snip]
| Quote: | std::string const field = "8.030";
unsigned long const lastClose = static_cast<unsigned long>(
1000*std::strtod( field.c_str(), 0 )
);
That's not very idiomatic C++. And IMHO, It's pretty bad
style to put a constant string in an std::string, just to
call c_str() on the string. Constant strings which
interface to C library functions are one case where the
classical char[] is probably more appropriate.
I'm not amused.
I wasn't being funny. In my own code, I don't normally create
std::string's from string literals just to be able to call
c_str() on them.
|
Since the name 'field' doesn't convey much about the intended usage we
should IMHO assume that the OP meant to illustrate a case with the string
value in a variable or constant.
Presumably, in your own code as you say, you don't strcpy a literal to a
character array just in order to pass it to strtod.
I think my example code matches the OP's just fine, and that a std::string
is probably a much more reasonable choice than a raw character array for
whatever purpose it's actually used for.
[snip]
| Quote: | The point is: the idiomatic general solution uses istringstream.
|
Well, until the "idiomatic" solution gains Defined Behavior I reserve the
right not to use it... ;-)
The Undefined Behavior stems from reliance on sscanf (§27.6.1.2.2 refers to
§22.2.2.1, which in turn, in §22.2.2.1.2/10 refers to scanf) which is
defined by the C standard in terms of fscanf, for which my copy of a
committee draft says in (C std) §7.19.6.2/10, "if the result of the
conversion cannot be represented in the object, the behavior is undefined".
I.e., Undefined Behavior.
[snip]
| Quote: | And here's an example of rounding that floating point value:
std::string const field = "8.030";
unsigned long const lastClose = static_cast<unsigned long>(
0.5 + 1000*std::strtod( field.c_str(), 0 )
);
Regretfully, the example is not correct. Think of negative
values. And overflow.
Have you forgotten the context here? I won't use harsher
words. I'm a nice person.
The context is that you have seriously slammed his code right
and left, considering almost every line to violate a major
don't. Given that, you'd better make sure that your code is
perfect, because I'm not going to go easy on it.
|
It corresponds almost directly to the OP's code, sharing the same
assumptions.
[snip]
[About an istringstream-based conversion:]
| Quote: | *A* correct C++ solution, of course ... Other solutions are
also possible.
Uhm, well, it's UB if the string isn't a valid spec...
The only case of undefined behavior would be overflow, I think.
|
Yes, and UB is UB.
| Quote: | And a quality implementation will handle this correctly. (For
the life of me, I can't think of why the standard doesn't
require it, given that it requires it for strtod.)
|
Visual C++ 7.x has some problems there with conversion from a hex
specification, as I mentioned.
I haven't checked its behavior for floating point specifications.
But then, whether MSVC is a counter-example depends on one's definition of
Quality Implementation, and on one's view of what Correct Undefined Behavior
is. ;-)
--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
james keogh Guest
|
Posted: Tue Jun 07, 2005 9:58 am Post subject: Re: type conversion question,pls help!!! |
|
|
| Quote: | Having said that, I think that the issue of negative values is
an important one. Overflow probably less, although in robust
code, I'd check for it too.
|
i don't quite see why 'rounding upward' rather than, i assume 'rounding
outward' is wrong. It is probably slightly unusual here, but there are
plenty of cases where rounding away from zero would be considered worse
than rounding upward.
the overflow danger is clearly a problem however
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
kanze@gabi-soft.fr Guest
|
Posted: Tue Jun 07, 2005 1:15 pm Post subject: Re: type conversion question,pls help!!! |
|
|
Alf P. Steinbach wrote:
| Quote: | * James Kanze:
* Alf P. Steinbach:
* James Kanze:
* Alf P. Steinbach:
* [email]lfeiman888 (AT) gmail (DOT) com[/email]:
[snip]
char field[40];
2nd don't: as a newbie, don't use raw arrays, they're
unsafe.
That depends on the context. An std::string would
certainly be more appropriate here, but there are
contexts where it is unsafe, and the char[] isn't.
(Think of order of initialization issues.)
Example?
extern std::string someText ;
Then using someText in the constructor of a static object.
I don't see what you mean. Is there an example where a raw
character array is safe and a std::string isn't? Ignoring
memory exhaustion.
|
Sure:
convert.h:
std::string asString( unsigned )
convert.cc :
namespace {
std::string const digits( "0123456789" ) ;
}
std::string
asString( unsigned i )
{
std::string result ;
do {
result += digits[ i % 10 ] ;
i /= 10 ;
} while ( i != 0 )
reverse( result.begin(), result.end() ) ;
return result ;
}
file2.cc :
#include "convert.h"
struct C
{
C( unsigned i )
: value( asString( i )
{
}
std::string value ;
} ;
C aC( i ) ;
As written, the above has undefined behavior. It is undefined
whether the variable aC in file2.cc or the variable digits in
file1 will be constructed first. And if aC is constructed
first, the function asString, called from the constructor of aC,
will access the string digits before it is constructed.
Change the declaration of digits to:
char const digits[] = "0123456789" ;
and there is no problem.
There are obviously other ways of solving the problem, a
singleton, for example. But in this particular case, I still
often find char[] to be the simplest solution. (In this
particular case, I probably wouldn't use a variable at all, and
would just write "0123456789"[ i % 10 ] in the loop. But I'm an
fuddy old C programmer, and some younger upstarts have claimed
that that isn't the most transparent way of writing the
code:-).)
| Quote: | [snip]
The reason not to use memset here is not that it is
unsafe, but that it is totally unnecessary.
I think "legitimate" uses of 'memset' are so few and far
between that they don't contradict not using 'memset' as a
guideline/newbie law.
|
Oh, I don't recommend its use for newbies. I agree that its
uses are few and far between. (And I really only attacked
because I misinterpreted the tone of your original posting.)
But the real reason not to use memset isn't that it is unsafe,
per se; the real reason is that we rarely want its semantics.
Because its semantics are extremely low level: set all bits in
each byte to a specific pattern; most of the time, we have a
table of type T, which we want to initialize, and not a block of
raw bytes.
(I've used it in the past in a debugging operator new and
delete, for example. To zap the memory, in both cases, in order
to increase the chances of an uninitialized read or a use after
delete causing an immediate failure.)
| Quote: | For initialization we have C++ initialization.
|
And std::fill and std::fill_n, for e.g. a double[]. Agreed.
| Quote: | For zeroing and copying we have standard library functions.
|
It depends on what you want to do. If you want to zap raw
memory, memset is still the function I would choose.
But I agree that newbies shouldn't be concerned about raw
memory.
| Quote: | [snip]
I guess the argument order is forced by one being 'const',
but I haven't checked lately: it's much easier to Just Say
No. ;-)
But it's not, in itself, an argument against strcpy, any
more than it is an argument against std::copy.
Right. I just mentioned why I didn't bother to check whether
the OP's code was correct in that regard. Anyway, 'strncpy'.
|
I sort of disagree. I understand your argument for strncpy, but
the fact that it doesn't always append a ' ' makes it just as
error prone, if not more, than strcpy. With the difference
that, at least in the C world, everyone knows strcpy, and the
precautions which must be taken, where as strncpy is less widely
used, and therefore, it is more likely that your average
programmer forget the possible missing ' '.
| Quote: | [snip]
The arguments against the C style casts only apply to pointers
and references.
Now I've snipped so much that I'm not sure whether the above
quote is out of context.
|
The context was a cast to unsigned long.
| Quote: | Consider
typedef ... Integer;
void foo( double x )
{
Integer i = (Integer)x;
}
Then the argument type is changed to double*, the function
body is not updated accordingly, and the code may still
compile with the implied static_cast changed to an implied
reinterpret_cast.
|
If you change an int in pointer, you're going to have to change
a lot of things. Not just the cast. In practice, you've
created a new function.
Anyhow, it is a question of style. I'm not 100% consistent
myself, but if I had to choose one, I'd normally use functional
style casts for what I consider "creating new objects", and C
style casts when the functional style cast doesn't work (or
leads to ambiguities between initializing an object and
declaring a function).
| Quote: | "Only" pointers and references is enough... ;-)
[snip}
But the question remains: which casts are "unnecessary"?
As I see it, (char**)0 instead of NULL or 0, just clutters the
code.
|
OK. I can agree there. With the exception of passing it as a
vararg, of course. Or when I want exact control of overload
resolution or template instantiation. (NULL or 0 will call
f(int), and instatiate a template function on int.) Or...
| Quote: | In other cases it's more of a judgement call, IMO.
|
Largely. I tend to insist on an explicit conversion for lossy
conversions, but beyond that, I tend to prefer code which
doesn't allow the implicit conversions to begin with. And of
course, there are cases such as proxies, where using an explicit
cast more or less defeats the purpose of the implicit
conversion.
| Quote: | [snip]
It's not really a question of which is less error-prone. I
definitly prefer C++ to C. But in this case: the poster had a
specific problem, which has nothing to do with C-style or
C++-style.
On the contrary, I believe all that clutter gave the problem a
place to live.
Since the OP posted all that code I think we can be reasonably
sure that where the problem was had not been narrowed down.
Instead, there was probably some suspicion that it might have
to do with the posted (irrelevant) C-style code, and I think
it would not have been so with C++ style code which is more
trustworthy in not modifying things unexpectedly.
|
I'm not sure what the OP might have thought. But from the
symptoms, the problem was clear: he didn't understand the
implications of the granularity of floating point arithmetic.
And that is totally independant of C style code or C++ style
code. Translate what he wrote into idiomatic C++, and the
problem is still there. Neither more or less apparent.
| Quote: |
[snip]
[about arrogance in connection with my advice "as a newbie,
don't use raw arrays, they're unsafe"]
I don't see any use in jumping all over someone and treating
him as an idiot just because he's used <iostream.h> or "void
main". You've posted enough otherwise that I know it's not
the case for you, but it sometimes gives the impression that
the person is posting just to show off; it's a simple
criticism, and doesn't require actually understanding the
issues the original poster is asking about.
I think we agree there. I was merely trying to give advice
tuned to the needs of the OP. And although not the case here,
sometimes <iostream.h> or "void main" can give strong hints
about what's needed first of all, such as, "avoid the In 24
Hours book"; as I recall it's been argued by a committee
member that we really should keep the 'int' requirement for
'main' for just that reason...
|
If the posting is obvoiusly from a newbie, mentionning that his
information may be dated, in the case of <iostream.h>, is
definitly in order. Judging that the book he learned from isn't
any good, maybe not. The book I learned C++ from used
<iotream.h>. Written by a guy called Stroustrup -- I'm given to
understand that he really does know something about C++.
The void main issue is a bit more complex, since void main has
never been legal. Still, it's not worth making a big deal
about, either. Although its use to judge a book may be somewhat
justified; if that was the only problem with the book, I'd say,
who cares. But typically, it's a symptom for a much greater
problem.
| Quote: | [snip]
The point is: the idiomatic general solution uses
istringstream.
Well, until the "idiomatic" solution gains Defined Behavior I
reserve the right not to use it... ;-)
The Undefined Behavior stems from reliance on sscanf
(§27.6.1.2.2 refers to §22.2.2.1, which in turn, in
§22.2.2.1.2/10 refers to scanf) which is defined by the C
standard in terms of fscanf, for which my copy of a committee
draft says in (C std) §7.19.6.2/10, "if the result of the
conversion cannot be represented in the object, the behavior
is undefined".
I.e., Undefined Behavior.
|
I know. On the other hand, what do actual implementations do?
There is also a statement that exceeding resource limits is
undefined behavoir. How do you avoid that one?
(I'm not saying that you're totally wrong. I can imagine cases
where I would avoid it too, because of the undefined behavoir.
But there are a lot of cases where I don't need portability to
some arbitrary, possibly non-existant implementation, and the
behavior of the usual implementations is acceptable.)
(There's also an interesting point concerning floating point in
the exact words. The value in question is "8.030", and that
exact value is not representable in a double, at least on
machines using IEEE floating point. Somehow, however, I don't
think that it was the intent of the committes that this be
undefined behavior. It might be worth raising the point in the
standards groups, however.)
| Quote: | [snip]
And here's an example of rounding that floating point
value:
std::string const field = "8.030";
unsigned long const lastClose = static_cast<unsigned long>(
0.5 + 1000*std::strtod( field.c_str(), 0 )
);
Regretfully, the example is not correct. Think of negative
values. And overflow.
Have you forgotten the context here? I won't use harsher
words. I'm a nice person.
The context is that you have seriously slammed his code
right and left, considering almost every line to violate a
major don't. Given that, you'd better make sure that your
code is perfect, because I'm not going to go easy on it.
It corresponds almost directly to the OP's code, sharing the
same assumptions.
|
The problem is, we don't know that the assumptions of the OP's
code were. As written, his code truncated to zero. If
"classical" rounding was wanted, you have to move 0.5 away from
zero. If truncation was wanted (quite possibly the case), but
also "intuitive" behavior (whatever that means), adding some
much smaller fudge factor might be the correct assumption. But
I can't quite see the grounds for systematically adding 0.5; I
can't think of anything useful that that would do with a
negative value.
(Globally, of course, I think that the correct solution would be
to use the round() function found in <cmath>. Supposing an up
to date compiler, of course.)
| Quote: | [snip]
[About an istringstream-based conversion:]
*A* correct C++ solution, of course ... Other solutions are
also possible.
Uhm, well, it's UB if the string isn't a valid spec...
The only case of undefined behavior would be overflow, I
think.
Yes, and UB is UB.
And a quality implementation will handle this correctly.
(For the life of me, I can't think of why the standard
doesn't require it, given that it requires it for strtod.)
Visual C++ 7.x has some problems there with conversion from a
hex specification, as I mentioned.
I haven't checked its behavior for floating point
specifications.
But then, whether MSVC is a counter-example depends on one's
definition of Quality Implementation, and on one's view of
what Correct Undefined Behavior is.
|
Globally, I've been favorably impressed with the quality of VC++
(compared to that of other MS products I've used in the past, of
course), and particularly of their library. That doesn't mean
that it can't have a bug; regretfully, no implementation is safe
from that. Did they accept the problem as a bug, or claim that
it was correct behavior, simply because the standard allowed it?
In the case of double, of course, there is no reasonable way
that you can check for overflow before converting. Which IMHO
pretty much means that from a quality of implementation point of
view, the implementation must check. Regretfully, because of
the silence of the standard, it's not too clear what they should
do if it occurs. So you may be right: at least with strtod, you
know what happens. (In really robust code, of course, you would
set errno to zero before calling strtod, and check it after, in
order to distinguish between overflow and someone actually
inputting the value HUGE_VAL. But most of the time, that would
be overkill.)
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
kanze@gabi-soft.fr Guest
|
Posted: Tue Jun 07, 2005 1:22 pm Post subject: Re: type conversion question,pls help!!! |
|
|
james keogh wrote:
| Quote: | Having said that, I think that the issue of negative values
is an important one. Overflow probably less, although in
robust code, I'd check for it too.
i don't quite see why 'rounding upward' rather than, i assume
'rounding outward' is wrong. It is probably slightly unusual
here, but there are plenty of cases where rounding away from
zero would be considered worse than rounding upward.
|
Given the initial code, I'm not sure that the OP wanted any
rounding. He started with 8.03, and multiplied by 1000; from
the overall tone of his posting, I think that he expected to
have an exact integral value, and no rounding.
Assigning to an integral type truncates. Or rounds to zero, if
you prefer. If we assume that the initial value is fairly close
to an integer (not something like 3.5), then moving a little bit
away from zero, then truncating, will "eliminate" (or "mask",
depending on your point of view) the "error"; a priori, in the
absense of other information, I presume that what was wanted was
rounding to nearest, and that he didn't care about the case
where the value was exactly between. The most efficient way of
doing this depends on the procesor, but presumably, will be what
is actually done in the standard function round(). Failing
that, the simplest way to simulate the functionality is to add
or subtract 0.5, depending on the sign of the value.
| Quote: | the overflow danger is clearly a problem however
|
Yes. You definitly want to check the value of the floating
point before trying to assign it to an integral type.
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Gabriel Dos Reis Guest
|
Posted: Tue Jun 07, 2005 10:50 pm Post subject: Re: type conversion question,pls help!!! |
|
|
[email]kanze (AT) gabi-soft (DOT) fr[/email] writes:
[...]
| Quote: | I think we agree there. I was merely trying to give advice
tuned to the needs of the OP. And although not the case here,
sometimes <iostream.h> or "void main" can give strong hints
about what's needed first of all, such as, "avoid the In 24
Hours book"; as I recall it's been argued by a committee
member that we really should keep the 'int' requirement for
'main' for just that reason... ;-)
If the posting is obvoiusly from a newbie, mentionning that his
information may be dated, in the case of <iostream.h>, is
definitly in order. Judging that the book he learned from isn't
any good, maybe not. The book I learned C++ from used
iotream.h>.
|
and that was ages ago and I suspect that there are some constructs in
the book that are no longer valid in C++98, for C++ evolved since
then. Right?
| Quote: | Written by a guy called Stroustrup -- I'm given to
understand that he really does know something about C++.
|
--
Gabriel Dos Reis
[email]gdr (AT) integrable-solutions (DOT) net[/email]
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Alf P. Steinbach Guest
|
Posted: Tue Jun 07, 2005 10:52 pm Post subject: Re: type conversion question,pls help!!! |
|
|
* James Kanze:
| Quote: | * Alf P. Steinbach:
I don't see what you mean. Is there an example where a raw
character array is safe and a std::string isn't? Ignoring
memory exhaustion.
Sure:
convert.h:
std::string asString( unsigned )
convert.cc :
namespace {
std::string const digits( "0123456789" ) ;
}
std::string
asString( unsigned i )
{
std::string result ;
do {
result += digits[ i % 10 ] ;
i /= 10 ;
} while ( i != 0 )
reverse( result.begin(), result.end() ) ;
return result ;
}
file2.cc :
#include "convert.h"
struct C
{
C( unsigned i )
: value( asString( i )
{
}
std::string value ;
} ;
C aC( i ) ;
As written, the above has undefined behavior. It is undefined
whether the variable aC in file2.cc or the variable digits in
file1 will be constructed first. And if aC is constructed
first, the function asString, called from the constructor of aC,
will access the string digits before it is constructed.
|
Ah. Yes. I'd forgotten.
*Noting additional argument for not using globals*
*Noting they forgot to fix that "after the _first statement_ of 'main'".*
[snip]
| Quote: | (In this
particular case, I probably wouldn't use a variable at all, and
would just write "0123456789"[ i % 10 ] in the loop. But I'm an
fuddy old C programmer, and some younger upstarts have claimed
that that isn't the most transparent way of writing the
code:-).)
|
Bah, ignore those upstarts, it's obviously bestest.
[snip]
| Quote: | Anyway, 'strncpy'.
I sort of disagree. I understand your argument for strncpy, but
the fact that it doesn't always append a ' ' makes it just as
error prone, if not more, than strcpy. With the difference
that, at least in the C world, everyone knows strcpy, and the
precautions which must be taken, where as strncpy is less widely
used, and therefore, it is more likely that your average
programmer forget the possible missing ' '.
|
The nice thing with 'strnpy' is that you have to wrap it, in general.
[snip]
| Quote: | The arguments against the C style casts only apply to pointers
and references.
Now I've snipped so much that I'm not sure whether the above
quote is out of context.
The context was a cast to unsigned long.
Consider
typedef ... Integer;
void foo( double x )
{
Integer i = (Integer)x;
}
Then the argument type is changed to double*, the function
body is not updated accordingly, and the code may still
compile with the implied static_cast changed to an implied
reinterpret_cast.
If you change an int in pointer, you're going to have to change
a lot of things. Not just the cast. In practice, you've
created a new function.
|
Nope, for at least three reasons.
First, it's rather common that function signatures are changed
during development.
Second, to get a type change it's enough to e.g. change the _name_
of the argument, and voilà, the name in the cast refers to a global.
Third, this problem is not limited to functions.
[snip]
| Quote: | If the posting is obvoiusly from a newbie, mentionning that his
information may be dated, in the case of <iostream.h>, is
definitly in order. Judging that the book he learned from isn't
any good, maybe not. The book I learned C++ from used
iotream.h>. Written by a guy called Stroustrup -- I'm given to
understand that he really does know something about C++.
|
It's _definitely_ in order to tell someone who learns C++ from the 1st
edition of TCPPPL that it's pre-standard, and that treating it as applying
to now standard C++ is incorrect.
I'm sure Bjarne would say the same, only more diplomatically.
See, I'm also good at this appeal-to-authority-with-a-twist stuff. ;-)
[snip]
[About istringstream for input data conversion]
| Quote: | I.e., Undefined Behavior.
I know. On the other hand, what do actual implementations do?
|
Visual C++ 7.1 is inconsistent in its treatment of decimal integer
input versus hexadecimal input: IIRC, with hex it just reads as many digits
as will fit, and then stops reading and reports success.
| Quote: | There is also a statement that exceeding resource limits is
undefined behavoir. How do you avoid that one?
|
You can control resource usage.
You can seldom control the format of data in an istream, except for
interactive input.
Therefore, the former is not a very critical problem, but the latter is.
[snip]
| Quote: | (There's also an interesting point concerning floating point in
the exact words. The value in question is "8.030", and that
exact value is not representable in a double, at least on
machines using IEEE floating point. Somehow, however, I don't
think that it was the intent of the committes that this be
undefined behavior. It might be worth raising the point in the
standards groups, however.)
|
Ouch, this gets worse... I didn't think of that.
[snip]
| Quote: | And here's an example of rounding that floating point
value:
std::string const field = "8.030";
unsigned long const lastClose = static_cast<unsigned long>(
0.5 + 1000*std::strtod( field.c_str(), 0 )
);
The problem is, we don't know that the assumptions of the OP's
code were. As written, his code truncated to zero. If
"classical" rounding was wanted, you have to move 0.5 away from
zero. If truncation was wanted (quite possibly the case), but
also "intuitive" behavior (whatever that means), adding some
much smaller fudge factor might be the correct assumption. But
I can't quite see the grounds for systematically adding 0.5; I
can't think of anything useful that that would do with a
negative value.
|
We know the code assumed a floating point value that could be
successfully casted to 'unsigned long'.
Thus there are no negative values involved.
However, if negative values _were_ involved, systematically rounding
up might be desirable in some situations, e.g. for some given definition
of a remainder function.
[snip]
[About the UB's manifestation in Visual C++ 7.x]
| Quote: | Globally, I've been favorably impressed with the quality of VC++
(compared to that of other MS products I've used in the past, of
course), and particularly of their library. That doesn't mean
that it can't have a bug; regretfully, no implementation is safe
from that. Did they accept the problem as a bug, or claim that
it was correct behavior, simply because the standard allowed it?
|
I haven't reported this problem to the vendor.
| Quote: | In the case of double, of course, there is no reasonable way
that you can check for overflow before converting. Which IMHO
pretty much means that from a quality of implementation point of
view, the implementation must check. Regretfully, because of
the silence of the standard, it's not too clear what they should
do if it occurs. So you may be right: at least with strtod, you
know what happens. (In really robust code, of course, you would
set errno to zero before calling strtod, and check it after, in
order to distinguish between overflow and someone actually
inputting the value HUGE_VAL. But most of the time, that would
be overkill.)
|
--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
kanze@gabi-soft.fr Guest
|
Posted: Wed Jun 08, 2005 12:17 pm Post subject: Re: type conversion question,pls help!!! |
|
|
Alf P. Steinbach wrote:
| Quote: | * James Kanze:
[snip]
If the posting is obvoiusly from a newbie, mentionning that
his information may be dated, in the case of <iostream.h>,
is definitly in order. Judging that the book he learned
from isn't any good, maybe not. The book I learned C++ from
used <iotream.h>. Written by a guy called Stroustrup -- I'm
given to understand that he really does know something about
C++.
It's _definitely_ in order to tell someone who learns C++ from
the 1st edition of TCPPPL that it's pre-standard, and that
treating it as applying to now standard C++ is incorrect.
|
Isn't that what I just said. Say the book is dated (which is
indeniably true). But a book isn't "bad" because it happened to
be written a while back.
There are at least two older books for which I've not found more
recent replacements: the Barton and Nackman, and the Robert
Martin. In the case of Barton and Nackman, the audience is
generally more experienced programmers -- you can say "read it"
without hesitation, since presumably the targetted reader will
be able to sort out the minor details like <iostream.h>, and not
be bothered by them. Robert Martin addresses less experienced
programmers, and as much as I like the book, I'd hesitate
recommending it as a sole text, because the language it teaches
isn't really the C++ we have today -- although a surprisingly
large amount of the code will probably work unchanged, basing
your code on home build container classes, instead of the STL,
is not installing good principles in a beginner.
| Quote: | I'm sure Bjarne would say the same, only more diplomatically.
|
I doubt that he'd say the book was bad. Just dated.
In this case, there's no real problem, because there are more up
to date editions which we can recommend without restrictions.
But what do you recommend if there are no more up to date
editions: Lakos, or Barton and Nackman, for example. (I'd still
consider Barton and Nackman required reading.)
| Quote: | [snip]
[About istringstream for input data conversion]
I.e., Undefined Behavior.
I know. On the other hand, what do actual implementations do?
Visual C++ 7.1 is inconsistent in its treatment of decimal
integer input versus hexadecimal input: IIRC, with hex it just
reads as many digits as will fit, and then stops reading and
reports success.
|
A bug, or intentional (mis-)behavior?
| Quote: | There is also a statement that exceeding resource limits is
undefined behavoir. How do you avoid that one?
You can control resource usage.
|
The resource limits don't have to be specified, and usually
aren't. Typically, in a quality implementation, this is not a
problem, unless you do something stupid (like endless
recursion). Ideally, a quality implementation will give you a
possibility of checking before hand, and not starting the
operation if it requires too many resources, but very few
actually do. (The most obvious resource which will get you into
trouble is the stack. How many implementations have a facility
for 1) determining how much stack you are going to need, given a
specified call sequence, and 2) asking whether it is available?)
My point is simply that some undefined behavior can't be
avoided. You used strtod -- what happens if the stack overflows
when you call it? In practice, we always depend on some
implementation defined undefined behavior; on my systems, I know
that an accidental stack overflow will not destroy the hard
disk, for example, and in anything but the most robust software,
I will simply ignore the possibility (except for avoiding
unnecessarily deep recursion). I'll bet I'm not alone in this,
either. (Of course, if the system must really be robust, I'll
take the necessary steps to ensure that stack overflow doesn't
occur.)
| Quote: | You can seldom control the format of data in an istream,
except for interactive input.
|
Well, with ostringstream, you can control somewhat. In fact, in
simple parsers, my standard procedure is to validate the input
line first using a regular expression matcher.
But in general, I'm afraid I'll have to agree with you. Maybe
I've just been lucky in practice, but the istream parsers that
I've used all do behave reasonably in case of overflow, setting
errno and failbit, so the error can be detected, and the cause
evaluated. IMHO, it's a scandal that the standard doesn't
require this.
| Quote: | Therefore, the former is not a very critical problem, but the
latter is.
|
In both cases, it's a cost/benefits trade-off. How much does it
cost to avoid the problem, and how much does it cost if you
don't. In really critical systems, I've implemented stack
verification (in a system dependant manner, under Sun OS). In a
quick throw-away program, just to test something in the
compiler, I'll use >> to a double, and might not even check the
status of the stream afterwards (but it would have to be 100%
sure a throw-away program for me not to check the status).
Anyway, I do think it good that you reminded us of this
undefined behavior; it's far too easily forgotten. While I
don't think the risk is high enough to justify avoiding using >>
to read numeric values entirely, it is definitely a factor to
take into account when evaluating the technolgies, particularly
if the code is supposed to be portable. The cost (in terms of
risk) of using >> is probably higher than most people (including
myself) think.
| Quote: | [snip]
(There's also an interesting point concerning floating point
in the exact words. The value in question is "8.030", and
that exact value is not representable in a double, at least
on machines using IEEE floating point. Somehow, however, I
don't think that it was the intent of the committes that
this be undefined behavior. It might be worth raising the
point in the standards groups, however.)
Ouch, this gets worse... I didn't think of that.
|
I'm pretty sure it's just a problem in the wording, and that
there is no intent on the part of the committee that this
actually be undefined behavior. On the other hand, from
discussions within the C committee (leading up to C90), it is
intentional that there are no guarantees concerning the
accuracy; while I think we'd agree that the quality isn't very
high, an implementation which converts the string "8.030" into
the binary representation for 8.0 is perfectly conforming.
It's worth noting that stdtod uses more reasonable words: "if
the value is outside the range of representable values..." The
C99 standard also gives a "recommended practice" concerning how
to handle rounding.
Finally, the C/C++ standards aren't the only games in town.
IEEE also has something to say about conversions (although I
don't know all the details), so if a system claims that it uses
IEEE floating point, you get some guarantees from that side.
| Quote: | [snip]
And here's an example of rounding that floating point
value:
std::string const field = "8.030";
unsigned long const lastClose = static_cast<unsigned long>(
0.5 + 1000*std::strtod( field.c_str(), 0 )
);
The problem is, we don't know that the assumptions of the
OP's code were. As written, his code truncated to zero. If
"classical" rounding was wanted, you have to move 0.5 away
from zero. If truncation was wanted (quite possibly the
case), but also "intuitive" behavior (whatever that means),
adding some much smaller fudge factor might be the correct
assumption. But I can't quite see the grounds for
systematically adding 0.5; I can't think of anything useful
that that would do with a negative value.
We know the code assumed a floating point value that could be
successfully casted to 'unsigned long'.
Thus there are no negative values involved.
|
Good point. I missed the "unsigned" aspect of the problem.
| Quote: | However, if negative values _were_ involved, systematically
rounding up might be desirable in some situations, e.g. for
some given definition of a remainder function.
|
Maybe, but round to nearest is more likely, I think.
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
kanze@gabi-soft.fr Guest
|
Posted: Wed Jun 08, 2005 12:18 pm Post subject: Re: type conversion question,pls help!!! |
|
|
Gabriel Dos Reis wrote:
| Quote: | kanze (AT) gabi-soft (DOT) fr writes:
[...]
| > I think we agree there. I was merely trying to give
| > advice tuned to the needs of the OP. And although not the
| > case here, sometimes <iostream.h> or "void main" can give
| > strong hints about what's needed first of all, such as,
| > "avoid the In 24 Hours book"; as I recall it's been argued
| > by a committee member that we really should keep the 'int'
| > requirement for 'main' for just that reason... ;-)
| If the posting is obvoiusly from a newbie, mentionning that
| his information may be dated, in the case of <iostream.h>,
| is definitly in order. Judging that the book he learned
| from isn't any good, maybe not. The book I learned C++ from
| used <iotream.h>.
and that was ages ago and I suspect that there are some
constructs in the book that are no longer valid in C++98, for
C++ evolved since then. Right?
|
Sure, and if I saw anyone using it today, I wouldn't hesitate to
warn them that it was dated (as I said above). An excellent
book, but they'd be better off with the later editions.
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
kanze@gabi-soft.fr Guest
|
Posted: Wed Jun 08, 2005 12:18 pm Post subject: Re: type conversion question,pls help!!! |
|
|
Antoun Kanawati wrote:
| Quote: | Francis Glassborow wrote:
sizeof( double ) must be a constant. And double must be a
POD (i.e. memcpy'able). That, along with the requirement to
define such constants as DBL_EPS limit the possibilities
considerably.
I think an implementation could still deal with recurring
decimals by an extension that does not break the
rules. Consider a 64-bit double where only the first 48 bits
were used for the Standard compliant representation. The
remaining 16-bits represent two 8-bit values designating the
first and last place for the recurring section (I could come
up with alternative more efficient representations). From
the Standards view those final 16-bits would be padding, but
that extra information could be used to avoid some loss of
information.
Note that I am not putting this forward as a serious
proposition because the consequential arithmetic
implementation would be horrendous. But I cannot see that
either C or C++ prohibits such a design.
Or, we could have a Rational data type, by using some bits for
a type tag, and the rest two represent two integers.
|
I'm not really sure, but how do you define DBL_EPS in that
case. By definition, supposing only positive values:
if ( e < DBL_ESP )
assert( 1.0 + e == 1.0 ) ;
else
assert( 1.0 + e != 1.0 ) ;
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Alf P. Steinbach Guest
|
Posted: Thu Jun 09, 2005 9:41 am Post subject: Re: type conversion question,pls help!!! |
|
|
* James Kanze:
| Quote: |
Isn't that what I just said. Say the book is dated (which is
indeniably true). But a book isn't "bad" because it happened to
be written a while back.
[...]
I doubt that he [Bjarne]'d say the book [1st ed. TCPPPL] was bad.
Just dated.
|
It would be nice if you could give a reference to the "is bad" or
"isn't any good" (direct quotes) the above gives the impression of
arguing against.
As it is I'm bewildered.
Where did this come from? I think perhaps someone must have
misunderstood someone else. As the elderly wife said to her husband,
when one of us dies, I'll use the insurance to go on a luxury cruise.
--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Antoun Kanawati Guest
|
Posted: Thu Jun 09, 2005 9:47 am Post subject: Re: type conversion question,pls help!!! |
|
|
[email]kanze (AT) gabi-soft (DOT) fr[/email] wrote:
| Quote: | Antoun Kanawati wrote:
Or, we could have a Rational data type, by using some bits for
a type tag, and the rest to represent two integers.
I'm not really sure, but how do you define DBL_EPS in that
case. By definition, supposing only positive values:
if ( e < DBL_ESP )
assert( 1.0 + e == 1.0 ) ;
else
assert( 1.0 + e != 1.0 ) ;
|
Messing around with the numeric tower does change things quite a
bit. The DBP_EPS concept applies to conventional representation.
So, we could define it as:
assert(!is_exact(DBL_EPS));
// followed by original.
Anyway, I am not sure that rational numbers are as generally useful
as the usual floating point; and they're probably more expensive.
Defining them as a separate class would probably be sufficient, and
would avoid interaction with the natural representations.
--
A. Kanawati
[email]NO.antounk.SPAM (AT) comcast (DOT) net[/email]
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Powered by phpBB © 2001, 2006 phpBB Group
|