#### Do you have a question? Post it now! No Registration Necessary. Now with pictures!

## Re: Rounding issue

Hello,

why isn't it stored as 0.150000000000000 internally?

This is explains why 0.149 doesn't round to 0.2, but it doesn't explain

why 1.5 works different than 0.15 wuth just one floating point digit

difference.

In this specific case I hardcoded it into the source. But in the real

application it is coming from a database and then some tax calculations

are done and in accounting every cent difference causes headaches.

Why does

perl -e 'print sprintf("%.1f", 0.10 + 0.05). "\n"'

work fine?

Kind regards

Marten

## Re: Rounding issue

Please re-read your notes from "Basics of Computer Numerics".

Hint: Typical computers are binary, they don't use the decimal system.

That is why using floating point numbers for accounting is A VERY Bad

Idea(TM) and very strongly discouraged. Didn't your computer numerics

teacher tell you so?

Again: print the result of that addition with 20 digits and you will see

why.

jue

## Re: Rounding issue

Hello,

although I know about the difference between an internal representation

and the number itself, I don't want to care about that the whole day.

All I'm expecting is the programming language to remember the precision

I used when a value was stored. 1.5 is not an infinite value as 1/3.

Kind regards

Marten

## Re: Rounding issue

*Marten> All I'm expecting is the programming language to remember the*

*Marten> precision I used when a value was stored. 1.5 is not an infinite value*

*Marten> as 1/3.*

But it is. The moment 0.15 is "stored", it's a truncation of an infinite

binary value, and is therefore not precisely 0.15 any more.

(And actually, 1.5

***can***be stored precisely, so you must've gotten mixed up

in this thread.)

print "Just another Perl hacker,"; # the original

--

Randal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095

Smalltalk/Perl/Unix consulting, Technical writing, Comedy, etc. etc.

See http://methodsandmessages.vox.com/ for Smalltalk and Seaside discussion

## Re: Rounding issue

Tough. That's the price for using floating point. If you don't want to

pay it, don't use floating point.

That concept is called 'bignums'. Perl supports bignums through the use

of the bignum pragma. They are (substantially) slower than native

floats, and cannot easily be passed to functions like sprintf that

expect a float. Nevertheless, feel free to use them if you like.

1.5 has a finite decimal representation in both base 10 and base 2.

0.15 does not.

Ben

## Re: Rounding issue

Then you shouldn't do numeric work.

You'll be disappointed. Almost no programming language does this. At

least not for basic numeric types. Some programming languages may offer

a "rational" type (Perl has "BigRat").

You mean "periodic", not "infinite". Infinite is for example the number

of natural numbers.

1/3 is a periodic number in base 10 or base 2.

It is not a periodic number in base 3.

1/10 is a periodic number in base 2 or base 3.

It is not a periodic number in base 10.

1/2 is a periodic number in base 3.

It is not a periodic number in base 2 or 10.

hp

## Re: Rounding issue

Then you better don't use floating point numbers;-)

And you don't have the problem with 1.5, you have it with 0.15,

which is a number that requires an infinite number of bits to

be represented exactly in binary...

It's not a question of the programming language - you will have

the same problem in all programming languages that allow you

to use floating point numbers. The first thing is that the same

problems also appears if you represent numbers in base 10 -

1/3 is the simplest example - you're used to it and thus don't

wonder what's going on. Of course, you can use Perl (or any

other language) to represent numbers in a 10 based system

(and some processors even have some kind of support for it,

see for example BCD (binary coded decimals)). But you then

have to write functions for all the arithmetic operators

printing etc. etc. and the computation speed will probably

be rather abysimal.

But the fundamental problem remains. You have an infinite

number of floating point numbers that must be represented

with a limited number of bits. Thus except for an infinitely

small fraction of them you have just approximations. And

once you have only an approximation there's no going back to

the exact number you started of with.

Asking for 0.1 + 0.05 to be output after rounding as "0.2"

when the numbers are represented in binary is exactly the

same as asking for 2.0/6.0 + 1.0/6.0 to be output as "1"

after rounding when using a base 10 representation with a

limited number of digits - it simply can't be done. That

you accept the problem when using base 10 but not when

using base 2 is just a result of being used to the limi-

tations of base 10 but less to that of base 2.

Regards, Jens

--

\ Jens Thoms Toerring ___ jt@toerring.de

\__________________________ http://toerring.de

## Re: Rounding issue

Because it is not represented in base 10, it is represented in base 2 (binary).

Using floating point numbers.

Do not work with floating point dollars, instead work with

integer cents (then convert to dollars only at the last output stage).

--

Tad McClellan

email: perl -le "print scalar reverse qq/moc.noitatibaher0cmdat/"

## Re: Rounding issue

Because 0.15 is not representable in (finite) binary floating point.

(This is the same as asking why 1/3 is not exactly representable as a

percentage, except the numbers are different because computers use base

2 instead of base 10.)

1.5 and 0.15 do not look particularly similar in binary. 1.5 is '1.1',

0.15 is a recurring 'binimal' that starts '0.001001100110011...'.

Then don't use floating point. Store all your amounts in cents, and do

the necessary rounding yourself. I would expect that if this is a

financial application there are some very carefully-specified rounding

rules you must follow: you need to know what these are.

~% perl -E'say sprintf "%0.40f\n%0.40f\n%0.40f", 0.1, 0.05, 0.1+0.05'

0.1000000000000000055511151231257827021182

0.0500000000000000027755575615628913510591

0.1500000000000000222044604925031308084726

You could perfectly well have answered that question yourself.

Ben

## Re: Rounding issue

Floating point numbers are stored as binary numbers. And, just

like you can't write a lot of numbers in a 10 based system

(e.g. one third) with a limited number of digits, you can't

store a lot of numbers in a binary system with a limited

number of bits. Thus the overwhelming majority of floating

point numbers are only approximations. For example 0.1 looks

like a simple number when written in a 10 based number system,

but when you would try to write it in a 2 based system you

would need an infinite number of bits. On the other hand,

a number like 1.0/3.0 would require an infinite number of

digits when written in a 10 based system but would be a

very "simple" number when you would write in base 3. (In

base 10 all rational numbers that are a fraction with the

denominator being the product of a power of 2 and a power

of 5 can be written with a limited number of digits, in

base 2 only those that have a power of 2 as the denominator.)

Thus, 1.5 can be stored precisely in base 2 with the limited

number of bits you have for a floating point number while

0.15 can't. Thus 0.15 can only be represented by an appro-

ximation which then leads to the problem you observed.

Here a further problem with using floating point numbers

is at work. Neither 0.1 nor 0.05 can be stored precisely.

So you are already starting of with imprecise numbers. And

when you add those imprecise numbers the errors can add up!

And the more calculations you do the larger the error can

become.

In your example one result is that 0.15 isn't equal to

0.1 + 0.05. Thus a rule of thumb is never to try to

compare floating point numbers for equality since the

result hardly ever is what you expect.

But even with numbers that can be represented exactly there

can be problems. On my machine I get

jens@cm:~$ perl -e 'print sprintf("%.1f", 0.25). "\n"'

0.2

jens@cm:~$ perl -e 'print sprintf("%.1f", 0.75). "\n"'

0.8

While the 0.8 result for 0.75 is what one would expect,

the 0.2 for 0.25 isn't. The explanation is probably that

it's the result of rounding errors introduced when the

digits to be displayed are calculated.

If you want the precision you're looking for you probably

shouldn't use floating point numbers at all! If you want

e.g. 2 digits after the decimal point then "scale" your

calculations by a factor of 100, so that everything can

be done with integers.

Welcome to the wonderful world of floating point calculations;-)

Regards, Jens

--

\ Jens Thoms Toerring ___ jt@toerring.de

\__________________________ http://toerring.de

## Re: Rounding issue

Hello,

maybe I should change the question to:

How can I force Perl to process certain calculations using decimals?

There are special datatypes in Python and databases. Is there any in Perl?

Ben proposed using the bignums pragma, but I'm afraid that this breaks

something else in the code.

Kind regards

Marten

## Re: Rounding issue

If you use just ints you should be fine since there aren't any

rounding errors. There's no built-in BCD type or similar. But

there seems to be rather new module

http://search.cpan.org/~zefram/Math-Decimal-0.001/lib/Math/Decimal.pm

for doing decimal arithmetic. Or have a look at

http://search.cpan.org/~tels/Math-BigRat-0.22/lib/Math/BigRat.pm

if you want as much precision as the amount of memory in your

machine allows (note: I didn't use any of these modules, so

I can't tell how well they work).

That you will need if the numbers you use ints and end up with

are too large to be stored in a simple int.

But what will do for you (if it's possible at all) depends on

what exactly you want to do. Until now all we have seen is a

few one-liners, what you need it for is still unclear.

Regards, Jens

--

\ Jens Thoms Toerring ___ jt@toerring.de

\__________________________ http://toerring.de

## Re: Rounding issue

Hello,

I want to do financial calculations (nothing special, just simple things

like adding items of an invoice, adding taxes, removing disagio from

creditcard transactions etc.) and I want to be sure of correct results.

So if a total with tax is $0.015, then I need it to be rounded to $0.02

and not $0.01. Therefor, numbers need to be represented as they are and

not the typical internal representation. I don't want to use cents as

integers. Even cents will get odd numbers after the decimal point if you

have to add e.g. 19% value added taxes. And even worser: The whole code

would get bloated and the original intention of the code would be harder

to understand if the whole source is full of "* 100" and "/ 100". In

databases it is easy to define a decimal like (10,2): 10 digits before

the point, 2 after it. It would be great to have this directly in Perl

so that sprintf and other functions can still work with such values and

variables, not only specific modules like Math::Decimal.

Regards

Marten

## Re: Rounding issue

I don't understand your big concern. Anytime you see numbers, decimal

points, or anything printed in a view from a computer is a rounded

representation of what the internal variable contains.

Its just a view, its a one way snapshot of the contents of the variable.

That doesen't affect the internal calculations. Indeed for percentage

calculations, there is absolutely nothing wrong with floating point

at all.

When you have to display (have a view) where you think its necessary

to show a correct image of the results of percentages as fractions of

dollars (cents) then use a rounding translation that everybody and thier

uncle uses.

Why is it so hard?

sub getFmtRound

{

my ($val,$width) = @_;

my $fmt = "%.$width"."f";

return sprintf $fmt,(($val

*** 10***

***($width+1))+.5)/(10****($width+1));

}

-sln

## Re: Rounding issue

sln@netherlands.com wrote:

) I don't understand your big concern. Anytime you see numbers, decimal

) points, or anything printed in a view from a computer is a rounded

) representation of what the internal variable contains.

No it isn't. It can also be an exact representation. For example,

if the numbers are integers or some kind of decimal format.

) Its just a view, its a one way snapshot of the contents of the variable.

) That doesen't affect the internal calculations. Indeed for percentage

) calculations, there is absolutely nothing wrong with floating point

) at all.

Yes there is. Percentage calculations are fixed point, and to get exact

results, you need to be doing them in an exact representation such as

integers or rationals.

) When you have to display (have a view) where you think its necessary

) to show a correct image of the results of percentages as fractions of

) dollars (cents) then use a rounding translation that everybody and thier

) uncle uses.

If you use floating point internally, then the display result will not

always be exact.

) Why is it so hard?

Because people think too easy of it. Such as you.

SaSW, Willem

--

Disclaimer: I am in no way responsible for any of the statements

made in the above text. For all I know I might be

drugged or something..

No I'm not paranoid. You all think I'm paranoid, don't you !

#EOT

#### Site Timeline

- » search for hex characters in a binary file and remove them
- — Next thread in » PERL Discussions

- » log timestamp a script is used
- — Previous thread in » PERL Discussions

- » s suffix question
- — Newest thread in » PERL Discussions

- » Dell Battery Slice LED codes
- — The site's Newest Thread. Posted in » Laptop Computers Forum