Rounding double

R

RoS

In data Sat, 24 Nov 2007 20:34:46 -0000, Gordon Burditt scrisse:
No, I'm not. I want you to print out enough digits to get the
*EXACT* value of the result you actually got. This doesn't make
sense when you are interested in the value you are calculating, but
it does make sense when you are debugging floating-point rounding
problems. (You will never get an infinite repeating decimal taking
binary floating point values with finite mantissa bits and converting
them to decimal).



But it's not enough to print the exact value you are getting. When
you are debugging rounding problems, why introduce *more* rounding
error that may obscure the problem you are trying to debug?


No, it's not nonsense. The value you *actually got* can be
represented exactly if you use enough digits. The value you should
have gotten in infinite-precision math, and taking into account the
accuracy of the inputs cannot be, and you have a point outside the
context of debugging rounding issues.

i agree with above
the only place where there should be some approximation is
in the traslation "float to string" (or "string to float")
e.g.
1234567
fun(7, 12.9999999555556)="13.0000000"
or when print in stdout or in a file

and this could[should?] be done in the "string" not in the float
so i could say: "no approximation for float"!
 
J

James Kanze

Jim Langston said:
Right - but of course a cross-posted article should "work" in all the
groups into which it's posted.

Certainly. But here, the original question is actually relevant
to both groups: for the most part, the C++ standard handles
floating point by saying: see the C standard (or alternatively,
by duplicating the wording of the C standard). I'd guess that
whoever posted the answer didn't notice that he was responding
to a cross-posted article.
Here, the post worked in clc++ but not in clc, so either it
should not have been posted to clc or the pointer syntax
should have been used rather than the reference syntax (at
which point, of course, there would have been howls of protest
from the clc++ crowd, and perhaps rightly so).

Shit happens. Since the pointer syntax is a legal in C++ as
well, you shouldn't hear to many complaints from that side; it's
what I use when I'm aware of a cross-posting. (In this case, I
didn't happen to notice the thread until it was fairly long.
And just seeing the names of the posters signaled that it was a
cross-posting---I've never seen you respond to a posting in
clc++ which wasn't cross-posted, and you're not the only one in
this case.)

The languages aren't unrelated, and in this case, the original
question was quite appropriate for a cross-posting. IMHO, it
doesn't seem to be asking too much to be somewhat tolerant with
regards to the actual syntax used in the answers, on the
grounds that whoever is responding may not be aware of the
cross-posting. (Not to criticize your original response.
Nothing wrong with mentionning that the syntax isn't legal in
the language of the group in which you're posting, as long as
you go on to address the real issues, as you did. Let's just
not get hung up with it.)
 
J

James Kanze

Hm. I wonder if this might be a matter of interpreting the
problem.
The C standard says about sqrt() that it computed the
nonnegative square root. C++ inherits this requirement. If
your interpretation of the rounding problem is correct and if
we transfer it to the other arithmetic operations, then there
can be no conforming implementations. In fact, even elementary
school arithmetic (+,-,*,/) cannot be done correctly under
that interpretation. However, that interpretation of the specs
is not the only possible.
A different interpretation of floating point computations is
that an operation (say multiplication, addition, sqrt, or
rounding to a given number of decimal places) should yield a
double (or float or whatever types are topical in the
corresponding newsgroup) that is closest to the exact
mathematical result. If I recall correctly, this is by and
large the position taken by IEEE754.

The problem is that neither the C nor the C++ standard require
such. And that both standards also allow greater precision in
the intermediate results. A liberty that is, in fact, used by
most compilers for at least one very common architecture today.
And which makes "rounding" using just floating point arithmetic
very, very difficult. (I remember seeing an implementation of
modf, a very long time ago, which used so trick involving
multiplication and division in a way to end up with the integral
part as a result of loss of precision. The person who was
porting the library to the 8086 was astonished to find that the
value written through the iptr argument wasn't an integer.)
When this (reasonable) interpretation is adopted,

I'm not too sure about the "reasonable" part. On an Intel
machine, a * b, where a and b are both double, does NOT give the
exact result, rounded to the nearest representable double. And
although Intel processors are pretty scarce in the milieu where
I work (the last Intel I actively programmed on was an 80386),
I've heard that they are still in use. (And other processors,
such as the AMD 64 bit processor on my home PC, also behave this
way.)
the problem of rounding to a fixed number of decimals is
solvable (and it is indeed not different from any other
computational problem). And if you don't adopt an
interpretation like that, floating point arithmetic in general
is "impossible".

I'd still leave it up to the implementation to handle the tricky
parts. Multiply by a power of 10, floor(), ciel() or round(),
and then divide by the same power, and you should get something
fairly close to a correct result (and if you're talking about
"rounding in base 10", close is all you're going to get).
 
R

Richard

James Kanze said:
The languages aren't unrelated, and in this case, the original
question was quite appropriate for a cross-posting. IMHO, it
doesn't seem to be asking too much to be somewhat tolerant with
regards to the actual syntax used in the answers, on the

Welcome to C.L.C. There are a small group of "regulars" who stick out
their pigeon chests and get very, very angry when they think you are
"OT" or not showing them enough respect. It is blatantly obvious to
anyone with a modicum of programming experience what was meant and to
who it was directed but the core here just love to "catch people out"
and then huff and puff about for ages while trying to tie you up in word
games.
grounds that whoever is responding may not be aware of the
cross-posting. (Not to criticize your original response.
Nothing wrong with mentionning that the syntax isn't legal in
the language of the group in which you're posting, as long as
you go on to address the real issues, as you did. Let's just
not get hung up with it.)

Fortunately there is an influx of saner people who do not feel the need
to gain points by stating the bloody obvious in every other reply - as
is obvious if you filter our Mr Heathfield and his cronies.
 
J

jacob navia

James said:
I'd still leave it up to the implementation to handle the tricky
parts. Multiply by a power of 10, floor(), ciel() or round(),
and then divide by the same power, and you should get something
fairly close to a correct result (and if you're talking about
"rounding in base 10", close is all you're going to get).

This is exactly what my function does. It is a close implementation of
the rounding, and never claimed to be the *exact* since that is impossible!
 
J

jacob navia

Richard said:
Welcome to C.L.C. There are a small group of "regulars" who stick out
their pigeon chests and get very, very angry when they think you are
"OT" or not showing them enough respect. It is blatantly obvious to
anyone with a modicum of programming experience what was meant and to
who it was directed but the core here just love to "catch people out"
and then huff and puff about for ages while trying to tie you up in word
games.

Please remember that the "regulars" of c.l.c are a small group of people
that speak only for themselves. Do not generalize to all the
users of that group please.

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);
Fortunately there is an influx of saner people who do not feel the need
to gain points by stating the bloody obvious in every other reply - as
is obvious if you filter our Mr Heathfield and his cronies.

Yeah, but it is obvious that the cronies are difficult to ignore.
 
R

Richard

jacob navia said:
Please remember that the "regulars" of c.l.c are a small group of people
that speak only for themselves. Do not generalize to all the
users of that group please.

You mean "remember" like when I said

** There are a small group of "regulars" **

Yes. I will :-;
 
M

Mark McIntyre

Richard wrote:

(trollish stuff)
Of course I agree with your short description of these people.

Two trolls agreeing with each other is hardly interesting.
--
Mark McIntyre

"Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are,
by definition, not smart enough to debug it."
--Brian Kernighan
 
R

Richard

Mark McIntyre said:
(trollish stuff)

Facts of life with regard to C.L.C you mean.
Two trolls agreeing with each other is hardly interesting.

Interesting enough for you to reply to I notice. Of course being in the
"small clique" which causes so much repugnance, you are indeed honour
bound to reply. In fairness at least you don't tell people to RTFM as
frequently as your namesake Blumel.
 
J

James Kuyper

jacob said:
This is exactly what my function does. It is a close implementation of
the rounding, and never claimed to be the *exact* since that is impossible!

No, your function does not use floor(), ceil(), or round() to calculate
floating point representations of the intermediate integral value. It
uses conversion to long long for that purpose. As a result it relies
upon non-portable assumptions about the relationship between the
precision of long long and the precision of long double. On
implementations where that assumption is invalid, your algorithm
unnecessarily produces results for some argument values that are less
accurate than they could be with a more appropriate algorithm. Note:
such an algorithm should actually use floorl(), ceill() or roundl(),
rather than floor(), ceil() and round(), for precisely the same reason
that your algorithm uses powl() rather than pow().
 
K

Kaz Kylheku

Hi

Does any body know, how to round a double value with a specific number
of digits after the decimal points?

A double probably doesn't have a decimal point. Floating point values
are commonly represented in binary, not decimal. So what you are
asking for is generally impossible.
A function like this:

RoundMyDouble (double &value, short numberOfPrecisions)

It then updates the value with numberOfPrecisions after the decimal
point.

The real number which this function is intended to compute might not
be representable in the floating point format used by your double
type.

At best, you can only write this function such that it produces a
close approximation of that number. But still, this function is
silly, and serves no purpose.

Usually this type of rounding is done in two situations.

One situation is that you are doing some kind of scientific or
mathematic computing, and want to display results to a given decimal
precision. In that case, the rounding and truncation is handed in the
conversion of floating point values to text in the output routine. You
do not actually massage your data to do the rounding. The number-to-
string routine you use, whether it be within of printf or C++ ostreams
or whatever, will do the job of rendering a printed representation of
the number in decimal to the specified precision. You never adjust the
internal representation to achieve this. Internally, you always keep
the maximum precision afforded to you by the machine. A roudning
function like the above is of little use to you.

The second situation is that you are doing financial computing, and
need internally to have exact decimal-based arithmetic that follows
certain prescibed rounding rules. All intermediate results in
financial calculations must obey these rules. Whatever is printed in a
financial statement matches the internal representation. If your bank
book says that an account had 1234.53 dollars after a certain
transaction, it means exactly that. It doesn't mean there were
actually 1234.5321 dollars, which were printed to the nearest cent. In
this situation, it is simply inappropriate to be using floating-point
numbers, and so a routine which simulates decimal rounding over the
double type is also of no use.
 
K

Kaz Kylheku

I got tired of #including math.h and finding out that atof
wasn't there but in stdlib.h. So I put it in math.h in lcc-win.
This is of course not the case with gcc, that has it in stdlib.

GCC has it in stdlib.h, because, like, this thing called the ISO C
standard wants it in stdlib.h.
 
J

jkherciueh

James said:
The problem is that neither the C nor the C++ standard require
such. And that both standards also allow greater precision in
the intermediate results. A liberty that is, in fact, used by
most compilers for at least one very common architecture today.
And which makes "rounding" using just floating point arithmetic
very, very difficult. (I remember seeing an implementation of
modf, a very long time ago, which used so trick involving
multiplication and division in a way to end up with the integral
part as a result of loss of precision. The person who was
porting the library to the 8086 was astonished to find that the
value written through the iptr argument wasn't an integer.)


I'm not too sure about the "reasonable" part.

a) This interpretation is not the only reasonable alternative
interpretation. Unfortunately, the OP has not given us much to go on. I
just wanted to demonstrate that the assumption, the OP wanted the rounding
to yield _exact_ results (as opposed to all other arithmetic operation) is
unfounded.

b) [comp.land.c++ only] Although the C++ does not require floating point
arithmetic to conform to IEEE754, it recognizes the relevance of IEEE754 in
that the header <limits> provides compile time means to check for
On an Intel
machine, a * b, where a and b are both double, does NOT give the
exact result, rounded to the nearest representable double. And
although Intel processors are pretty scarce in the milieu where
I work (the last Intel I actively programmed on was an 80386),
I've heard that they are still in use. (And other processors,
such as the AMD 64 bit processor on my home PC, also behave this
way.)

Interesting. My laptop uses an Intel processor and my C++ implementation
claims that my floating point types conform to IEEE754. I guess, the
compiler does something funny and corrects for the insufficient machine
instructions.

I'd still leave it up to the implementation to handle the tricky
parts. Multiply by a power of 10, floor(), ciel() or round(),
and then divide by the same power, and you should get something
fairly close to a correct result (and if you're talking about
"rounding in base 10", close is all you're going to get).

Actually, without guarantees about the precision of the results, it can be
very difficult to judge whether approximate rounding is useful for a given
purpose. Suppose you are computing letter grades and the rules require you
to take a weighted average, round to one digit after the decimal point, and
then check whether the result is within, say, [0.7,1.3]. If you do not have
the guarantee that rounding and compiler interpretation of floating point
literals is done according to the same precision and floating point
rounding, you cannot really use floating point arithmetic (unless you
engage in numerical analysis). In those cases, I would be inclined to use a
decimal type. (And even if you have the necessary guarantees about
rounding, you still have to make sure that the excess precision allowed for
internal computations does not get in the way.)


The real problem is the OP who fails to provide enough information to get
meaningful help. As of now, all proposed rounding methods (and also all
suggestions saying that rounding should be put off until output) in this
thread are based on guesses as to what the OP needs the rounding for.


Best

Kai-Uwe Bux
 
R

Richard Bos

jacob navia said:
I got tired of #including math.h and finding out that atof
wasn't there but in stdlib.h. So I put it in math.h in lcc-win.

Ok, so you've found yet another way for your toy to break ISO C
compatibility. Well done.

Richard
 
R

Richard Heathfield

jacob navia said:
Richard wrote:

Please remember that the "regulars" of c.l.c are a small group of people
that speak only for themselves. Do not generalize to all the
users of that group please.

Please note that both you and "Richard" (i.e. Richard Riley) are regular
contributors to comp.lang.c - therefore, any comment you make about
regular contributors can reasonably be assumed to apply to both you and
him too. In this case, that makes perfect sense. After all, you are both
part of a small group of people that speak only for themselves.
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.
 
J

jacob navia

Richard said:
Ok, so you've found yet another way for your toy to break ISO C
compatibility. Well done.

Richard

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

jacob navia

James said:
No, your function does not use floor(), ceil(), or round() to calculate
floating point representations of the intermediate integral value. It
uses conversion to long long for that purpose. As a result it relies
upon non-portable assumptions about the relationship between the
precision of long long and the precision of long double. On
implementations where that assumption is invalid, your algorithm
unnecessarily produces results for some argument values that are less
accurate than they could be with a more appropriate algorithm. Note:
such an algorithm should actually use floorl(), ceill() or roundl(),
rather than floor(), ceil() and round(), for precisely the same reason
that your algorithm uses powl() rather than pow().

That is the case. I changed the function to clean it up, and
its last version was:

#include <stdio.h>
#include <float.h>
#include <math.h>

double roundto(double value, unsigned digits)

{
long double v = value;
long double fv = fabs(value),p = powl(10.0L,digits);
if (fv > powl(10.0L,DBL_DIG) || digits > DBL_DIG)
return value;
return roundl(p*value)/p;
}
 
J

jacob navia

Richard said:
jacob navia said:


Please note that both you and "Richard" (i.e. Richard Riley) are regular
contributors to comp.lang.c - therefore, any comment you make about
regular contributors can reasonably be assumed to apply to both you and
him too. In this case, that makes perfect sense. After all, you are both
part of a small group of people that speak only for themselves.


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.

#include <stdio.h>
#include <float.h>
#include <math.h>

double roundto(double value, unsigned digits)

{
long double v = value;
long double fv = fabs(value),p = powl(10.0L,digits);
if (fv > powl(10.0L,DBL_DIG) || digits > DBL_DIG)
return value;
return roundl(p*value)/p;
}
 
R

Richard Heathfield

jacob navia said:

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.

As you ought to know already, I don't see any point in trying to solve a
problem that is inherently impossible for reasons that I have already
explained.

We need to know why we're rounding. If we're dealing with, say, currency
(or some analogous system), the proper solution is to do calculations in
an integer unit of which all other currency units are a multiple (e.g. for
Sterling, use pennies; in the USA, use cents; in Europe, use Euros), and
to establish a protocol for dealing with calculations that don't fit into
this process (e.g. interest calculations). If we're dealing with
calculations that simply require a neatening off for display purposes, on
the other hand, then the proper solution is to round the text
representation, not the value itself.
#include <stdio.h>
#include <float.h>
#include <math.h>

double roundto(double value, unsigned digits)

{
long double v = value;
long double fv = fabs(value),p = powl(10.0L,digits);
if (fv > powl(10.0L,DBL_DIG) || digits > DBL_DIG)
return value;
return roundl(p*value)/p;
}

That code is perfectly topical, of course - but it doesn't solve the
problem. Given this fact, the fact that it doesn't cater for those without
C99 compilers is of little consequence.

Data point: on my system, it gives very very very incorrect results (even
within the context that you've adopted: e.g. if I ask it to round 0.33 to
1dp I get -1.3543851449227162220), but then I don't have a C99 compiler,
merely a gcc implementation that provides non-C99-conforming extensions -
but clearly this is a separate issue, and one on which the opinions of
reasonable people are divided.

(For those who may well be thinking - and indeed have already expressed the
thought - that I should "get a C99 compiler then - or at least a compiler
that supports many C99 features", my position is this: Many professional C
programmers do not get to choose the implementation they are using. In
many software environments, the decision about which compiler to use was
made long ago for reasons that do not count "having the latest C99 stuff"
as being particularly important when measured against more important
stability criteria. It is therefore unwise to assume that other
programmers have access to C99 features. But the C99-ness of Mr Navia's
code is not the reason I think it fails to solve the problem. The reason I
think Mr Navia's code doesn't solve the problem is that I think the
problem as stated is insoluble.)
 
R

Ralf Damaschke

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

It depends on the cleverness of such an implementation.
If such implementation does not also have the magic to forget the
extra declaration of atof when stdlib.h was not included, it
violates 7.1.3. The following program is strictly conforming.

#include <math.h>
static int atof = 4;
int main()
{
return 0;
}

Ralf
 

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,995
Messages
2,570,230
Members
46,819
Latest member
masterdaster

Latest Threads

Top