sprintf rouding error

B

Broki

Hello,
I could observe something strange under different versions of perl
[ActiveState 5.6,5.8] and CPUs[P4,CoreDuo].
(both under WIN XP SP2)

Example:
x=1.5;sprintf("%.0f",$x);->2 [ok]
BUT
$x=5830*1.15; ->the calculated 6704.5 gets rounded down to 6704 ![not
okay].

Is this a known problem?!

Götz
 
B

Broki

Hello,
I could observe something strange under different versions of perl
[ActiveState 5.6,5.8] and CPUs[P4,CoreDuo].
(both under WIN XP SP2)

Example:
x=1.5;sprintf("%.0f",$x);->2 [ok]
BUT
$x=5830*1.15; ->the calculated 6704.5 gets rounded down to 6704 ![not
okay].

Is this a known problem?!

Götz

I found the answer with google.
Perl uses an biased rounding function.
at .5 50%up - 50% down.

I am surprised.
Its an IEEE rule.
 
B

Ben Morrow

Quoth (e-mail address removed):
Example:
x=1.5;sprintf("%.0f",$x);->2 [ok]
BUT
$x=5830*1.15; ->the calculated 6704.5 gets rounded down to 6704 ![not
okay].

Is this a known problem?!

Known, but not a problem. Perl's default float-to-string conversion uses
DBL_DIG (from your system's <float.h>) digits of precision, as this is
all that is guaranteed to be accurate. However, the number actually has
more precision than that, which can mean it rounds differently from the
way you would expect. On my system, DBL_DIG is 15, and I get

~% perl -le'print sprintf $_, 5830*1.15 for qw/%s %f %.0f %.20f/'
6704.5
6704.500000
6704
6704.49999999999909050530

so the value calculated is actually a smidge smaller than 6704.5, and so
rounds down.

Ben
 
J

Jürgen Exner

x=1.5;sprintf("%.0f",$x);->2 [ok]
BUT
$x=5830*1.15; ->the calculated 6704.5 gets rounded down to 6704 ![not
okay].

I found the answer with google.
Perl uses an biased rounding function.
at .5 50%up - 50% down.

Actually no. Try printing your number with e.g. 40 digits:
printf '%.40f', $x;
and you will see that you fell for the oldest problem in computer numerics.

"Thou shalt not use floating point if thou expect exact results"

For a brief explanation why see "perldoc -q 999". For a comprehensive answer
see the introductory class in Computer Numerics at your favourite
university.

jue
 
P

Peter J. Holzer

I could observe something strange under different versions of perl
[ActiveState 5.6,5.8] and CPUs[P4,CoreDuo].
(both under WIN XP SP2)

Example:
x=1.5;sprintf("%.0f",$x);->2 [ok]
BUT
$x=5830*1.15; ->the calculated 6704.5 gets rounded down to 6704 ![not
okay].

1.15 is not representable exactly in binary floating point (just like
1/3 isn't exactly representable in decimal floating point), so since you
don't multiply 5830 by exactly 1.15, the result won't be exactly 6704.5
- it will be slightly more or slightly less and this number will then be
rounded correctly. You get more accurate results if you avoid decimals:
5830*115/100 is exactly 6704.5.

Incidentally, the correct rounding of 6704.5 *is* 6704, so in this case
the result is what you should expect.

I found the answer with google.
Perl uses an biased rounding function.

You mean "non-biased" (unlike the rounding rule you learn in school
which is biased).
at .5 50%up - 50% down.

Be careful here. ".5" in english-speaking countries means "0.5", so what
you wrote is wrong: 0.5 is always rounded down because 0 is even. I
guess you meant that half of the numbers n + 0.5 where n is an integer
get rounded up and half of them get rounded down. That's correct.

I am surprised. Its an IEEE rule.

You should read the newsgroup you are posting to - this was discussed in
another thread only a few days ago.

hp
 
B

Broki

Thanks a lot for your answers!
I will see how I can solve this problem.
The calculation is just for price calculation and I think factor that
lead
to correct floating point values are tather probable.

GM



I could observe something strange under different versions of perl
[ActiveState 5.6,5.8] and CPUs[P4,CoreDuo].
(both under WIN XP SP2)
Example:
x=1.5;sprintf("%.0f",$x);->2 [ok]
BUT
$x=5830*1.15; ->the calculated 6704.5 gets rounded down to 6704 ![not
okay].

1.15 is not representable exactly in binary floating point (just like
1/3 isn't exactly representable in decimal floating point), so since you
don't multiply 5830 by exactly 1.15, the result won't be exactly 6704.5
- it will be slightly more or slightly less and this number will then be
rounded correctly. You get more accurate results if you avoid decimals:
5830*115/100 is exactly 6704.5.

Incidentally, the correct rounding of 6704.5 *is* 6704, so in this case
the result is what you should expect.
I found the answer with google.
Perl uses an biased rounding function.

You mean "non-biased" (unlike the rounding rule you learn in school
which is biased).
at .5 50%up - 50% down.

Be careful here. ".5" in english-speaking countries means "0.5", so what
you wrote is wrong: 0.5 is always rounded down because 0 is even. I
guess you meant that half of the numbers n + 0.5 where n is an integer
get rounded up and half of them get rounded down. That's correct.
I am surprised. Its an IEEE rule.

You should read the newsgroup you are posting to - this was discussed in
another thread only a few days ago.

hp
 
P

Peter J. Holzer

Thanks a lot for your answers!
I will see how I can solve this problem.
The calculation is just for price calculation and I think factor that
lead to correct floating point values are tather probable.

A common technique in financial computations is to avoid decimal places.
Don't store your prices in Euros, store them in Cent. If you need
percentages, store them as they are, don't divide them by 100 (i.e. "20
%" is "20" not "0.20")[0]. That way you can store almost always the exact
amount and not an approximation. I learned that 22 years ago from a tax
advisor who was writing his own software in BASIC :).

hp

[0] Incidentally banks like to use interest rates which are a multiple
of 1/8: 3.625 is representable exactly in binary FP, while 3.6 is
not. I doubt this is caused by the software they use (it's probably
written in COBOL anyway, which uses decimal arithmetic), but it
it has the nice property that you can use these values in binary
arithmetic without worrying about rounding errors in the
representation.
 
B

Bart Lateur

x=1.5;sprintf("%.0f",$x);->2 [ok]
BUT
$x=5830*1.15; ->the calculated 6704.5 gets rounded down to 6704 ![not
okay].

Is this a known problem?!

It is caused by the fact that in binary presentation, 1.15 is not
exactly the same as 115/100. Just like 1/3 is not exactly the same as
0.333333333... There's a limited number of bits available to represent
that number, and it's not enough for an exact representation -- you'd
need an infinite number of bits, because 5 is not a power of 2. Just
like 3 is not a power of 10.
 
A

Alex

Isn't that explained on
under 'Round-to-even method'?

Well, yes (and I confess I didn't even read so far at first), but given
no context at all, I'm fairly certain that mathematical rounding
defaults to the common method, which yields 6705. At least, this is how
I was taught in school.

Alex
 
J

Joost Diepenmaat

Alex said:
Well, yes (and I confess I didn't even read so far at first), but
given no context at all, I'm fairly certain that mathematical rounding
defaults to the common method, which yields 6705. At least, this is
how I was taught in school.

I was also taught this rule in school. I am pretty certain round-to-even
is more common at least for statistical calculations.

From that wiki page:

History

The Round-to-even method has been the ASTM (E-29) standard since
1940. The origin of the terms unbiased rounding and statistician's
rounding are fairly self-explanatory. In the 1906 4th edition of
Probability and Theory of Errors [1] Robert Woodward called this "the
computer's rule" indicating that it was then in common use by human
computers who calculated mathematical tables. Churchill Eisenhart's 1947
paper "Effects of Rounding or Grouping Data" (in Selected Techniques of
Statistical Analysis, McGrawHill, 1947, Eisenhart, Hastay, and Wallis,
editors) indicated that the practice was already "well established" in
data analysis.

Joost.
 
A

Alex

I was also taught this rule in school. I am pretty certain round-to-even
is more common at least for statistical calculations.

Which is a specific application area and, as such, irrelevant to the
case. The OP stated clearly that rounding 6704.5 down was an error,
therefore indicating (which is the default *anyway*) that the common
method is the method of choice.


Alex
 
J

Joost Diepenmaat

Alex said:
Which is a specific application area and, as such, irrelevant to the
case. The OP stated clearly that rounding 6704.5 down was an error,
therefore indicating (which is the default *anyway*) that the common
method is the method of choice.

Well, *mathematics* is a specific application area. Most computer
programs aren't about math per se.

Round-to-even is what I expect from floating point rounding on a
computer, unless the language specifies otherwise, since it's the
default IEEEE rounding.

I don't see where in this thread it's made clear that rounding down is
an error, though it may well be for the OP's program. I only see that
the result is unexpected, so I tried to explain a bit more about the
reasoning behind this unexpected rounding.

Joost.
 
B

Broki

Thanks for your replies.
To reduce the problem I will try to use expressions like 115/100 than
1.15.
The rounded down 6704 is an error in the eyes of my boss - so it´s not
only unexpected - it´s an error for me.

What about the IEEE rounding and Perl...is it a fact that the
"uncorrectness" with floating point conversion leads to the
statistically interesting way to round 50% up/ 50% down?

Goetz
 
J

Joost Diepenmaat

Thanks for your replies.
To reduce the problem I will try to use expressions like 115/100 than
1.15.

If I understand you correctly, I think that won't make any
difference. I'm sure it won't once you assign 115/100 to a variable;
perl doesn't handle rational numbers natively. You may want to use the
(math)bigrat modules if you want *EXACT* results (provided ofcourse that
your calculations use rational numbers only).

If you mean to use cents or mils as the base unit (you mention currency
in one of your posts) for all calculations that will help, since that
way at least you can exactly specify prices in that unit without
floating point errors. You should still mind divisions or multiplying
with floats.
The rounded down 6704 is an error in the eyes of my boss - so it´s not
only unexpected - it´s an error for me.

int($val+0.5) should do what you want for positive numbers, I guess. you
may also want to take a look at POSIX's ceil(), and Math::Round on CPAN.

Does your boss wants you to round -1.5 to -2 or -1?
What about the IEEE rounding and Perl...is it a fact that the
"uncorrectness" with floating point conversion leads to the
statistically interesting way to round 50% up/ 50% down?

For some sets of data, yes. It obviously won't work that way for floats
in the range 0 .. 1.

See also Math::Round's round_rand.

Joost.
 
P

Peter J. Holzer

If I understand you correctly, I think that won't make any
difference.

It does in many cases, if used correctly.
I'm sure it won't once you assign 115/100 to a variable;

That's the incorrect way :).

What I meant when I suggested that (and I think I already wrote that,
but I probably wasn't clear enough), is to use terms like 115 / 100 in
an expression:


my $x = 5830;
my $y = $x * 115 / 100;

will first compute the product of 5830 and 115 (which is 670450) and
then divide it by 100. The result is exactly 6704.5.

my $y = $x * (115 / 100);

will first compute 115 / 100: This is exactly representable in a finite
binary number, so it is rounded (to 53 binary digits, usually). Then $x
is divided by the rounded number, which is of course only approximately
6704.5.

Variations like

my $y = $x * 1.15;

or

my $z = 115 / 100;
my $y = $x * $z;

do the same, so they produce the same error.

[...]
For some sets of data, yes.

If I understood the question correctly, no. The "statistically
interesting way to round" is just a way to deal with the errors
introduced by rounding in general. As the Wikipedia article mentions, it
was already the recommended way of rounding in 1906 - long before
computers. (Although it is true that if you used infinitely precise
numbers and if your functions were all continous, the error would be
infinitely small - so in a way it is the "uncorrectness of floating
point numbers" which is the reason for the rule).


hp
 
B

Broki

Thanks again and again.
Another programmer gave me the hint to multiply with 100 (calculate in
cents)
and then divide and multiply only with integers, one after another.
(its the same as the hint of Mr. Holzer)
There are so many answers but as far as I can see, the problem is
minimized but not
solved completely due to the nature of at least the floating point
RESULT.
The programmer tried the same with Delphi an got correct result.
So I guess something like a library (like Math::Round) is used there
by default.

So, I have good advices for the future and see this case as closed for
my
point of view.

Thanks to the community!


Götz


If I understand you correctly, I think that won't make any
difference.

It does in many cases, if used correctly.
I'm sure it won't once you assign 115/100 to a variable;

That's the incorrect way :).

What I meant when I suggested that (and I think I already wrote that,
but I probably wasn't clear enough), is to use terms like 115 / 100 in
an expression:

my $x = 5830;
my $y = $x * 115 / 100;

will first compute the product of 5830 and 115 (which is 670450) and
then divide it by 100. The result is exactly 6704.5.

my $y = $x * (115 / 100);

will first compute 115 / 100: This is exactly representable in a finite
binary number, so it is rounded (to 53 binary digits, usually). Then $x
is divided by the rounded number, which is of course only approximately
6704.5.

Variations like

my $y = $x * 1.15;

or

my $z = 115 / 100;
my $y = $x * $z;

do the same, so they produce the same error.

[...]
For some sets of data, yes.

If I understood the question correctly, no. The "statistically
interesting way to round" is just a way to deal with the errors
introduced by rounding in general. As the Wikipedia article mentions, it
was already the recommended way of rounding in 1906 - long before
computers. (Although it is true that if you used infinitely precise
numbers and if your functions were all continous, the error would be
infinitely small - so in a way it is the "uncorrectness of floating
point numbers" which is the reason for the rule).

        hp
 
J

Jürgen Exner

Another programmer gave me the hint to multiply with 100 (calculate in
cents)
and then divide and multiply only with integers, one after another.
(its the same as the hint of Mr. Holzer)
There are so many answers but as far as I can see, the problem is
minimized but not
solved completely due to the nature of at least the floating point
RESULT.

There is a reason why Numerics is a subject of its own right in computer
science.

jue
 
T

Ted Zlatanov

JE> There is a reason why Numerics is a subject of its own right in
JE> computer science.

Yes, it's so students can avoid it specifically by name :)

Ted
 

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
474,209
Messages
2,571,086
Members
47,684
Latest member
Rashi Yadav

Latest Threads

Top