Screenshot instructions:
Windows
Mac
Red Hat Linux
Ubuntu
Click URL instructions:
Rightclick on ad, choose "Copy Link", then paste here →
(This may not be possible with some types of ads)
From: Koen Bok <koen@ma...>  20060517 14:22:44
Attachments:
smime.p7s
Message as HTML

I have this very simple unit test, but it fails! Is this normal? import unittest class OrderProduct(object): def total(self): return self.quantity * self.price class CaseCheck(unittest.TestCase): def setUp(self): self.orderproduct = OrderProduct() self.orderproduct.product = Product() self.orderproduct.product.tax = 19 self.test_data = [ {'quantity': 6, 'price': 12.78, 'total': 76.68}, {'quantity': 78, 'price': 12.78, 'total': 996.84}, ] def testTaxSanity(self): """Description of the test""" for t in self.test_data: self.orderproduct.quantity = t['quantity'] self.orderproduct.price = t['price'] self.assertEqual(self.orderproduct.total(), t['total']) if __name__ == "__main__": unittest.main() F ====================================================================== FAIL: Description of the test  Traceback (most recent call last): File "/Users/koen/unit_test.py", line 21, in testTaxSanity self.assertEqual(self.orderproduct.total(), t['total']) AssertionError: 76.679999999999993 != 76.680000000000007  Ran 1 test in 0.001s FAILED (failures=1) 
From: Phil Frost <indigo@bi...>  20060517 14:51:37

On Wed, May 17, 2006 at 04:22:32PM +0200, Koen Bok wrote: > I have this very simple unit test, but it fails! > > Is this normal? > > import unittest > > class OrderProduct(object): > def total(self): > return self.quantity * self.price > > class CaseCheck(unittest.TestCase): > > def setUp(self): > self.orderproduct = OrderProduct() > self.orderproduct.product = Product() > self.orderproduct.product.tax = 19 > > self.test_data = [ > {'quantity': 6, 'price': 12.78, 'total': 76.68}, > {'quantity': 78, 'price': 12.78, 'total': 996.84}, > ] > > def testTaxSanity(self): > """Description of the test""" > for t in self.test_data: > self.orderproduct.quantity = t['quantity'] > self.orderproduct.price = t['price'] > self.assertEqual(self.orderproduct.total(), > t['total']) > > if __name__ == "__main__": > unittest.main() > > F > ====================================================================== > FAIL: Description of the test >  > Traceback (most recent call last): > File "/Users/koen/unit_test.py", line 21, in testTaxSanity > self.assertEqual(self.orderproduct.total(), t['total']) > AssertionError: 76.679999999999993 != 76.680000000000007 > >  > Ran 1 test in 0.001s > > FAILED (failures=1) This is normal, and has nothing to do with PyObjC, but rather is the consequence of how floating point values are stored by a computer. When you write "76.68", you mean "76 + 68/100". The decimal system of writing number can thus only express exactly some subset of all rational numbers. For example, "1/3" is a rational number, but you can't write it in decimal. If you try, you find it is "0.333333333333...". Likewise, computers store floating point values somewhat like fractions, only instead of powers of 10 as the denominator, they use powers of 2. Thus, some numbers that can be written in a base 10 decimal system cat not be written with a finite number of digits in a base 2 decimal system. When whatever language you use (python in this case) parses "76.68" or whatever in your source code, it picks the binary representation that is closest to that number, which might be very slightly different. This is why your calculations are 0.00000000000001 in error. To avoid this issue, you have at least two solutions:  Round the final results to two decimal places, and hope that the accumilated error will never be more than 0.005 in error. Accountants will glare at you since this is a gamble, but it is the easiest to code, and fastest to compute.  Use a class designed for doing base 10 decimal arithmetic. Python has the decimal module, Foundation has NSDecimalNumber. This is only a little more coding effort, but your results will be identical to the results you would have obtained doing the math with traditional methods on paper. Arithmetic operations are signifantly slower to compute, but if you are just adding and multiplying a handfull of numbers, we are talking in terms of microseconds. 
From: Koen Bok <koen@ma...>  20060517 15:02:44
Attachments:
smime.p7s

Thank you for this lesson. While it has to be accepted by an accountant, I'll go for the decimal method. One for the record. Is it even possible that the accumilated error will be greater then . 005 when you use currency values (number with max two decimals)? Koen > This is normal, and has nothing to do with PyObjC, but rather is the > consequence of how floating point values are stored by a computer. > > When you write "76.68", you mean "76 + 68/100". The decimal system of > writing number can thus only express exactly some subset of all > rational > numbers. For example, "1/3" is a rational number, but you can't > write it > in decimal. If you try, you find it is "0.333333333333...". > > Likewise, computers store floating point values somewhat like > fractions, > only instead of powers of 10 as the denominator, they use powers of 2. > Thus, some numbers that can be written in a base 10 decimal system cat > not be written with a finite number of digits in a base 2 decimal > system. > > When whatever language you use (python in this case) parses "76.68" or > whatever in your source code, it picks the binary representation > that is > closest to that number, which might be very slightly different. > This is > why your calculations are 0.00000000000001 in error. > > To avoid this issue, you have at least two solutions: > >  Round the final results to two decimal places, and hope that the > accumilated error will never be more than 0.005 in error. > Accountants > will glare at you since this is a gamble, but it is the easiest to > code, and fastest to compute. > >  Use a class designed for doing base 10 decimal arithmetic. Python > has > the decimal module, Foundation has NSDecimalNumber. This is only a > little more coding effort, but your results will be identical to the > results you would have obtained doing the math with traditional > methods on paper. Arithmetic operations are signifantly slower to > compute, but if you are just adding and multiplying a handfull of > numbers, we are talking in terms of microseconds. 
From: Bob Ippolito <bob@re...>  20060517 15:51:36

On May 17, 2006, at 5:02 PM, Koen Bok wrote: > Thank you for this lesson. While it has to be accepted by an > accountant, I'll go for the decimal method. > > One for the record. > > Is it even possible that the accumilated error will be greater > then .005 when you use currency values (number with max two decimals)? Sure, consider small numbers x = 0.01 * 0.01 * 10 Depending on the precision used and the order of operations that could be zero. bob 
From: Brian O'Brien <bobrien@uc...>  20060517 16:00:39

On 17May06, at 9:51 AM, Bob Ippolito wrote: > > On May 17, 2006, at 5:02 PM, Koen Bok wrote: > >> Thank you for this lesson. While it has to be accepted by an >> accountant, I'll go for the decimal method. >> >> One for the record. >> >> Is it even possible that the accumilated error will be greater >> then .005 when you use currency values (number with max two >> decimals)? > > Sure, consider small numbers > > x = 0.01 * 0.01 * 10 > > Depending on the precision used and the order of operations that > could be zero. > I love these discussions on .01 and .02 etc.. i.e. their floating point precision or in other words the approximation of that number. The number .01 is not perfectly translatable into binary. I seem to remember a story of bank fraud based on the rounding errors going into peoples bank accounts... > bob > > > > >  > Using Tomcat but need to do more? Need to support web services, > security? > Get stuff done quickly with preintegrated technology to make your > job easier > Download IBM WebSphere Application Server v.1.0.1 based on Apache > Geronimo > http://sel.asus.falkag.net/sel? > cmd=lnk&kid=120709&bid=263057&dat=121642 > _______________________________________________ > Pyobjcdev mailing list > Pyobjcdev@... > https://lists.sourceforge.net/lists/listinfo/pyobjcdev 
From: Phil Frost <indigo@bi...>  20060518 00:31:34

On Wed, May 17, 2006 at 05:02:34PM +0200, Koen Bok wrote: > Thank you for this lesson. While it has to be accepted by an > accountant, I'll go for the decimal method. > > One for the record. > > Is it even possible that the accumilated error will be greater then . > 005 when you use currency values (number with max two decimals)? It's possible, but quite unlikely until you start dealing with large numbers. A related problem with floats is that they have a fixed number of significant digits. For example, here is a session with Python's interactive interpreter. I enter a number at the >>> prompt, python parses it, and then prints a decimal representation of the binary float it made. >>> 0.9123456789 0.91234567889999996 >>> 10000000.9123456789 10000000.912345679 >>> 100000000.9123456789 100000000.91234568 >>> 1000000000.9123456789 1000000000.9123456 >>> 10000000000000.9123456789 10000000000000.912 >>> 10000000000000000000000000000000.9123456789 9.9999999999999996e+30 >>> 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.9123456789 9.9999999999999996e+87 The floating point format my python interpreter is using can store only about 18 significant digits. You can see the numbers get farther from 0, digits on the right begin to be dropped. In the last example, you can see a float can store a very huge number (and even much huger) but with decreasing precision as the numbers get farther from zero. The consequence for money is that when you start dealing with large numbers (in the range of the US national debt) the difference between some decimal number you input and the closest floating point representation nears tenths of cents. If you had to store numbers much larger, you might not even have enough precision to repsent increments of 1 cent. 
From: Greg Ewing <greg.ewing@ca...>  20060518 00:06:52

Koen Bok wrote: > Is it even possible that the accumilated error will be greater then . > 005 when you use currency values (number with max two decimals)? Yes, it's possible. If you add up enough numbers with small errors, you will get big errors.  Greg 
Sign up for the SourceForge newsletter:
No, thanks