Home All Groups Group Topic Archive Search About

When double precision isn't very precise

Author
19 May 2009 11:23 PM
Peter B. Steiger
I wrote a quick-and-dirty accounting routine that added up charges and
payments to show a running balance on someone's ledger.  Our QA team
reported that it is producing the wrong balances, and sure enough it was.
The answers were coming back with umpteen decimal places written in
scientific notation, e.g. 1.23456E-9.

I thought I'd play it smart and wrote a function to do all the work in
integers - it receives the running balance and new amount, multiplies
them by 100 into integer variables, adds the two integers, and divides
the result by 100.  It's stupid that I have to do this because our state-
of-the-art computer can't add two-place numbers together and come up with
the right answer, but at least it was an easy enough kludge.

Now it turns out it's also converting to integers wrong. For example,
with a balance of $595.17 added to $80.00, the new balance is $675.16
because it multiplied 595.17 * 100 and came up with... 59516.

The balance and amount are both double precision, so I did some testing:
Module FloatTest

  Sub Main()

    Dim nFloat1 As Single

    Dim nFloat2 As Double

    Dim nInt1 As Integer

    Dim nInt2 As Integer



    nFloat1 = 595.17

    nInt1 = Int(nFloat1 * 100)

    Console.WriteLine("Int of single " & nFloat1 & " * 100 is " & nInt1)

    nFloat2 = 595.17

    nInt2 = Int(nFloat2 * 100)

    Console.WriteLine("Int of double " & nFloat2 & " * 100 is " & nInt2)

  End Sub

End Module


The results?  Multiply a single-precision number with two decimal places
by 100, and you get the right answer (in this case, 59517).  Multiply a
double precision number by 100 and you get the wrong answer (59516).

I know FP math produced weird results on old CPUs (8086 through 80486),
but I would have thought for sure some processor genius would have fixed
the problem by now.  Is this a known bug, or am I using the wrong data
types for my purposes, or what?

--
Peter B. Steiger
Cheyenne, WY
If you must reply by email, you can reach me by placing zeroes
where you see stars: wypbs.**1 at gmail.com.

Author
19 May 2009 11:40 PM
OmegaSquared
Hello, Peter,

No, this is not a bug, but for floating point numbers this behaviour that
you are seeing is by design.  Please see:

http://docs.sun.com/source/806-3568/ncg_goldberg.html

for a detailed explanation.

The data type that you probably want to be using is Decimal.

Cheers,
Randy


Show quoteHide quote
"Peter B. Steiger" wrote:

> I wrote a quick-and-dirty accounting routine that added up charges and
> payments to show a running balance on someone's ledger.  Our QA team
> reported that it is producing the wrong balances, and sure enough it was.
> The answers were coming back with umpteen decimal places written in
> scientific notation, e.g. 1.23456E-9.
>
> I thought I'd play it smart and wrote a function to do all the work in
> integers - it receives the running balance and new amount, multiplies
> them by 100 into integer variables, adds the two integers, and divides
> the result by 100.  It's stupid that I have to do this because our state-
> of-the-art computer can't add two-place numbers together and come up with
> the right answer, but at least it was an easy enough kludge.
>
> Now it turns out it's also converting to integers wrong. For example,
> with a balance of $595.17 added to $80.00, the new balance is $675.16
> because it multiplied 595.17 * 100 and came up with... 59516.
>
> The balance and amount are both double precision, so I did some testing:
> Module FloatTest

>   Sub Main()

>     Dim nFloat1 As Single

>     Dim nFloat2 As Double

>     Dim nInt1 As Integer

>     Dim nInt2 As Integer

>

>     nFloat1 = 595.17

>     nInt1 = Int(nFloat1 * 100)

>     Console.WriteLine("Int of single " & nFloat1 & " * 100 is " & nInt1)

>     nFloat2 = 595.17

>     nInt2 = Int(nFloat2 * 100)

>     Console.WriteLine("Int of double " & nFloat2 & " * 100 is " & nInt2)

>   End Sub

> End Module

>
> The results?  Multiply a single-precision number with two decimal places
> by 100, and you get the right answer (in this case, 59517).  Multiply a
> double precision number by 100 and you get the wrong answer (59516).
>
> I know FP math produced weird results on old CPUs (8086 through 80486),
> but I would have thought for sure some processor genius would have fixed
> the problem by now.  Is this a known bug, or am I using the wrong data
> types for my purposes, or what?
>
> --
> Peter B. Steiger
> Cheyenne, WY
> If you must reply by email, you can reach me by placing zeroes
> where you see stars: wypbs.**1 at gmail.com.
>
Author
20 May 2009 6:25 PM
Peter B. Steiger
On Tue, 19 May 2009 16:40:01 -0700, OmegaSquared sez:

> No, this is not a bug, but for floating point numbers this behaviour
> that you are seeing is by design.  Please see:
>
> http://docs.sun.com/source/806-3568/ncg_goldberg.html
>
> for a detailed explanation.
>
> The data type that you probably want to be using is Decimal.

Thanks!  I'll certainly make the change... but that article didn't do
much to convince me "that's not a bug, it's a feature."  If single- or
double-precision floating point variables don't perform basic arithmetic
correctly, what IS an appropriate (and reliable) use for them?

--
Peter B. Steiger
Cheyenne, WY
If you must reply by email, you can reach me by placing zeroes
where you see stars: wypbs.**1 at gmail.com.
Author
20 May 2009 11:05 PM
Michael D. Ober
Show quote Hide quote
"Peter B. Steiger" <see.sig@for.email.address> wrote in message
news:3eydnWCzoONg14nXnZ2dnUVZ_jJi4p2d@bresnan.com...
> On Tue, 19 May 2009 16:40:01 -0700, OmegaSquared sez:
>
>> No, this is not a bug, but for floating point numbers this behaviour
>> that you are seeing is by design.  Please see:
>>
>> http://docs.sun.com/source/806-3568/ncg_goldberg.html
>>
>> for a detailed explanation.
>>
>> The data type that you probably want to be using is Decimal.
>
> Thanks!  I'll certainly make the change... but that article didn't do
> much to convince me "that's not a bug, it's a feature."  If single- or
> double-precision floating point variables don't perform basic arithmetic
> correctly, what IS an appropriate (and reliable) use for them?
>
> --
> Peter B. Steiger
> Cheyenne, WY
> If you must reply by email, you can reach me by placing zeroes
> where you see stars: wypbs.**1 at gmail.com.
>


Base 2 numbers, which is what computers use, cannot precisely represent Base
10 numbers, which is what humans use.  You have a gap in your understanding
of how computers do arithmetic.  Basically, there are three systems - whole
numbers, which are exactly represented within the valid range of the machine
word size, packed or binary coded decimals, which can be exactly represented
within the valid range of the machine's packed decimal, and floats, which
can be exactly represented if and only if there is a non-infinite
mathematical expansion of the base 10 to base 2 conversion for the number
that fits inside the machine's floating number word size.  In dotNet, these
translate into

whole numbers - System.Int16, System.Int32, and System.Int64 and their
unsigned variants
                        - arithmetic on these values is relatively simple as
most CPUs directly support it.

packed decimals - System.Decimal
                        - The x86 line of processors has had direct support
for since the 8086 and 8088 in the binary coded decimal instructions.  The
8087 extended the range.

floats - Single and Double
                        - the 8087 and it's descendents support the IEEE
definitions for these types.  Starting with 80486 this support was built in
to the main CPU.

The issue you feel is a bug is actually a limitation in _ALL_ current
computing hardware.  Languages that provide "exact" values for floating
point numbers don't directly use the hardware's floating point support.
Instead, they split the number into chunks that can be directly handled by
the packed decimal hardware and do the arithmetic using the rules for
carrying and borrowing we all learn in grade school.

Mike Ober.
Author
21 May 2009 12:11 AM
eBob.com
Show quote Hide quote
"Peter B. Steiger" <see.sig@for.email.address> wrote in message
news:3eydnWCzoONg14nXnZ2dnUVZ_jJi4p2d@bresnan.com...
> On Tue, 19 May 2009 16:40:01 -0700, OmegaSquared sez:
>
>> No, this is not a bug, but for floating point numbers this behaviour
>> that you are seeing is by design.  Please see:
>>
>> http://docs.sun.com/source/806-3568/ncg_goldberg.html
>>
>> for a detailed explanation.
>>
>> The data type that you probably want to be using is Decimal.
>
> Thanks!  I'll certainly make the change... but that article didn't do
> much to convince me "that's not a bug, it's a feature."  If single- or
> double-precision floating point variables don't perform basic arithmetic
> correctly, what IS an appropriate (and reliable) use for them?
>
> --
> Peter B. Steiger
> Cheyenne, WY
> If you must reply by email, you can reach me by placing zeroes
> where you see stars: wypbs.**1 at gmail.com.

It's not a feature or a bug.  It's a fact of binary arithmetic; some
fractions can not be precisely represented in binary.  Just as some cannot
be precisely represented in decimal - 1/3 for example.  Except for extremely
rare bugs computers perform arithmetic correctly.  You just have to know
which kind of arithmetic, binary or decimal, to ask for.

Bob
Author
21 May 2009 7:58 PM
Peter B. Steiger
On Wed, 20 May 2009 20:11:52 -0400, eBob.com sez:

> It's not a feature or a bug.  It's a fact of binary arithmetic; some
> fractions can not be precisely represented in binary.  Just as some
> cannot be precisely represented in decimal - 1/3 for example.  Except
> for extremely rare bugs computers perform arithmetic correctly.  You
> just have to know which kind of arithmetic, binary or decimal, to ask
> for.

Fair enough - thanks to Bob and Michael Ober for the explanation.  I do
understand the way computers use binary; I just thought that all the
"magic" to convert to base 10 happened internally in the CPU as requested
by assembler calls.  But hey, I'm an end user application developer - I
don't think about what goes on under the hood.

So under what circumstances would single & double precision floats be the
appropriate choice when you could count on accuracy to what MS claims to
be 4.a-bunch-of-numbersE-324?  What kind of operations (and values) can
use those data types safely?

--
Peter B. Steiger
Cheyenne, WY
If you must reply by email, you can reach me by placing zeroes
where you see stars: wypbs.**1 at gmail.com.
Author
21 May 2009 10:54 PM
Michael D. Ober
Show quote Hide quote
"Peter B. Steiger" <see.sig@for.email.address> wrote in message
news:SbidnfF7T7zOL4jXnZ2dnUVZ_uli4p2d@bresnan.com...
> On Wed, 20 May 2009 20:11:52 -0400, eBob.com sez:
>
>> It's not a feature or a bug.  It's a fact of binary arithmetic; some
>> fractions can not be precisely represented in binary.  Just as some
>> cannot be precisely represented in decimal - 1/3 for example.  Except
>> for extremely rare bugs computers perform arithmetic correctly.  You
>> just have to know which kind of arithmetic, binary or decimal, to ask
>> for.
>
> Fair enough - thanks to Bob and Michael Ober for the explanation.  I do
> understand the way computers use binary; I just thought that all the
> "magic" to convert to base 10 happened internally in the CPU as requested
> by assembler calls.  But hey, I'm an end user application developer - I
> don't think about what goes on under the hood.
>
> So under what circumstances would single & double precision floats be the
> appropriate choice when you could count on accuracy to what MS claims to
> be 4.a-bunch-of-numbersE-324?  What kind of operations (and values) can
> use those data types safely?
>
> --
> Peter B. Steiger
> Cheyenne, WY
> If you must reply by email, you can reach me by placing zeroes
> where you see stars: wypbs.**1 at gmail.com.
>


For Double, only the first 15 digits of the answer are guaranteed.  The last
two digits are referred to as guard digits and will almost never be
accurate.  For Single, 7 digits are guaranteed.  Again the last two digits
are guard digits.  As another poster pointed out, if you must have precise
answers, use System.Decimal.  As long as the fractional part of the number
can be exactly represented in four base 10 digits or less, System.Decimal
will not have rounding errors.

Mike.
Author
22 May 2009 10:50 AM
Armin Zingler
Peter B. Steiger wrote:
Show quoteHide quote
> On Tue, 19 May 2009 16:40:01 -0700, OmegaSquared sez:
>
>> No, this is not a bug, but for floating point numbers this behaviour
>> that you are seeing is by design.  Please see:
>>
>> http://docs.sun.com/source/806-3568/ncg_goldberg.html
>>
>> for a detailed explanation.
>>
>> The data type that you probably want to be using is Decimal.
>
> Thanks!  I'll certainly make the change... but that article didn't do
> much to convince me "that's not a bug, it's a feature."  If single- or
> double-precision floating point variables don't perform basic
> arithmetic correctly, what IS an appropriate (and reliable) use for
> them?


Single/Double can be considered as "analog" values. For example, if you
store coordinates in 3D space, it doesn't matter if the deviation (due to
finite storage space) is positive or negativ. Math.PI cam also not be stored
exactly and you can calculate well with it without caring if the actual PI
is a little bit smaller or larger than Math.PI.


Armin
Author
22 May 2009 3:32 PM
Andrew Morton
Armin Zingler wrote:
> Single/Double can be considered as "analog" values.

Isn't that the wrong way round? An analog variable can take any value, like
the groove on a vinyl record, whereas a digital value can only take discreet
values, like the sounds on a CD.

> For example, if you store coordinates in 3D space, it doesn't matter if
> the deviation (due to finite storage space) is positive or negativ.

I don't understand how that follows, but then again I finish work in a few
minutes so my brain may be elsewhere.

A Decimal just happens to be able to take and manipulate values in base 10,
which is what the OP needed to find out.

Andrew
Author
22 May 2009 4:20 PM
Armin Zingler
Andrew Morton wrote:
> Armin Zingler wrote:
>> Single/Double can be considered as "analog" values.
>
> Isn't that the wrong way round? An analog variable can take any
> value, like the groove on a vinyl record, whereas a digital value can
> only take discreet values, like the sounds on a CD.

As we don't have analog variables, a Double variable is the most often used
type to store an analog value. That means a value of which you don't care
about the number of decimal/octal/whatever digits necessary to display
the value as a String.

>> For example, if you store coordinates in 3D space, it doesn't matter
>> if the deviation (due to finite storage space) is positive or
>> negativ.
>
> I don't understand how that follows,

In the example of the OP's first post, the Double value was a little bit
smaller than the value expected. If you cut the fractional part, you get the
"wrong" result. Therefore his criticism/question about the Double data type
(in opposite to Single). If one considers a Double variable as a place to
store an analog value as close as possible, it also doesn't matter anymore
if the stored value is smaller or greater than the actual value.

> but then again I finish work in
> a few minutes so my brain may be elsewhere.

:-)

> A Decimal just happens to be able to take and manipulate values in
> base 10, which is what the OP needed to find out.

Absolutely, but my answer was to his question what a double value can be
used for at all.


Armin
Author
20 May 2009 7:08 AM
Cor Ligthert[MVP]
Peter,

The double is not meant to be used in financial computing.
This is not even by design, this is simple general used behaviour.

For accounting is the decimal meant.

Be aware that standard in Net ISO rounding (banking rounding is used)

Cor

Show quoteHide quote
"Peter B. Steiger" <see.sig@for.email.address> wrote in message
news:8d-dnVDaj4fyoo7XnZ2dnUVZ_j1i4p2d@bresnan.com...
>I wrote a quick-and-dirty accounting routine that added up charges and
> payments to show a running balance on someone's ledger.  Our QA team
> reported that it is producing the wrong balances, and sure enough it was.
> The answers were coming back with umpteen decimal places written in
> scientific notation, e.g. 1.23456E-9.
>
> I thought I'd play it smart and wrote a function to do all the work in
> integers - it receives the running balance and new amount, multiplies
> them by 100 into integer variables, adds the two integers, and divides
> the result by 100.  It's stupid that I have to do this because our state-
> of-the-art computer can't add two-place numbers together and come up with
> the right answer, but at least it was an easy enough kludge.
>
> Now it turns out it's also converting to integers wrong. For example,
> with a balance of $595.17 added to $80.00, the new balance is $675.16
> because it multiplied 595.17 * 100 and came up with... 59516.
>
> The balance and amount are both double precision, so I did some testing:
> Module FloatTest
>  Sub Main()
>    Dim nFloat1 As Single
>    Dim nFloat2 As Double
>    Dim nInt1 As Integer
>    Dim nInt2 As Integer
>
>    nFloat1 = 595.17
>    nInt1 = Int(nFloat1 * 100)
>    Console.WriteLine("Int of single " & nFloat1 & " * 100 is " & nInt1)
>    nFloat2 = 595.17
>    nInt2 = Int(nFloat2 * 100)
>    Console.WriteLine("Int of double " & nFloat2 & " * 100 is " & nInt2)
>  End Sub
> End Module
>
> The results?  Multiply a single-precision number with two decimal places
> by 100, and you get the right answer (in this case, 59517).  Multiply a
> double precision number by 100 and you get the wrong answer (59516).
>
> I know FP math produced weird results on old CPUs (8086 through 80486),
> but I would have thought for sure some processor genius would have fixed
> the problem by now.  Is this a known bug, or am I using the wrong data
> types for my purposes, or what?
>
> --
> Peter B. Steiger
> Cheyenne, WY
> If you must reply by email, you can reach me by placing zeroes
> where you see stars: wypbs.**1 at gmail.com.
Author
21 May 2009 8:39 AM
Michel Posseth [MCP]
>Be aware that standard in Net ISO rounding (banking rounding is used)

every computer language i know of does bankers rounding this includes even
Fortran and COBOL
most people are just not aware of that fact , so it is not something
specific to VB or  .Net

( just wanted to make that clear )


However in all those languages ( even COBOL ) it is possible to use common
rounding in .Net you do that with system.math.round
and the away from zero option

"By default, Math.Round uses MidpointRounding.ToEven. Most people are not
familiar with "rounding to even" as the alternative, "rounding away from
zero" is more commonly taught in school. .NET defaults to "Rounding to even"
as it is statistically superior because it doesn't share the tendency of
"rounding away from zero" to round up slightly more often than it rounds
down (assuming the numbers being rounded tend to be positive.) "


HTH

Michel


Show quoteHide quote
"Cor Ligthert[MVP]" <Notmyfirstn***@planet.nl> schreef in bericht
news:efL%23KnR2JHA.4416@TK2MSFTNGP05.phx.gbl...
> Peter,
>
> The double is not meant to be used in financial computing.
> This is not even by design, this is simple general used behaviour.
>
> For accounting is the decimal meant.
>
> Be aware that standard in Net ISO rounding (banking rounding is used)
>
> Cor
>
> "Peter B. Steiger" <see.sig@for.email.address> wrote in message
> news:8d-dnVDaj4fyoo7XnZ2dnUVZ_j1i4p2d@bresnan.com...
>>I wrote a quick-and-dirty accounting routine that added up charges and
>> payments to show a running balance on someone's ledger.  Our QA team
>> reported that it is producing the wrong balances, and sure enough it was.
>> The answers were coming back with umpteen decimal places written in
>> scientific notation, e.g. 1.23456E-9.
>>
>> I thought I'd play it smart and wrote a function to do all the work in
>> integers - it receives the running balance and new amount, multiplies
>> them by 100 into integer variables, adds the two integers, and divides
>> the result by 100.  It's stupid that I have to do this because our state-
>> of-the-art computer can't add two-place numbers together and come up with
>> the right answer, but at least it was an easy enough kludge.
>>
>> Now it turns out it's also converting to integers wrong. For example,
>> with a balance of $595.17 added to $80.00, the new balance is $675.16
>> because it multiplied 595.17 * 100 and came up with... 59516.
>>
>> The balance and amount are both double precision, so I did some testing:
>> Module FloatTest
>>  Sub Main()
>>    Dim nFloat1 As Single
>>    Dim nFloat2 As Double
>>    Dim nInt1 As Integer
>>    Dim nInt2 As Integer
>>
>>    nFloat1 = 595.17
>>    nInt1 = Int(nFloat1 * 100)
>>    Console.WriteLine("Int of single " & nFloat1 & " * 100 is " & nInt1)
>>    nFloat2 = 595.17
>>    nInt2 = Int(nFloat2 * 100)
>>    Console.WriteLine("Int of double " & nFloat2 & " * 100 is " & nInt2)
>>  End Sub
>> End Module
>>
>> The results?  Multiply a single-precision number with two decimal places
>> by 100, and you get the right answer (in this case, 59517).  Multiply a
>> double precision number by 100 and you get the wrong answer (59516).
>>
>> I know FP math produced weird results on old CPUs (8086 through 80486),
>> but I would have thought for sure some processor genius would have fixed
>> the problem by now.  Is this a known bug, or am I using the wrong data
>> types for my purposes, or what?
>>
>> --
>> Peter B. Steiger
>> Cheyenne, WY
>> If you must reply by email, you can reach me by placing zeroes
>> where you see stars: wypbs.**1 at gmail.com.
>
Author
21 May 2009 12:56 PM
Cor Ligthert[MVP]
Michel,

I have really made endless programs in past in Cobol for different systems.
I am me not aware that bankers rounding was used in the first ones.
We simply had to do that forever ourselves.

But maybe there are versions who do.

Are you telling in Albert Hein as well that they should round to even.
That it is better I know for a long time, but tell it to all the end-users
of your company

But thanks for the information

:-)

Cor

Show quoteHide quote
"Michel Posseth [MCP]" <M***@posseth.com> wrote in message
news:Oa7Nn%23e2JHA.2656@TK2MSFTNGP05.phx.gbl...
> >Be aware that standard in Net ISO rounding (banking rounding is used)
>
> every computer language i know of does bankers rounding this includes even
> Fortran and COBOL
> most people are just not aware of that fact , so it is not something
> specific to VB or  .Net
>
> ( just wanted to make that clear )
>
>
> However in all those languages ( even COBOL ) it is possible to use common
> rounding in .Net you do that with system.math.round
> and the away from zero option
>
> "By default, Math.Round uses MidpointRounding.ToEven. Most people are not
> familiar with "rounding to even" as the alternative, "rounding away from
> zero" is more commonly taught in school. .NET defaults to "Rounding to
> even" as it is statistically superior because it doesn't share the
> tendency of "rounding away from zero" to round up slightly more often than
> it rounds down (assuming the numbers being rounded tend to be positive.) "
>
>
> HTH
>
> Michel
>
>
> "Cor Ligthert[MVP]" <Notmyfirstn***@planet.nl> schreef in bericht
> news:efL%23KnR2JHA.4416@TK2MSFTNGP05.phx.gbl...
>> Peter,
>>
>> The double is not meant to be used in financial computing.
>> This is not even by design, this is simple general used behaviour.
>>
>> For accounting is the decimal meant.
>>
>> Be aware that standard in Net ISO rounding (banking rounding is used)
>>
>> Cor
>>
>> "Peter B. Steiger" <see.sig@for.email.address> wrote in message
>> news:8d-dnVDaj4fyoo7XnZ2dnUVZ_j1i4p2d@bresnan.com...
>>>I wrote a quick-and-dirty accounting routine that added up charges and
>>> payments to show a running balance on someone's ledger.  Our QA team
>>> reported that it is producing the wrong balances, and sure enough it
>>> was.
>>> The answers were coming back with umpteen decimal places written in
>>> scientific notation, e.g. 1.23456E-9.
>>>
>>> I thought I'd play it smart and wrote a function to do all the work in
>>> integers - it receives the running balance and new amount, multiplies
>>> them by 100 into integer variables, adds the two integers, and divides
>>> the result by 100.  It's stupid that I have to do this because our
>>> state-
>>> of-the-art computer can't add two-place numbers together and come up
>>> with
>>> the right answer, but at least it was an easy enough kludge.
>>>
>>> Now it turns out it's also converting to integers wrong. For example,
>>> with a balance of $595.17 added to $80.00, the new balance is $675.16
>>> because it multiplied 595.17 * 100 and came up with... 59516.
>>>
>>> The balance and amount are both double precision, so I did some testing:
>>> Module FloatTest
>>>  Sub Main()
>>>    Dim nFloat1 As Single
>>>    Dim nFloat2 As Double
>>>    Dim nInt1 As Integer
>>>    Dim nInt2 As Integer
>>>
>>>    nFloat1 = 595.17
>>>    nInt1 = Int(nFloat1 * 100)
>>>    Console.WriteLine("Int of single " & nFloat1 & " * 100 is " & nInt1)
>>>    nFloat2 = 595.17
>>>    nInt2 = Int(nFloat2 * 100)
>>>    Console.WriteLine("Int of double " & nFloat2 & " * 100 is " & nInt2)
>>>  End Sub
>>> End Module
>>>
>>> The results?  Multiply a single-precision number with two decimal places
>>> by 100, and you get the right answer (in this case, 59517).  Multiply a
>>> double precision number by 100 and you get the wrong answer (59516).
>>>
>>> I know FP math produced weird results on old CPUs (8086 through 80486),
>>> but I would have thought for sure some processor genius would have fixed
>>> the problem by now.  Is this a known bug, or am I using the wrong data
>>> types for my purposes, or what?
>>>
>>> --
>>> Peter B. Steiger
>>> Cheyenne, WY
>>> If you must reply by email, you can reach me by placing zeroes
>>> where you see stars: wypbs.**1 at gmail.com.
>>
>
>
Author
21 May 2009 4:08 PM
Mike Williams
"Cor Ligthert[MVP]" <Notmyfirstn***@planet.nl> wrote in message
news:u$9GYOh2JHA.6056@TK2MSFTNGP03.phx.gbl...

> Are you telling in Albert Hein as well that they should
> round to even. That it is better I know for a long time

That depends what you mean by "better". The most appropriate method of
rounding depends on the circumstances and there is not a "one size fits all"
best solution. For example, if in the UK you are writing code to calculate
how much money an individual owes you in order to send him a bill then you
must always "round down" to the nearest penny (currently the lowest value
legal tender coin) because rounding up would mean that you are billing him
for more than the amount he owes you, an amount to which you are not
entitled. And of course there are other different circumstances in which the
correct solution is the opposite (always rounding up to the nearest penny).
Many other circumstances would require you to perform bankers' rounding, or
some other form of rounding, depending on exactly what task you were
performing. As with most things, it's "horses for courses".

Mike
Author
21 May 2009 7:16 PM
Cor Ligthert[MVP]
Mike,

I'll change "better" in typical (real) English.

It is more cricket (I don't know if I write this correct or even if it is
croquet, but I say it like that)

:-)

Cor


Show quoteHide quote
"Mike Williams" <M***@WhiskyAndCoke.com> wrote in message
news:uf4mu5i2JHA.3304@TK2MSFTNGP06.phx.gbl...
> "Cor Ligthert[MVP]" <Notmyfirstn***@planet.nl> wrote in message
> news:u$9GYOh2JHA.6056@TK2MSFTNGP03.phx.gbl...
>
>> Are you telling in Albert Hein as well that they should
>> round to even. That it is better I know for a long time
>
> That depends what you mean by "better". The most appropriate method of
> rounding depends on the circumstances and there is not a "one size fits
> all" best solution. For example, if in the UK you are writing code to
> calculate how much money an individual owes you in order to send him a
> bill then you must always "round down" to the nearest penny (currently the
> lowest value legal tender coin) because rounding up would mean that you
> are billing him for more than the amount he owes you, an amount to which
> you are not entitled. And of course there are other different
> circumstances in which the correct solution is the opposite (always
> rounding up to the nearest penny). Many other circumstances would require
> you to perform bankers' rounding, or some other form of rounding,
> depending on exactly what task you were performing. As with most things,
> it's "horses for courses".
>
> Mike
>
>
>
Author
22 May 2009 10:51 AM
Michel Posseth [MCP]
Hello Cor ,

> I have really made endless programs in past in Cobol for different
> systems.
> I am me not aware that bankers rounding was used in the first ones.
> We simply had to do that forever ourselves.
>
> But maybe there are versions who do.

Well this doesn`t surprise me as my 2 COBOL  co workers who worked for 30
years in the industry where also stating that it wasn`t like that in COBOL
untill i showed them a simple calculation an challenged them to do the same
in COBOL , it turned out that i was right and there COBOL production system
used bankers rounding  ( as allmost all program languages do by default  )
This isn`t a bad thing however you must be aware of it when you perform
certain calculations

This is knowledge that i feel should be in every standard programming course
, but as i noticed in practice i still tell my new  co workers something new
even if they worked for years in the industry and hold multiple
certifications or even have university degrees :-( .

I learned it from my tutor in the first week i started to become a
commercial  programmer  ( i had a on the job training from a verry
experienced  developer , who wrote on the core modules of the EXACT
corporation   http://www.exactsoftware.com ) and already then ( 1998 ) he
told me that 95% of the coders are not aware of that simple fact .

> Are you telling in Albert Hein as well that they should round to even.
> That it is better I know for a long time, but tell it to all the end-users
> of your company

No Midpoint rounding away from zero  that is something different as just
rounding to even , for a fact "Midpoint rounding away from zero" is rounding
how it is tought at every elementary school , so i guess that if they
wouldn`t use that method on your receipt you would go to the manager and
tell them to correct your payment .


lets get to the basics

A + B = C
2.5 + 3.5 = C

with common rounding we get 3 + 4 = 7.
with banker's rounding we get 2 + 4 = 6.

wich one is correct ?    see the problem Albert Heijn `s POS software
developer is facing  :-)

If you always round away from zero  you'll always be over.  if you round
some numbers up and some numbers down  you'll be closer to the correct
answer.

You should NEVER implicitly round numbers if they matter at all.  always  be
explicit if your values really matter.

P.S.

AFAIK every POS system just rounds the final sum with midpoint rounding away
from zero



regards



Michel







Show quoteHide quote
"Cor Ligthert[MVP]" <Notmyfirstn***@planet.nl> schreef in bericht
news:u$9GYOh2JHA.6056@TK2MSFTNGP03.phx.gbl...
> Michel,
>
> I have really made endless programs in past in Cobol for different
> systems.
> I am me not aware that bankers rounding was used in the first ones.
> We simply had to do that forever ourselves.
>
> But maybe there are versions who do.
>
> Are you telling in Albert Hein as well that they should round to even.
> That it is better I know for a long time, but tell it to all the end-users
> of your company
>
> But thanks for the information
>
> :-)
>
> Cor
>
> "Michel Posseth [MCP]" <M***@posseth.com> wrote in message
> news:Oa7Nn%23e2JHA.2656@TK2MSFTNGP05.phx.gbl...
>> >Be aware that standard in Net ISO rounding (banking rounding is used)
>>
>> every computer language i know of does bankers rounding this includes
>> even Fortran and COBOL
>> most people are just not aware of that fact , so it is not something
>> specific to VB or  .Net
>>
>> ( just wanted to make that clear )
>>
>>
>> However in all those languages ( even COBOL ) it is possible to use
>> common rounding in .Net you do that with system.math.round
>> and the away from zero option
>>
>> "By default, Math.Round uses MidpointRounding.ToEven. Most people are not
>> familiar with "rounding to even" as the alternative, "rounding away from
>> zero" is more commonly taught in school. .NET defaults to "Rounding to
>> even" as it is statistically superior because it doesn't share the
>> tendency of "rounding away from zero" to round up slightly more often
>> than it rounds down (assuming the numbers being rounded tend to be
>> positive.) "
>>
>>
>> HTH
>>
>> Michel
>>
>>
>> "Cor Ligthert[MVP]" <Notmyfirstn***@planet.nl> schreef in bericht
>> news:efL%23KnR2JHA.4416@TK2MSFTNGP05.phx.gbl...
>>> Peter,
>>>
>>> The double is not meant to be used in financial computing.
>>> This is not even by design, this is simple general used behaviour.
>>>
>>> For accounting is the decimal meant.
>>>
>>> Be aware that standard in Net ISO rounding (banking rounding is used)
>>>
>>> Cor
>>>
>>> "Peter B. Steiger" <see.sig@for.email.address> wrote in message
>>> news:8d-dnVDaj4fyoo7XnZ2dnUVZ_j1i4p2d@bresnan.com...
>>>>I wrote a quick-and-dirty accounting routine that added up charges and
>>>> payments to show a running balance on someone's ledger.  Our QA team
>>>> reported that it is producing the wrong balances, and sure enough it
>>>> was.
>>>> The answers were coming back with umpteen decimal places written in
>>>> scientific notation, e.g. 1.23456E-9.
>>>>
>>>> I thought I'd play it smart and wrote a function to do all the work in
>>>> integers - it receives the running balance and new amount, multiplies
>>>> them by 100 into integer variables, adds the two integers, and divides
>>>> the result by 100.  It's stupid that I have to do this because our
>>>> state-
>>>> of-the-art computer can't add two-place numbers together and come up
>>>> with
>>>> the right answer, but at least it was an easy enough kludge.
>>>>
>>>> Now it turns out it's also converting to integers wrong. For example,
>>>> with a balance of $595.17 added to $80.00, the new balance is $675.16
>>>> because it multiplied 595.17 * 100 and came up with... 59516.
>>>>
>>>> The balance and amount are both double precision, so I did some
>>>> testing:
>>>> Module FloatTest
>>>>  Sub Main()
>>>>    Dim nFloat1 As Single
>>>>    Dim nFloat2 As Double
>>>>    Dim nInt1 As Integer
>>>>    Dim nInt2 As Integer
>>>>
>>>>    nFloat1 = 595.17
>>>>    nInt1 = Int(nFloat1 * 100)
>>>>    Console.WriteLine("Int of single " & nFloat1 & " * 100 is " & nInt1)
>>>>    nFloat2 = 595.17
>>>>    nInt2 = Int(nFloat2 * 100)
>>>>    Console.WriteLine("Int of double " & nFloat2 & " * 100 is " & nInt2)
>>>>  End Sub
>>>> End Module
>>>>
>>>> The results?  Multiply a single-precision number with two decimal
>>>> places
>>>> by 100, and you get the right answer (in this case, 59517).  Multiply a
>>>> double precision number by 100 and you get the wrong answer (59516).
>>>>
>>>> I know FP math produced weird results on old CPUs (8086 through 80486),
>>>> but I would have thought for sure some processor genius would have
>>>> fixed
>>>> the problem by now.  Is this a known bug, or am I using the wrong data
>>>> types for my purposes, or what?
>>>>
>>>> --
>>>> Peter B. Steiger
>>>> Cheyenne, WY
>>>> If you must reply by email, you can reach me by placing zeroes
>>>> where you see stars: wypbs.**1 at gmail.com.
>>>
>>
>>
>
Author
21 May 2009 8:00 PM
Peter B. Steiger
On Wed, 20 May 2009 09:08:14 +0200, Cor Ligthert[MVP] sez:

> Be aware that standard in Net ISO rounding (banking rounding is used)

True, but when we're dealing with whole pennies rounding is not an
issue... my original complaint that started this thread involved simply
adding two two-decimal numbers together and expecting the result to be
correct to two decimal places.  As others have noted, the
"decimal" (formerly currency) data type is what I need for this.

--
Peter B. Steiger
Cheyenne, WY
If you must reply by email, you can reach me by placing zeroes
where you see stars: wypbs.**1 at gmail.com.
Author
21 May 2009 8:22 PM
Cor Ligthert[MVP]
duh

"Others have noted", what you mean with that, I have written that as well?

If you cut out messages then do it correct?

Or do you find it normal as I make from your message

"Peter B. Steiger" <see.sig@for.email.address> wrote in message
>>>True, but we're dealing with pennies ...

Cor

Show quoteHide quote
"Peter B. Steiger" <see.sig@for.email.address> wrote in message
news:SbidnfB7T7xGL4jXnZ2dnUVZ_umnnZ2d@bresnan.com...
> On Wed, 20 May 2009 09:08:14 +0200, Cor Ligthert[MVP] sez:
>
>> Be aware that standard in Net ISO rounding (banking rounding is used)
>
> True, but when we're dealing with whole pennies rounding is not an
> issue... my original complaint that started this thread involved simply
> adding two two-decimal numbers together and expecting the result to be
> correct to two decimal places.  As others have noted, the
> "decimal" (formerly currency) data type is what I need for this.
>
> --
> Peter B. Steiger
> Cheyenne, WY
> If you must reply by email, you can reach me by placing zeroes
> where you see stars: wypbs.**1 at gmail.com.
Author
21 May 2009 9:45 PM
Peter B. Steiger
On Thu, 21 May 2009 22:22:14 +0200, Cor Ligthert[MVP] sez:
> "Others have noted", what you mean with that, I have written that as
> well?

No, I mean that I got the answer to my question in replies that other
people wrote.  I'm sorry if it sounds like I was misquoting you, but I
was not.

> If you cut out messages then do it correct?

I always preserve the exact wording of a message I quote, but I also
remove any quoted material that does not relate to my reply... it makes
it easier to read the question and answer in a normal conversational
flow. Again, I apologize if this upsets you.  If you find a place where I
misquoted you, please let me know.


> Or do you find it normal as I make from your message
> "Peter B. Steiger" <see.sig@for.email.address> wrote in message
>>>>True, but we're dealing with pennies ...

Sure, that works fine.  You quoted me accurately; the next step would be
to follow that quote with any comments you have that relate to the part
you quoted.

It turns out there is a name for the reply style I use - I did not know
that!  It's called interleaved, or inline, quoting:
http://en.wikipedia.org/wiki/Posting_style (no relation to the "Peter"
used in the examples).

HTH.

--
Peter B. Steiger
Cheyenne, WY
If you must reply by email, you can reach me by placing zeroes
where you see stars: wypbs.**1 at gmail.com.
Author
22 May 2009 5:42 AM
Cor Ligthert[MVP]
Peter

I wrote this quiet early

>For accounting is the decimal meant.

The only "other" who wrote that before me was Michael D Ober

I wrote it to show,  that I agreed with him.

Your message can give the idea that I disagreed with him.

Cor


Show quoteHide quote
"Peter B. Steiger" <see.sig@for.email.address> wrote in message
news:E4KdnauNxvsbVojXnZ2dnUVZ_o9i4p2d@bresnan.com...
> On Thu, 21 May 2009 22:22:14 +0200, Cor Ligthert[MVP] sez:
>> "Others have noted", what you mean with that, I have written that as
>> well?
>
> No, I mean that I got the answer to my question in replies that other
> people wrote.  I'm sorry if it sounds like I was misquoting you, but I
> was not.
>
>> If you cut out messages then do it correct?
>
> I always preserve the exact wording of a message I quote, but I also
> remove any quoted material that does not relate to my reply... it makes
> it easier to read the question and answer in a normal conversational
> flow. Again, I apologize if this upsets you.  If you find a place where I
> misquoted you, please let me know.
>
>
>> Or do you find it normal as I make from your message
>> "Peter B. Steiger" <see.sig@for.email.address> wrote in message
>>>>>True, but we're dealing with pennies ...
>
> Sure, that works fine.  You quoted me accurately; the next step would be
> to follow that quote with any comments you have that relate to the part
> you quoted.
>
> It turns out there is a name for the reply style I use - I did not know
> that!  It's called interleaved, or inline, quoting:
> http://en.wikipedia.org/wiki/Posting_style (no relation to the "Peter"
> used in the examples).
>
> HTH.
>
> --
> Peter B. Steiger
> Cheyenne, WY
> If you must reply by email, you can reach me by placing zeroes
> where you see stars: wypbs.**1 at gmail.com.
Author
22 May 2009 7:08 AM
Mike Williams
"Peter B. Steiger" <see.sig@for.email.address> wrote in message
news:SbidnfB7T7xGL4jXnZ2dnUVZ_umnnZ2d@bresnan.com...

> True, but when we're dealing with whole pennies
> rounding is not an issue...

It isn't if you're adding and subtracting them but it is if you are
performing other operations, such as calculating sales tax or VAT or
performing other similar operations.

Mike
Author
30 May 2009 1:00 AM
Jeff Dillingham
Peter:
While the response from other writter is correct in advising that you should
not be using floating point calculations for financial analysis, you are also
correct that there is a bug in the floating point arithmetic in VB.Net.  The
symptom is that when you add two double precision variables together and
assign the result to another double precision variable, the result may only
be accurate to single precision.  The remaining digits are random.  This
behavior does not occur all of the time however.  It seems to depend on what
has preceeded the calculation.  I have seen this in my own code under
repeatable conditions, and it has been verified by a co-worker who
encountered the same problem independently in a different program.

The code below is a simple subroutine that will test whether the error is
occurring.  Just call this from anywhere in your program.  I find that
sometimes this test routine passes, and sometimes it fails and traps an error
with the message box.  I have not determined any way to determine if/when the
error will occur.  This is truely a serious problem that makes VB inadequate
for certain types of engineering calculations where double precision is
important.  I have tried converting some my code to use the Decimal data
type, but that actually made the problem worse.  It is not practical to
convert my entire program to Decimal, so it is an unresolved problem at this
point.

Oddly, the problem seems related to the assignment of the value to a
variable.  If you set a break point in this routine and inspect the
expression in the watch window it will be correct, but the value of the
variable that the expression is assigned to is not correct. 


        Public Shared Sub TestAddition()

            Dim dummy1 As Double, dummy2 As Double, dummy3 As Double
            Dim dummy4 As Double
            Dim dummy5 As Double

            dummy1 = 1.0 / 3.0
            dummy2 = 1.0
            dummy3 = dummy1 + dummy2 'should be 1.3333333333333333, but is
actually 1.3333333730697632

            'Debug.Print(dummy3)

            dummy4 = 1.0 + 1.0 / 3.0 'this produces the correct answer:
1.3333333333333333; therefore the problem seems to be with adding
double-precision variables, not literals

            'Debug.Print(dummy4)

            dummy5 = dummy3 - dummy4 'the difference between the correct and
incorrect answers

            'Debug.Print(dummy5)

            If dummy5 > 0.000000001 Then
                MsgBox("round off error in vb")
            End If

        End Sub


Show quoteHide quote
"Peter B. Steiger" wrote:

> I wrote a quick-and-dirty accounting routine that added up charges and
> payments to show a running balance on someone's ledger.  Our QA team
> reported that it is producing the wrong balances, and sure enough it was.
> The answers were coming back with umpteen decimal places written in
> scientific notation, e.g. 1.23456E-9.
>
> I thought I'd play it smart and wrote a function to do all the work in
> integers - it receives the running balance and new amount, multiplies
> them by 100 into integer variables, adds the two integers, and divides
> the result by 100.  It's stupid that I have to do this because our state-
> of-the-art computer can't add two-place numbers together and come up with
> the right answer, but at least it was an easy enough kludge.
>
> Now it turns out it's also converting to integers wrong. For example,
> with a balance of $595.17 added to $80.00, the new balance is $675.16
> because it multiplied 595.17 * 100 and came up with... 59516.
>
> The balance and amount are both double precision, so I did some testing:
> Module FloatTest

>   Sub Main()

>     Dim nFloat1 As Single

>     Dim nFloat2 As Double

>     Dim nInt1 As Integer

>     Dim nInt2 As Integer

>

>     nFloat1 = 595.17

>     nInt1 = Int(nFloat1 * 100)

>     Console.WriteLine("Int of single " & nFloat1 & " * 100 is " & nInt1)

>     nFloat2 = 595.17

>     nInt2 = Int(nFloat2 * 100)

>     Console.WriteLine("Int of double " & nFloat2 & " * 100 is " & nInt2)

>   End Sub

> End Module

>
> The results?  Multiply a single-precision number with two decimal places
> by 100, and you get the right answer (in this case, 59517).  Multiply a
> double precision number by 100 and you get the wrong answer (59516).
>
> I know FP math produced weird results on old CPUs (8086 through 80486),
> but I would have thought for sure some processor genius would have fixed
> the problem by now.  Is this a known bug, or am I using the wrong data
> types for my purposes, or what?
>
> --
> Peter B. Steiger
> Cheyenne, WY
> If you must reply by email, you can reach me by placing zeroes
> where you see stars: wypbs.**1 at gmail.com.
>
Author
30 May 2009 1:54 AM
Armin Zingler
Jeff Dillingham wrote:
>            Dim dummy1 As Double, dummy2 As Double, dummy3 As Double
>            Dim dummy4 As Double
>            Dim dummy5 As Double
>
>            dummy1 = 1.0 / 3.0
>            dummy2 = 1.0
>            dummy3 = dummy1 + dummy2 'should be 1.3333333333333333,
> but is actually 1.3333333730697632


Your statement reminded me of an issue I encountered some years ago in a
different situation. I've search and found it: (in short: Direct3D sets FPU
to single precision mode)
http://groups.google.com/group/microsoft.public.win32.programmer.directx.managed/browse_frm/thread/10c3ba49a550edc4

Probably, somewhere in your code, maybe in a 3rd party component or if you
also use DirectX, the FPU is set to single-precision mode also.

If i execute

    call TestAddition
    Create Direct3D Device without FPU_Reserve flag
    call TestAddition

I get exactly the behavior you describe.


Armin
Author
31 May 2009 6:04 PM
Jeff Dillingham
Armin:

Wow!  Dude!  You are my new best friend. Yes, I use Direct3D in my code. 
Never occurred to me that was related to the problem.  I followed the link
you sent me.  As advised I now set the FPUPreserve flag when I create the
Direct3D device.  The problem went away immediately.  You have saved me days
of headache and anxiety.  Thanks much.

Jeff

Show quoteHide quote
"Armin Zingler" wrote:

> Jeff Dillingham wrote:
> >            Dim dummy1 As Double, dummy2 As Double, dummy3 As Double
> >            Dim dummy4 As Double
> >            Dim dummy5 As Double
> >
> >            dummy1 = 1.0 / 3.0
> >            dummy2 = 1.0
> >            dummy3 = dummy1 + dummy2 'should be 1.3333333333333333,
> > but is actually 1.3333333730697632
>
>
> Your statement reminded me of an issue I encountered some years ago in a
> different situation. I've search and found it: (in short: Direct3D sets FPU
> to single precision mode)
> http://groups.google.com/group/microsoft.public.win32.programmer.directx.managed/browse_frm/thread/10c3ba49a550edc4
>
> Probably, somewhere in your code, maybe in a 3rd party component or if you
> also use DirectX, the FPU is set to single-precision mode also.
>
> If i execute
>
>     call TestAddition
>     Create Direct3D Device without FPU_Reserve flag
>     call TestAddition
>
> I get exactly the behavior you describe.
>
>
> Armin
>
>
Author
2 Jun 2009 9:00 AM
Armin Zingler
Jeff Dillingham wrote:
> Armin:
>
> Wow!  Dude!  You are my new best friend. Yes, I use Direct3D in my
> code. Never occurred to me that was related to the problem.  I
> followed the link you sent me.  As advised I now set the FPUPreserve
> flag when I create the Direct3D device.  The problem went away
> immediately.  You have saved me days of headache and anxiety.  Thanks
> much.

You're welcome! :)


Armin