math error or rounding problem Money type - Mailing list pgsql-hackers

From Justin
Subject math error or rounding problem Money type
Date
Msg-id 484B06E8.1050902@emproshunts.com
Whole thread Raw
Responses Re: math error or rounding problem Money type  (Tom Lane <tgl@sss.pgh.pa.us>)
List pgsql-hackers
I believe i have found a math bug/rounding problem with Money type when its used with SUM()...  Postgresql 8.3.1<br
/><br/> --------------- Background on the Problem--------------------<br /><br /> We have gl_trans table with 92,000
rowswith one column containing the positive and negative entries.<br /><br /> In order to make this gl_trans table make
moresense and to group the accounts in correct debits and credits along with type of accounts, A view was created that
doesgrouping and sorting.   To further make things easier the view casted the results into the Money Type just to make
theselect statements that call the view shorter.<br /><br /> All looked great for several weeks till all of sudden the
sumedvalues for all accounts goes out by 0.01.  <br /><br /> I needed to confirm this was a rounding problem and not  a
GLentry that was bad.  ( if we had a bad entry this would scream we have a far bigger problem where the application
allowedan GL entry to be committed that was out of balance)<br /><br /> To confirm that all entries made have equal and
oppositeentry  below select statement was created.  The  gltrans_sequence column is integer key that groups General
Ledgerentries together so all the sides of a specific entry can be found.  <br /><br /> select * <br /> from <br />
(selectgltrans_sequence, sum(gltrans_amount) as Neg from gltrans  where gltrans_amount < 0 group by
gltrans_sequence)as neg,<br /> (select gltrans_sequence, sum(gltrans_amount) as pos from gltrans  where gltrans_amount
>0 group by gltrans_sequence) as pos<br /> where neg.gltrans_sequence = pos.gltrans_sequence <br /> and neg.neg
<>pos.pos*-1<br /><br /> This returns no records as expected...<br /><br /> Now armed with that no entry was bad
Isuspected it had to be with the money data type.<br /> So I added explicit castings <br /><font color="#3366ff"><br />
select* <br /> from <br /> (select gltrans_sequence, sum(gltrans_amount::text::money) as Neg from gltrans  where
gltrans_amount< 0 group by gltrans_sequence) as neg,<br /> (select gltrans_sequence,
sum(gltrans_amount::text::money)as pos from gltrans  where gltrans_amount > 0 group by gltrans_sequence) as pos<br
/>where neg.gltrans_sequence = pos.gltrans_sequence <br /> and neg.neg <> pos.pos*-1</font><br />
----------------<br/><font color="#3366ff">select * <br /> from <br /> (select gltrans_sequence,
sum(gltrans_amount::text::money)as Neg from gltrans  where gltrans_amount < 0 group by gltrans_sequence) as neg,<br
/>(select gltrans_sequence, sum(gltrans_amount::text::money) as pos from gltrans  where gltrans_amount > 0 group by
gltrans_sequence)as pos<br /> where neg.gltrans_sequence = pos.gltrans_sequence <br /> and neg.neg::text::money
<>pos.pos::text::money*-1</font><br /> -------------<br /><font color="#3366ff">select * <br /> from <br />
(selectgltrans_sequence, sum(gltrans_amount) as Neg from gltrans  where gltrans_amount < 0 group by
gltrans_sequence)as neg,<br /> (select gltrans_sequence, sum(gltrans_amount) as pos from gltrans  where gltrans_amount
>0 group by gltrans_sequence) as pos<br /> where neg.gltrans_sequence = pos.gltrans_sequence <br /> and
neg.neg::text::money<> pos.pos::text::money*-1</font><br /><br /> -------------------<br />  Nothing  resulted in
showinga entry that was out of balance.  <br /><br /><br /><br /><br /><br /> ----------------------Identifying the
problem---------------------------<br /><br /> So i turned my attention to the view which casted numeric type to
Money. View is called trailbalance <br /><br /> ------------The Bad Select Statement that creates the View
--------------<br/><font color="#3366ff">SELECT p.period_id, p.period_start, p.period_end, a.accnt_id,<br />       
a.accnt_number,a.accnt_descrip, p.period_yearperiod_id,<br />        a.accnt_type,<br />        SUM(CASE WHEN
g.gltrans_date< p.period_start<br />                 THEN g.gltrans_amount ELSE 0.0<br />           
END)::text::moneyAS beginbalance,<br />        SUM(CASE WHEN g.gltrans_date <= p.period_end<br />                 
ANDg.gltrans_date >= p.period_start<br />                  AND g.gltrans_amount <= 0::numeric<br />
               THEN g.gltrans_amount ELSE 0.0<br />            END)::text::money AS negative,<br />        SUM(CASE
WHENg.gltrans_date <= p.period_end<br />                  AND g.gltrans_date >= p.period_start<br />
                AND g.gltrans_amount >= 0::numeric<br />                 THEN g.gltrans_amount ELSE 0.0<br />
          END)::text::money AS positive,<br />        SUM(CASE WHEN g.gltrans_date <= p.period_end<br />
                AND g.gltrans_date >= p.period_start<br />                 THEN g.gltrans_amount ELSE 0.0<br />
          END)::text::money AS difference,<br />        SUM(CASE WHEN g.gltrans_date <= p.period_end<br />
               THEN g.gltrans_amount ELSE 0.0<br />            END)::text::money AS endbalance<br />   FROM period p<br
/> CROSS JOIN accnt a<br />   LEFT JOIN gltrans g ON (g.gltrans_accnt_id = a.accnt_id<br />                          
ANDg.gltrans_posted = true)<br />   where p.period_id = 58<br />  group by  p.period_id, p.period_start, p.period_end,
a.accnt_id,<br/>        a.accnt_number, a.accnt_descrip, p.period_yearperiod_id,<br />        a.accnt_type<br />  <br
/> ORDER BY p.period_id, a.accnt_number;</font><br /> ---------------End Select --------------------<br /><br /><br />
Thequery that calls this View  <br /><br /> ------------------<br /><font color="#3366ff">Select <br />     sum(
beginBalance) as beginbalance, <br />     sum( negative ) as debit, <br />     sum( positive ) as credit, <br />    
sum(difference ) as difference, <br />     sum( endbalance) as endbalance <br />  from trailbalance </font><br />
---------------------<br/><br /> Result is  <br /><br /><font color="#ff0000">-$0.01</font>    -$11,250,546.74   
$11,250,546.75 <fontcolor="#ff0000"> -$0.02</font>   <font color="#ff0000">-$0.01</font><br /><br /> This be wrong. <br
/><br/> Figuring it must be Money type dropped and recreated the view without the money casting. <br /><br />
------------TheFixed Select Statement that creates the View --------------<br /><font color="#3366ff">SELECT
p.period_id,p.period_start, p.period_end, a.accnt_id, a.accnt_number, a.accnt_descrip, p.period_yearperiod_id,
a.accnt_type,sum(<br />         CASE<br />             WHEN g.gltrans_date < p.period_start THEN g.gltrans_amount<br
/>            ELSE 0.0<br />         END) AS beginbalance, sum(<br />         CASE<br />             WHEN
g.gltrans_date<= p.period_end AND g.gltrans_date >= p.period_start AND g.gltrans_amount <= 0::numeric THEN
g.gltrans_amount<br/>             ELSE 0.0<br />         END) AS negative, sum(<br />         CASE<br />            
WHENg.gltrans_date <= p.period_end AND g.gltrans_date >= p.period_start AND g.gltrans_amount >= 0::numeric
THENg.gltrans_amount<br />             ELSE 0.0<br />         END) AS positive, sum(<br />         CASE<br />
           WHEN g.gltrans_date <= p.period_end AND g.gltrans_date >= p.period_start THEN g.gltrans_amount<br />
           ELSE 0.0<br />         END) AS difference, sum(<br />         CASE<br />             WHEN g.gltrans_date
<=p.period_end THEN g.gltrans_amount<br />             ELSE 0.0<br />         END) AS endbalance<br />    FROM
periodp<br />   CROSS JOIN accnt a<br />    LEFT JOIN gltrans g ON g.gltrans_accnt_id = a.accnt_id AND g.gltrans_posted
=true<br />   GROUP BY p.period_id, a.accnt_number, p.period_start, p.period_end, a.accnt_id, a.accnt_descrip,
p.period_yearperiod_id,a.accnt_type<br />   ORDER BY p.period_id, a.accnt_number;</font><br /> ---------------End
Select--------------------<br /><br /><br /> The above query results in this  which is what i would expect. <br /><br
/>0.00000000    -11250546.74375232     11250546.74375232     0.00000000     0.00000000<br /><br /><br /> Now knowing
forsure its in Money type casting i do this select statement <br /> ----------------------<br /><font
color="#3333ff">Select<br />     '2',<br />     sum( beginBalance )::text::money as beginbalance, <br />     sum(
negative)::text::money as debit, <br />     sum( positive )::text::money as credit, <br />     sum( difference
)::text::moneyas difference, <br />     sum( endbalance)::text::money as endbalance <br />  from trailbalance <br />
 union<br />  Select <br />     '1',<br />     sum( beginBalance::text::money) as beginbalance, <br />     sum(
negative::text::money)as debit, <br />     sum( positive::text::money) as credit, <br />     sum(
difference::text::money)as difference, <br />     sum( endbalance::text::money) as endbalance <br />  from trailbalance
</font><br/> -------------------------<br /><br /> The results I think very interesting <br /> "1"  <font
color="#ff0000">-$0.01</font>  -$11,250,546.74     $11,250,546.75   <font color="#ff0000">-$0.02;</font>   <font
color="#ff0000">-$0.01</font><br/> "2"  $0.00    -$11,250,546.74     $11,250,546.74    $0.00      $0.00<br /><br /> As
youcan see  casting to money before sum() is called are incorrect<br /><br /> Can anyone else confirm this odd behavior
whencasting to Money type.<br /><br /> Thank you for your time and patience reading this long post....<br /><br /><br
/><br/> 

pgsql-hackers by date:

Previous
From: Magnus Hagander
Date:
Subject: Re: TODO, FAQs to Wiki?
Next
From: Tom Lane
Date:
Subject: Re: math error or rounding problem Money type