Rounding double

J

jacob navia

Richard said:
That, at least, is true. The best solution is to understand the inherent
limitations in representing fractions, and to recognise that the
appropriate way to deal with this is to deal with the rounding at the
display stage.

That wasn't what the OP asked. He wanted to round it without any
display, maybe to be used in further calculation or whatever.

And it IS possible to round correctly a double precision
number. One of the possible solutions is:

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

void RoundMyDouble (long double *value, short numberOfPrecisions)

{
long double fv=fabsl(*value);
if (fv > 1e17)
return ;
long long p = powl(10.0L, numberOfPrecisions);
*value = (long long)(*value * p + 0.5L) / (double)p;
}

This is quite awful, but it works (with 64 bit long long and
wider precision long double)
Ah, we're back to "nonsense" as your synonym for "correct solution that
Jacob Navia doesn't understand", are we?

Nonse means to tell the OP that what he is asking is
impossible!

I think is the pompous tone that makes me nervous.

"Ex cathedra"...
 
J

James Kuyper

jacob said:
James Kuyper wrote: ....
Probably. Your solution is always the best since you did not
propose any

?
I suggesting using sprintf() and sscanf(). I left the details to be
worked out by the reader. Was that too obscure a hint for you?
 
R

Richard Heathfield

jacob navia said:
Richard Heathfield wrote:


I do not know which "C" you are talking about.

I'm talking about C as widely implemented and used.
I am talking about
the C as defined by the C99 standard which says in the
Annex F (normative)

<quote>
The C floating types match the IEC 60559 formats as follows:
? The float type matches the IEC 60559 single format.
? The double type matches the IEC 60559 double format.

Well done! A valid technical point, at long last! You see? You *can* do it
when you try. Of course, almost nobody actually *uses* C99, so it's a bit
academic, but that's okay - you've at least demonstrated that we seem to
have been talking at cross-purposes, in that you were talking about the de
jure C language, whereas I was talking about the de facto C language.
 
J

jacob navia

James said:
?
I suggesting using sprintf() and sscanf(). I left the details to be
worked out by the reader. Was that too obscure a hint for you?

But he was asking for a function that returns a double, not
asking about PRINTING a double.

Maybe he wants to WRITE printf :)

Or you are saying that you belive this is impossible
in C???

double roundto(double n,int digits);

I do not believe so. OK. My solution looks (and is horrible,
but it has the merit of working for a wide range:
#include <math.h>

double RoundMyDouble (long double *value, short numberOfPrecisions)

{
long double fv=fabsl(*value);
if (fv > 1e17 ) // That could be replaced with some standard constant
return ;
long long p = powl(10.0L, numberOfPrecisions);
return (long long)(*value * p + 0.5L) / (long double)p;
}
 
R

Richard Heathfield

jacob navia said:
Walter Roberson wrote:


You are wrong.

Maybe he is, and maybe he isn't. Walter, can you tell us which system it is
that has 107-bit mantissae in doubles? I can find a few references to long
doubles with 107-bit mantissae, but I've drawn a blank on doubles. This is
probably because my search terms are faulty. Ploughing through 56,000 hits
isn't my idea of fun.
 
J

James Kuyper

jacob navia wrote:
....
I do not know which "C" you are talking about. I am talking about the
C as defined by the C99 standard which says in the Annex F
(normative)

Annex F is indeed normative, but it's specifications apply only if
__STDC_IEC559__ is #defined by the implementation. The standard very
deliberately fails to requires that __STDC_IEC559__ be #defined.
 
F

Flash Gordon

jacob navia wrote, On 22/11/07 21:57:
Who is saying anything about "C in general" ???

Can't you read THE SUBJECT OF THIS THREAD!!!
"Rounding double"

You know what DOUBLE means?

I'm sure he does, and as he pointed out there is no upper limit on the
range and precision a double is allowed to have. So an implementation
that allowed the values Richard suggested would still be conforming.
This thread is about rounding double precision numbers,
and your multi precision is off topic in this thread!

NOTE THE SUBJECT HEATHFIELD!

Threads drift, that is part of the nature of Usenet. Surely you have
noticed this by now?
It is NOT "rounding in general to 50 decimal places" it
is "Rounding double" OK?

So people are not allowed to correct errors that are not directly
related to the initial topic?

Richard was perfectly correct in stating that rounding of large values
makes sense. It would also make sense under some situations to round the
number you quoted to -21 decimal places (or similar).
 
J

jacob navia

Richard said:
jacob navia said:


Maybe he is, and maybe he isn't. Walter, can you tell us which system it is
that has 107-bit mantissae in doubles?

Strange. lcc-win has a 352 bit (around 107 decimal digits) qfloat,
but not doubles of course... Maybe Mr Robertson was speaking about lcc-win?
I can find a few references to long
doubles with 107-bit mantissae, but I've drawn a blank on doubles. This is
probably because my search terms are faulty. Ploughing through 56,000 hits
isn't my idea of fun.

Much more fun would be to answer the original question.

Excuse me nbut you REALLY believe that it is impossible to write in
standard C

double roundto(double n,int digits);

????
 
R

Richard Heathfield

Walter Roberson said:
You missed the earlier portion of the same Annex:

An implementation
that defines __STDC_IEC_559__ conforms to the specifications
in this annex.

Implementations are not required to define __STDC_IEC_559__
and hence have no mandated maximum limits on the number of double
digits.

Good spot, but credit where credit's due - Jacob Navia did finally manage
to argue a technical point properly and almost correctly. (I, too, missed
the earlier portion of the same annex, even though, writing this, I can
*see* it in another window on my screen!) Perhaps this is a turning point
for him. Let's hope so.
 
J

jacob navia

Richard said:
Walter Roberson said:


Good spot, but credit where credit's due - Jacob Navia did finally manage
to argue a technical point properly and almost correctly. (I, too, missed
the earlier portion of the same annex, even though, writing this, I can
*see* it in another window on my screen!) Perhaps this is a turning point
for him. Let's hope so.

OK That's much better tone Heathfield. Let's calm down.

Now, are we really such a C "heads" that we can't answer the OP
question IN HIS terms?

double roundto(double d,int digits);

I use more precision than double, test for out of range, and give the
answer. That's a solution, but I am sure you can bring a better one.

After all, we have this question once every two three months.
 
W

Walter Roberson

Walter, can you tell us which system it is
that has 107-bit mantissae in doubles? I can find a few references to long
doubles with 107-bit mantissae, but I've drawn a blank on doubles.

I'm thinking of a special compilation mode on SGI IRIX that automagically
converts coded double into what would otherwise require a coded
long double. A bit of a cheat perhaps, but a "system" for compilation
purposes includes the compiler flags: the -code- would say "double"
and the compiler would do the arithmetic with 107 bit mantissae.
Odd, but standard conforming if __STDC_IEC_559__ is not defined.
 
R

Richard Heathfield

jacob navia said:
That wasn't what the OP asked. He wanted to round it without any
display, maybe to be used in further calculation or whatever.

And it was demonstrated that this can't be done in general, so his
requirement cannot be met in general.
And it IS possible to round correctly a double precision
number.

Show me 0.33 rounded to one decimal place.
One of the possible solutions is:

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

void RoundMyDouble (long double *value, short numberOfPrecisions)

{
long double fv=fabsl(*value);
if (fv > 1e17)
return ;
long long p = powl(10.0L, numberOfPrecisions);
*value = (long long)(*value * p + 0.5L) / (double)p;
}

This is quite awful, but it works (with 64 bit long long and
wider precision long double)

For 0.33 rounded to one decimal place with this code of yours, I get a
result of 0.300000000000000000011, which is clearly wrong (it should be
0.3).
 
R

Richard Heathfield

jacob navia said:

Maybe Mr Robertson was speaking about lcc-win?

Who is Mr Robertson? Nobody by that name has posted any article in this
thread AFAIK.
Much more fun would be to answer the original question.

Excuse me nbut you REALLY believe that it is impossible to write in
standard C

double roundto(double n,int digits);

????

Of course you can write a function by that name and with that signature if
you like - but it won't do what the OP requires.
 
J

James Kuyper

jacob said:
But he was asking for a function that returns a double, not
asking about PRINTING a double.

OK - so my hint WAS too obscure. Think about it a little while before
you respond again. I'm sure you can figure it out with a little extra
thought. In particular, think about the implications of the fact that I
specified sprintf() rather than printf(), and sscanf() instead of scanf().

Incidentally, the sprintf()/sscanf() combination is unnecessarily
inefficient. One loop writes to a buffer, and another loop reads from
it, and both loops spends time dealing with possibilities that don't
apply in this context. It's possible to remove the buffer by extracting
the loops from from sprintf() and sscanf(), simplifying them, and
connecting them directly together. However, that's a significantly more
complicated solution, and I wouldn't recommend attempting it unless you
do in-memory rounding to a specified number of decimal digits
frequently. I can't remember ever needing that capability. I round to a
specified number of decimal digits only in my outputs.
Or you are saying that you belive this is impossible
in C???

I wouldn't have provided a hint how to solve the problem, if I'd thought
that solving the problem was impossible. I'm not a sadist.
 
R

Richard Heathfield

jacob navia said:
OK That's much better tone Heathfield.

You are in no position to judge what constitutes "better tone".
Let's calm down.

Sounds good to me. Now all we need is your sincere apology for the
innumerable times you've hurled abuse at me for correcting your errors.

Now, are we really such a C "heads" that we can't answer the OP
question IN HIS terms?

I've already answered the question correctly. What he wants cannot be done,
but I've explained how he can get the next best thing.
double roundto(double d,int digits);

I use more precision than double, test for out of range, and give the
answer. That's a solution, but I am sure you can bring a better one.

Yes, I can come up with a correct solution, which is to sort out the
rounding at display time - but I already posted that solution, upthread.
 
J

jacob navia

Richard said:
Show me 0.33 rounded to one decimal place.


For 0.33 rounded to one decimal place with this code of yours, I get a
result of 0.300000000000000000011, which is clearly wrong (it should be
0.3).

This is perfectly OK since double has only 16 decimal digits,
and the first ones in your result appear at 1e-21 (if I counted
correctly). Those values can't be accurately represented with
double precision.

You can't go beyond 1e-16, since DBL_DIG is 15.

Writing it in in a more sensible way:

double roundto(long double value, unsigned digits)
{
long double fv=fabsl(value);
if (fv > powl(10.0L,DBL_DIG) || digits > DBL_DIG)
return value; // Out of range
long long p = powl(10.0L, digits);
return roundl(value * p ) / (double)p;
}
 
J

jacob navia

James said:
OK - so my hint WAS too obscure. Think about it a little while before
you respond again. I'm sure you can figure it out with a little extra
thought. In particular, think about the implications of the fact that I
specified sprintf() rather than printf(), and sscanf() instead of scanf().

Incidentally, the sprintf()/sscanf() combination is unnecessarily
inefficient. One loop writes to a buffer, and another loop reads from
it, and both loops spends time dealing with possibilities that don't
apply in this context. It's possible to remove the buffer by extracting
the loops from from sprintf() and sscanf(), simplifying them, and
connecting them directly together. However, that's a significantly more
complicated solution, and I wouldn't recommend attempting it unless you
do in-memory rounding to a specified number of decimal digits
frequently. I can't remember ever needing that capability. I round to a
specified number of decimal digits only in my outputs.


I wouldn't have provided a hint how to solve the problem, if I'd thought
that solving the problem was impossible. I'm not a sadist.

double roundto(long double value, unsigned digits)
{
long double fv=fabsl(value);
if (fv > powl(10.0L,DBL_DIG) || digits > DBL_DIG)
return value; // Out of range
long long p = powl(10.0L, digits);
return roundl(value * p ) / (double)p;
}

Supposing long double is extended precision, why should this not
work?
 
W

Walter Roberson

Now, are we really such a C "heads" that we can't answer the OP
question IN HIS terms?
double roundto(double d,int digits);

Richard already posted a long article showing that NO, it cannot
be done.

Consider the value 1/10th. The exact representation is
binary 0. followed by (00011) repeated infinitely. If you
truncate that infinite sequence anywhere, you are going to get a value
that is slightly less than 1/10th. If you round some 00011 to 001
or 01 or 1 then you are going to get a value that is slightly more
than 1/10th. Therefore no matter what kind of arithmetic manipulations
you do, if you round to "one digit after the decimal point"
then the binary value that is stored is always going to be
slightly lower or slightly higher than the required .1 .

You simply might not see the difference unless you print out
the value to sufficient decimal places (e.g., %24.19g)
 
F

Flash Gordon

jacob navia wrote, On 22/11/07 23:01:
This is perfectly OK

It does not meet the OPs requirements as specified.
since double has only 16 decimal digits,
and the first ones in your result appear at 1e-21 (if I counted
correctly). Those values can't be accurately represented with
double precision.

<snip>

Which is precisely the problem. If it is wanted for display then there
are better ways, if it is wanted for further calculations then it is
very important that the OP understand why it is not possible in general.
 
J

jacob navia

Walter said:
Richard already posted a long article showing that NO, it cannot
be done.

Consider the value 1/10th. The exact representation is
binary 0. followed by (00011) repeated infinitely. If you
truncate that infinite sequence anywhere, you are going to get a value
that is slightly less than 1/10th. If you round some 00011 to 001
or 01 or 1 then you are going to get a value that is slightly more
than 1/10th. Therefore no matter what kind of arithmetic manipulations
you do, if you round to "one digit after the decimal point"
then the binary value that is stored is always going to be
slightly lower or slightly higher than the required .1 .

You simply might not see the difference unless you print out
the value to sufficient decimal places (e.g., %24.19g)

Of course, and so what? Obviously numbers are written in
binary, but we can do the dismissed quantity very
small. That means rounding to the nth digit.

If we use higher precision in the calculations, and only round at the
end, we get the best possible result for that rounding.

Unrepresentable numbers in floating point will not go away
but we can get the BEST approximation.

Besides, as you know perfectly well, most printfs are
written in C, and they do the same I am doing there
basically.
 

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

Forum statistics

Threads
473,780
Messages
2,569,611
Members
45,281
Latest member
Pedroaciny

Latest Threads

Top