Bug #61938 | Invalid result from equation of double data return by stored function | ||
---|---|---|---|
Submitted: | 21 Jul 2011 6:57 | Modified: | 24 Jan 2012 10:27 |
Reporter: | William Anthony | Email Updates: | |
Status: | Won't fix | Impact on me: | |
Category: | MySQL Server: Stored Routines | Severity: | S3 (Non-critical) |
Version: | 5.5.14 | OS: | Any |
Assigned to: | CPU Architecture: | Any | |
Tags: | double, FUNCTION, precision, regression |
[21 Jul 2011 6:57]
William Anthony
[21 Jul 2011 6:59]
William Anthony
the stored function used for test case
Attachment: x_netprice_calc.sql (text/x-sql), 999 bytes.
[21 Jul 2011 7:58]
Valeriy Kravchuk
Verified with 5.5.14 on 32-bit Windows: C:\Program Files\MySQL\MySQL Server 5.5\bin>mysql -uroot -proot -P3312 test Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 3 Server version: 5.5.14 MySQL Community Server (GPL) Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> delimiter $$ mysql> CREATE DEFINER=`root`@`localhost` FUNCTION `x_netprice_calc`( -> price double, -> disc1_amt double, disc1_ kode varchar(1), -> disc2_amt double, disc2_ kode varchar(1), -> disc3_amt double, disc3_ kode varchar(1), -> disc4_amt double, disc4_ kode varchar(1)) RETURNS double -> BEGIN -> declare baseprice double; -> declare netprice1 double; -> declare netprice2 double; -> declare netprice3 double; -> -> set baseprice = price - If( -> disc1_kode = "P", -> ROUND((price * disc1_amt)/100,2), -> disc1_amt -> ); -> -> set netprice1 = If( -> disc2_kode = "P", -> ROUND((baseprice * disc2_amt)/100,2), -> disc2_amt -> ); -> -> set netprice2 = If( -> disc3_kode = "P", -> ROUND((baseprice * disc3_amt)/100,2), -> disc3_amt -> ); -> -> set netprice3 = If( -> disc4_kode = "P", -> ROUND((baseprice * disc4_amt)/100,2), -> disc4_amt -> ); -> -> return baseprice - netprice1 - netprice2 - netprice3; -> END$$ Query OK, 0 rows affected (0.47 sec) mysql> delimiter ; mysql> select x_netprice_calc(80000,4,'R',0,'P',0,'P',0,'P') as base, -> x_netprice_calc(80000,4,'R',3,'P',0,'P',0,'P') as net2, -> (x_netprice_calc(80000,4,'R',0,'P',0,'P',0,'P') - -> x_netprice_calc(80000,4,'R',3,'P',0,'P',0,'P')) as base_minus_net2; +-------+----------+--------------------+ | base | net2 | base_minus_net2 | +-------+----------+--------------------+ | 79996 | 77596.12 | 2399.8800000000047 | +-------+----------+--------------------+ 1 row in set (0.38 sec) mysql> select 79996 - 77596.12; +------------------+ | 79996 - 77596.12 | +------------------+ | 2399.88 | +------------------+ 1 row in set (0.00 sec) mysql> select '79996' - '77596.12'; +----------------------+ | '79996' - '77596.12' | +----------------------+ | 2399.8800000000047 | +----------------------+ 1 row in set (0.00 sec) mysql> select '79996' - 77596.12; +--------------------+ | '79996' - 77596.12 | +--------------------+ | 2399.8800000000047 | +--------------------+ 1 row in set (0.00 sec) So, while function clearly returns double and use double in all the intermediate calculations, looks like when we calculate f(...) - f(...) we interpret result returned by function call as string somewhere, and convert it back to double.
[21 Jul 2011 11:14]
Tor Didriksen
CREATE FUNCTION `x_netprice_calc_2`( price double, disc1_amt double, disc1_kode varchar(1), disc2_amt double, disc2_kode varchar(1), disc3_amt double, disc3_kode varchar(1), disc4_amt double, disc4_kode varchar(1)) RETURNS decimal(9,2) ... ... select x_netprice_calc_2(80000,4,'R',0,'P',0,'P',0,'P') as base, x_netprice_calc_2(80000,4,'R',3,'P',0,'P',0,'P') as net2, (x_netprice_calc_2(80000,4,'R',0,'P',0,'P',0,'P') - x_netprice_calc_2(80000,4,'R',3,'P',0,'P',0,'P')) as base_minus_net2; base net2 base_minus_net2 79996.00 77596.12 2399.88
[21 Jul 2011 12:03]
Tor Didriksen
f(...) - f(...) is double minus double, which may yield rounding errors like this. Either change function definition, or round() the result of the subtraction to the desired precision.
[23 Jul 2011 1:16]
William Anthony
Thanks for the workaround, it's worked. But will it be fixed? Why 5.0 and 5.1 not affected?
[24 Jan 2012 10:27]
Tor Didriksen
See "Incompatible Change: The server now includes dtoa, a library for conversion..." http://dev.mysql.com/doc/refman/5.5/en/news-5-5-3.html