Rounding double

M

Mark L Pappin

jacob navia said:
If you invoke lcc-win in fully conforming mode with

lcc -ansic

atof should not be declared in math.h.

Then your original bit of posted code should have #included <stdlib.h>
in order to get the prototype, and your comment, when this deficiency
was pointed out, that you had [also] put this prototype in <math.h>
was irrelevant.


You can't have it both ways:

If you want to promote lcc-win as a conforming compiler, then DO NOT
provide examples that require it be invoked in a non-conforming mode
to compile.

If you want to promote the features of lcc-win that are not currently
part of the C standard, then kindly do it elsewhere.

Pick one.

mlp
 
J

jacob navia

Mark said:
jacob navia said:
If you invoke lcc-win in fully conforming mode with

lcc -ansic

atof should not be declared in math.h.

Then your original bit of posted code should have #included <stdlib.h>
in order to get the prototype, and your comment, when this deficiency
was pointed out, that you had [also] put this prototype in <math.h>
was irrelevant.

Er...

did you notice that I always forget where that dammed function is
declared?

And not only me. I received a lot of mail from users that tell me
that my compiler system doesn't work because atof doesn't work...
and then they forget that it is in stdlib, not in math.
You can't have it both ways:

If you want to promote lcc-win as a conforming compiler, then DO NOT
provide examples that require it be invoked in a non-conforming mode
to compile.

If you want to promote the features of lcc-win that are not currently
part of the C standard, then kindly do it elsewhere.

I did not PROMOTE IT I just forgot that for the NTH dammed time!

I just did not think about it. ANd now that you have scored good
points, can we leave this subject?

Thanks
 
F

Flash Gordon

jacob navia wrote, On 26/11/07 22:23:
Mark said:
jacob navia said:
If you invoke lcc-win in fully conforming mode with

lcc -ansic

atof should not be declared in math.h.

Then your original bit of posted code should have #included <stdlib.h>
in order to get the prototype, and your comment, when this deficiency
was pointed out, that you had [also] put this prototype in <math.h>
was irrelevant.

Er...

did you notice that I always forget where that dammed function is
declared?

Yes. Did you notice that compilers remind you when you make such a basic
mistake?
And not only me. I received a lot of mail from users that tell me
that my compiler system doesn't work because atof doesn't work...
and then they forget that it is in stdlib, not in math.

So correct them. I don't see the GNU or MS taking your route.
I did not PROMOTE IT I just forgot that for the NTH dammed time!

I just did not think about it. ANd now that you have scored good
points, can we leave this subject?

If you had just accepted that your code was in error rather than
pointing out that you include a prototype for atof in math.h you would
not have received comments about your compiler being non-conforming. If
you had not said that there was nothing to prevent you doing this then
you would not have received correction about that either.
 
J

jacob navia

James said:
All of our negative comments about your movement of atof() to math.h
apply only to the mode in which your comment indicating that you had so
moved it was true. If this is not true about 'lcc -ansic', then our
comments have not been about that mode. Our comments remain true and
relevant about whichever mode(s) of lcc your comment was true of. In
that mode, lcc is NOT ISO C compatible.

Ahhh ok, it is not ISO compatible because it declares atof
in math.h also...

As you know, there are quite a few functions that the programmer
has to remember. Not only remember that they exist, but also
remember in which header file they live. It is something
similar to

man 4 xxx

in the old unix days, where we had to remember not only that a
command existed but in which section of the manual it existed.

To remediate this, lcc-win gives to the users a

#include <stdheaders.h>

that I always use in my programs. It will include ALL headers.
and you do not have to remember where is each function.

Compiling this file takes 123 ms to lcc-win. It is an acceptable
waiting time.

Then, I tend to forget in which file functions that I use not
very frequently are located. And not me. I received complaints from
users that said tha atof was "NOT WORKING", just because they wrote

double d = (double)atof("2.3"); // cast hides warning
and got nonsense results.

They too thought that atof is in math.h since it is a math function,
isn't it? Somehow the idea is not *completely* stupid you will agree
with me.

I put it there and... FORGOT about it completely. So completely
that I posted the code like that, and a warning in gcc was the
only hint about that.

Is this something so serious that the whole workings of the compiler
can be said to be "incompatible with ansi/iso C"???

Are we lawyers or programmers?

Do we write very often programs like

#define atof (
#include <math.h>

???

What bothers me the most is that all this complaints come from people
that say openly that they will NEVER use my system, and that otherwise
are always trying to start lengthy discussions about things that maybe
are not 100% "kosher" but useful nevertheless!!
 
F

Flash Gordon

jacob navia wrote, On 26/11/07 22:45:
Ahhh ok, it is not ISO compatible because it declares atof
in math.h also...

As you know, there are quite a few functions that the programmer
has to remember. Not only remember that they exist, but also
remember in which header file they live. It is something
similar to

man 4 xxx

in the old unix days, where we had to remember not only that a
command existed but in which section of the manual it existed.

To remediate this, lcc-win gives to the users a

#include <stdheaders.h>

that I always use in my programs. It will include ALL headers.
and you do not have to remember where is each function.

That is perfectly acceptable as an extension. Of course, anyone could
write such a header for any implementation.
Compiling this file takes 123 ms to lcc-win. It is an acceptable
waiting time.

Then, I tend to forget in which file functions that I use not
very frequently are located. And not me. I received complaints from
users that said tha atof was "NOT WORKING", just because they wrote

double d = (double)atof("2.3"); // cast hides warning
and got nonsense results.

They too thought that atof is in math.h since it is a math function,
isn't it? Somehow the idea is not *completely* stupid you will agree
with me.

So tell them what they have done wrong. You could even do what gcc is
starting to do and warn when some functions have incorrect prototypes.
I put it there and... FORGOT about it completely. So completely
that I posted the code like that, and a warning in gcc was the
only hint about that.

Yet you chose to ignore the warning.
Is this something so serious that the whole workings of the compiler
can be said to be "incompatible with ansi/iso C"???
Yes.

Are we lawyers or programmers?

Both. You as an implementer should be even more of a language lawyer.
Do we write very often programs like

#define atof (
#include <math.h>

???

What bothers me the most is that all this complaints come from people
that say openly that they will NEVER use my system,

Have you ever considered that the reason they will not use your system
is because of your attitude?
and that otherwise
are always trying to start lengthy discussions about things that maybe
are not 100% "kosher" but useful nevertheless!!

No, I believe what most people want when they point out a non-compliance
is for you to accept and correct it. The long threads happen because you
keep insist on first claiming that the problems are not non-compliances,
then defending them. Fortunately this time it has taken rather less time
for you to admit that the standard does not permit declaring atof in math.h
 
D

Dik T. Winter

>
> Could you describe which of the *given* solutions is "better" and why?

Do you not understand the difference between a "best approximation" and
a "better solution"? Until now I have seen *no* solution that gives the
best approximation. I think it is possible, but pretty elaborate. Pray,
reread what I wrote.
 
D

Dik T. Winter

> Richard Heathfield wrote: ....
>
> The original problem as stated was, IMO, probably not intended to be
> read with the unconventionally strict interpretation you're using. I
> believe that Jacob is correctly describing the problem as clearly
> expressed by the OP.

I do not think so. I have seen too many articles posted in this newsgroup
asking why (when 0.33333333 is rounded to two decimals), the result is
printed as 0.3299999999, and not as 0.33000000 (or something similar) ...
 
K

Keith Thompson

I've dropped comp.lang.c++ for this followup.

jacob said:
It is also in stdlib. It is in BOTH, and nothing
is written about not putting it in math.h

Ok, I understand that you have a correct declaration for atof in
<stdlib.h>; I never assumed otherwise.

May a conforming implementation declare a function called "atof" in
<math.h>? I'm actually not 100% certain of the answer to that. Since
"atof" is an identifier with external linkage declared in a standard
header, it's always reserved for use as an identifier with external
linkage, whether the program has a ``#include <stdlib.h>'' or not. It
may be that adding a declaration for "atof" to <math.h> cannot break any
program that isn't already broken. (Of course, <math.h> must not
additionally declare "atof" as a macro, something that <stdlib.h> is
allowed to do.)

If this is actually permitted, I suppose that an implementation could go
ahead and declare *all* standard functions in *all* the standard headers.

jacob, did you actually go to the effort of verifing, based on the
wording of the standard, that declaring "atof" in <math.h> is permitted,
or did you just go ahead and do it because it was convenient for you?
Is the behavior affected by the "-ansic" option?

The real problem with this practice is that, even if it's permitted as
an extension, it makes it far too easy for users of the implementation
to write non-portable code. A programmer who mistakenly thinks (as you
apparently did) that atof is declared in <math.h> will not have his
error caught by lcc-win32. He may then try to compile his code under a
different implementation and be surprised that it fails to compile.
You're really not doing your users any favors.

....

After I wrote the above, I thought of a test case. I believe that the
following program is strictly conforming:

#include <math.h>

static void atof(void)
{
}

int main(void)
{
atof();
return 0;
}

But if I change "#include <math.h>" to "#include <stdio.h>", it has a
constraint violation, requiring a diagnostic. One compiler I tried
handles this correctly (at least it agrees with me). How does lcc-win32
handle it?
 
K

Keith Thompson

jacob said:
Richard said:
jacob navia said: [...]
Of course I agree with your short description of these people.
And one of their "word games" was precisely to say that it is
impossible to write:

double rounto(double x, unsigned places);

No, of course you can *write* it (duh). It just won't do what you
claim it does, that's all.

I claim that this will deliver the best approximation to the
rounding to n decimal places, and I have never claimed otherwise.

If you do not agree, produce a better function.
[code snipped]

Provide a better function to do what?

jacob, you persist in missing the point, even as you implicitly
acknowledge it.

The OP did *not* ask for a function to provide *the best approximation
of* a double value rounded to n decimal places. The OP asked for a
function to provide *a double rounded to n decimal places*. You have
simply assumed that, since what he literally asked for is impossible, he
must really be asking for something that's similar but possible.

How do you know what the OP really wants?

When someone asks a question with incompletely specified requirements,
as has happened here, we can ask for clarification (as we've done, but
the OP has so far failed to offer it), or we can make reasonable
assumptions. You've assumed that a close approximation is good enough.
You *might* be correct, but your solution, though it may meet the OP's
requirements when coupled with your assumption, is IMHO not particularly
useful. (Think about it for a moment. Given a function such that
roundto(3.14159, 2) yields *approximately* 3.14, most likely something
like 3.140000000000000124344978758017532527446746826171875, would *you*
have any real use for such a thing? You might provide it in your
library, but would you really use it in your own code?)

What I and most others here have assumed instead is that what the OP
really wants is something *useful*. Given this assumption (which I
acknowledge is just an assumption), the OP needs to step back a bit and
re-think the problem. He probably really wants a *textual*
representation of "3.14", which is exact, rather than a floating-point
representation that can only be approximate, and whose only likely
purpose is to produce the exact textual representation anyway. The
point I suspect he's missing is that the original floating-point value
(an approximation of 3.14159) is just as useful as an approximation of
3.14 for the purpose of producing the string or output "3.14".

If you choose to make an assumption about what the OP really wants,
that's fine. You might even be correct. What I find annoying is your
stubborn insistence that no other assumption is possible.

There's another possibility that I don't recall anyone mentioning.
Perhaps this is a homework assignment, and perhaps it's the instructor
who fails to realize that decimal rounding of binary floating-point
values is not useful.
 
G

Gordon Burditt

jacob, you persist in missing the point, even as you implicitly
acknowledge it.

The OP did *not* ask for a function to provide *the best approximation
of* a double value rounded to n decimal places. The OP asked for a
function to provide *a double rounded to n decimal places*. You have
simply assumed that, since what he literally asked for is impossible, he
must really be asking for something that's similar but possible.

It is possible to provide a rounded double that will have N decimal
places and be rounded exactly (when represented in binary floating
point). Round to the nearest multiple of 2**-N . (** is an
exponentiation operator, which C doesn't have and mathematics uses
superscript for, which is a bit difficult in ASCII text.) When
converted to decimal, it will have N decimal places. It will also
be representable exactly provided you've got enough mantissa bits.
However, I seriously doubt that anyone would actually ask for this,
except as a puzzle or to win a bet.

There's another possibility that I don't recall anyone mentioning.
Perhaps this is a homework assignment, and perhaps it's the instructor
who fails to realize that decimal rounding of binary floating-point
values is not useful.

Unfortunately, I think fixing this problem would exhaust the world
supply of clue-bats.
 
K

Keith Thompson

I'm dropping comp.lang.c++ from this followup, since it's only about C.

jacob navia said:
There are many freely available C99 compilers.
[...]

Name one.

Note that neither lcc-win32 nor gcc qualifies; both implement
substantial subsets of C99, but neither implements the entire C99
standard. You know that perfectly well, of course.

Let me explain what I mean by this. Suppose I have a copy of the C99
standard (in fact, I do), and based on that, I want to write a
portable C program that conforms to that standard. If your statement
above were true, I could write such a program, obtain one of these
free compilers, and be confident that I can then compile and execute
my program.

I can't do that. Without knowing the specific vagaries of the
compiler(s) I'm going to use, I'm likely to want to use some feature
that is not implemented by the particular compiler. I can work around
that by learning which features are not supported and avoiding them.
If I want portability, then I have to avoid the union of the sets of
unimplemented features for *all* the compilers I might want to use. I
can use a subset of C99, but I have to know about specific compilers
to determine what subset I can use. (As it happens, a good subset for
this purpose is very close to plain C90 with a few restrictions.)

By contrast, suppose I do the same things starting with a copy of the
C90 standard. If I stick to what's guaranteed by C90, it's very
likely that my program will work properly with nearly any existing
compiler.

Do you understand the difference?

That's the whole point of having a standard; it lets me write portable
programs without having to know the details of individual
implementations.

To be very clear, I am not expressing opposition to C99, or a desire
for C either to die or to remain unchanged forever. (That's the false
conclusion you typically draw.) I would very much like for C99 to be
as widely implemented as C90 is today. It just isn't, and since I'm
not an implementer, there's very little I can do to change that fact.
 
B

Bart

...is IMHO not particularly
useful. (Think about it for a moment. Given a function such that
roundto(3.14159, 2) yields *approximately* 3.14, most likely something
like 3.140000000000000124344978758017532527446746826171875, would *you*
have any real use for such a thing? You might provide it in your
library, but would you really use it in your own code?)

Definitely. The error is not significant, I can live with it.

There are many reasons why this kind of rounding is useful in the real
world, often to deal with noise reduction. There seems to be a
remarkable lack of imagination from many contributors here who think
rounding is only useful in printing numbers.

Having a round(x) that was magically exact wouldn't change anything
because nobody would know what to do with it!

But round(x) isn't exact, although neither is, say, reciprocal(x), so
that would be banned too.
who fails to realize that decimal rounding of binary floating-point
values is not useful.

By rounding, I can compare my noisy data with yours; if the difference
is less than some tolerance, it can be considered the same!

And it can look cleaner when printed without having to depend on
highly specific printf formats. If I know my data doesn't contain more
than 3 decimals of info, but might be printed with 6 decimals,
rounding will clean it up. Maybe I don't even know who will print it
and in what format.

Bart
 
R

Richard Heathfield

Bart said:
Definitely. The error is not significant, I can live with it.

There are many reasons why this kind of rounding is useful in the real
world, often to deal with noise reduction.

We don't call that "rounding", though. We call it "approximating". Nobody
denies its utility. But rounding it ain't.

<snip>
 
K

Keith Thompson

James Kuyper said:
Bart wrote:
...

Incorrect. What I believe is that the real and practical reasons tend
to fall into two categories:

a) Conversion of floating point numbers to digit strings, usually for
output.

b) Calculations that should, properly, be carried out in fixed-point
arithmetic. In the absence of direct language support for fixed-point,
it should be emulated by the programmer using, for instance, an
integer to represent 1000 times the actual value, if that value is to
be stored with 3 digits after the decimal place. All of the example
you gave should fall into this second category.
[...]

Yes, there are good reasons for wanting to round a numeric value to a
specified number of decimal places, but neither example you provided
(nor any other reasonable example I can think of) calls for storing
the resulting rounded value in a double.
 
K

Keith Thompson

jacob navia said:
Mark said:
jacob navia said:
If you invoke lcc-win in fully conforming mode with

lcc -ansic

atof should not be declared in math.h.

Then your original bit of posted code should have #included <stdlib.h>
in order to get the prototype, and your comment, when this deficiency
was pointed out, that you had [also] put this prototype in <math.h>
was irrelevant.

Er...

did you notice that I always forget where that dammed function is
declared?

And not only me. I received a lot of mail from users that tell me
that my compiler system doesn't work because atof doesn't work...
and then they forget that it is in stdlib, not in math.
[snip]

I did not PROMOTE IT I just forgot that for the NTH dammed time!

I just did not think about it. ANd now that you have scored good
points, can we leave this subject?

I'm not particularly interested in scoring points.

Let me see if I understand what's going on here. jacob, you posted a
program that uses atof() and #includes <math.h> but not <stdlib.h>.
When this was pointed out, you said that lcc-win32 declared atof() in
<math.h>, to cater to programmers (including yourself) who forget that
it's declared in <stdlib.h>.

I *think* that when you mentioned this lcc-win32 extension, you did so
to try to explain why you had made the error in the program you
originally posted. But a lot of readers (including me) thought that
you were advocating your extension as a way of solving this problem,
and implicitly claiming that failing to include <stdlib.h> wasn't
really an error. In my opinion you still haven't been entirely
clear on this point.

Furthermore, you did not *initially* mention that the extension is not
enabled when the "-ansic" option is specified (i.e., in the mode that
attempts to be conforming).

Declaring atof() in <math.h> is not allowed for a conforming C
implementation, but a non-conforming implementation (like lcc-win32
without "-ansic") can of course do anything it likes.

As for what lcc-win32 *should* do, I have a suggestion, which you can
take or leave as you like (somebody else in this thread suggested
something similar). Declaring atoi() in <math.h>, even in a
non-conforming mode, encourages the writing of non-portable code.
Enabling your users to make this particular mistake doesn't do them
any favors. I admit that keep track of which functions are declared
in which headers can be difficult. So why not make it easier? Let
the compiler know which standard functions are declared where. If a
program uses an external identifier that's declared in a standard
header, and that header is not included, then issue a diagnostic
message *that says which header to use*.
 
R

Richard Heathfield

Keith Thompson said:
Declaring atoi() in <math.h>, even in a
non-conforming mode, encourages the writing of non-portable code.

Although this is certainly true, I think you meant to write atof().
 
J

James Kuyper

Keith said:
James Kuyper said:
Bart wrote:
...
Incorrect. What I believe is that the real and practical reasons tend
to fall into two categories:

a) Conversion of floating point numbers to digit strings, usually for
output.

b) Calculations that should, properly, be carried out in fixed-point
arithmetic. In the absence of direct language support for fixed-point,
it should be emulated by the programmer using, for instance, an
integer to represent 1000 times the actual value, if that value is to
be stored with 3 digits after the decimal place. All of the example
you gave should fall into this second category.
[...]

Yes, there are good reasons for wanting to round a numeric value to a
specified number of decimal places, but neither example you provided
(nor any other reasonable example I can think of) calls for storing
the resulting rounded value in a double.

That was my point, and judging from the two responses I've received, I
didn't make it clear enough. What I was saying was that, contrary to
Bart's statement, I do "believe there are real and practical reasons for
rounding value to so many decimals". As you say, the problem is that
none of the reasons I'm aware of for doing so "calls for storing the
resulting rounded value in a double". Bart apparently thinks that such
storage is perfectly acceptable in some circumstances, but I disagree.
 
B

Bart

We don't call that "rounding", though. We call it "approximating". Nobody
denies its utility.

I think Keith Thompson and one or two others were.
But rounding it ain't.

I'm intrigued, why are you so prejudiced about poor old round(x)
function for reasons that could be applied to nearly anything, like
(slightly contrived) a reciprocal(x) function for example? In fact in
seems Divide is the real culprit because often you don't get exactly
what it says on the box.

Bart
 
R

Richard Heathfield

Bart said:
I think Keith Thompson and one or two others were.


I'm intrigued, why are you so prejudiced about poor old round(x)
function for reasons that could be applied to nearly anything, like
(slightly contrived) a reciprocal(x) function for example?

I'm not sure that "prejudiced" is the right word. I don't /use/ it, but
that's only for the same reason that I don't use any C99 features in code
intended to be portable. But round() doesn't actually round to a given
number of decimal places (or rather, it does, provided that the number of
decimal places you want is zero!).

If you try to use it to round 0.33 to one decimal place, e.g. like this:

f = round(f * 10) / 10;

then you'll get 0.30000000000000000001 or 0.29999999999999998 or something
like that. What you won't get is *precisely* 0.3 in f.

In fact in
seems Divide is the real culprit because often you don't get exactly
what it says on the box.

Whatever the culprit, the crime remains - you can't (in the general case)
store an arbitrary value in a double to precisely n decimal places, for
any value of n > 0. (You *can* store particular values, of course: 0.5,
0.75, and so on.)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top