4 Replies Latest reply on Jul 4, 2019 2:37 PM by Fofana

    Strange Issue in Adding Decimals using XSL

    Fofana

      Can someone explain why I am getting This?

      All I am doing, is to add <amount> values from XML, in this case 5 times the value 1.190 (the zero is there because of the 3-digit format)

      I noticed the funny result that occurs while adding more than 5 times, but below that all is fine.

       

      This is the code

       

       

       

      This is the result:

      Normally 5 X 1.190 = 5.95

      where do all digits come from? I am using FOP 1.0

      How can I avoid that?

      This can be seen as minor issue, some may say just need to round, but here we have around 800 transactions to add, and there may be discrepancies between values

       

      Thanks

        • 1. Re: Strange Issue in Adding Decimals using XSL
          cormaco

          where do all digits come from? I am using FOP 1.0

          How can I avoid that?

          This is a problem of floating point arithmetics:

          https://en.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems

           

          If your data have at maximum 3 decimal digits, then you can avoid the problem by multiplying your numbers by 1000

          thus making them integers and then adding them and dividing the end result by 1000 to get the end result.

          • 2. Re: Strange Issue in Adding Decimals using XSL
            Fofana

            Thanks for your suggestion. I am not really an XSL expert, just new to it.

            I have the feeling it doesn't give much flexibility to manipulate the paths.

            I did this:

            <fo:block>

              <xsl:variable name="totamnt">

                  <xsl:value-of select="sum (key('groupBycategory', concat(subscriptionAttributes[parameter='CATEGORY_NAME_FR']/value,../../../accountName))/totalAmount * 1000)" />

              </xsl:variable>

              <xsl:value-of select="($totamnt) div 1000" />

            </fo:block>

             

            And got this error: Can not convert #NUMBER to a Nodelist. See picture

            I guess it's the issue of SUM (function()) which is not allowed and maybe I have to call a function passing whole path as variable.? Not sure

            • 3. Re: Strange Issue in Adding Decimals using XSL
              cormaco

              I have recreated your issue here:

               

              with xmldata(xmlval) as (
              select xmltype(
              '<Values>
                  <Val>1.190</Val>
                  <Val>1.190</Val>
                  <Val>1.190</Val>
                  <Val>1.190</Val>
                  <Val>1.190</Val>
              </Values>') from dual)
              select xmlcast(xmlquery('sum(/Values/Val * 1000)' passing xmlval returning content) as float) as result
              from xmldata
              
              XVM-01004: [XPTY0004] Expression type does not match a required type
              
              

               

              The problem is that /Values/Val represents a nodelist, as your error message says and you can't multiply a nodelist with a number.

              The fix is to loop over the nodelist:

              select 
                  xmlcast(xmlquery('sum(for $v in /Values/Val return $v * 1000)' passing xmlval returning content) as float) / 1000 as result
              from xmldata
              
              
               RESULT
              -------
              5.95000
              
              
              
              • 4. Re: Strange Issue in Adding Decimals using XSL
                Fofana

                cormaco Thank you for the sample code and explanation.

                I understand the logic but I see that you are using SQL query on XML data. But in my case I am working purely with XSL code, so I cannot do things like:

                sum (for each .....) which is the equivalent of your sum(for $v in /Values/Val return $v * 1000)

                 

                So alternatively, I am trying this technic of passing the whole nodelist as parameter to a function that loops through itself...(found here on forum)

                <xsl:template name="calculateSum">

                    <xsl:param name="liste"/>

                        <xsl:choose>

                            <xsl:when test="$liste">

                                <xsl:variable name="prem" select="$liste[1]/totalAmount"/>

                                 <xsl:variable name="firstVal">

                                     <xsl:call-template name="getNewValue">

                                         <xsl:with-param name="valueToMultiply">

                                         <xsl:value-of select="$prem" />

                                     </xsl:with-param>                    

                                    </xsl:call-template>

                                 </xsl:variable>

                                 <xsl:variable name="reste">

                                     <xsl:call-template name="calculateSum">

                                         <xsl:with-param name="liste" select="$liste[position()!=1]"/>

                                     </xsl:call-template>

                                </xsl:variable>

                                <xsl:value-of select="$firstVal + $reste"/>

                           </xsl:when>

                        <xsl:otherwise>0</xsl:otherwise>

                      </xsl:choose>

                </xsl:template>

                 

                Unfortunately, this is costly when the XML has a lot of data meaning the processing time gets longer and sometimes system aborts....

                Anyway, I have to assign correct answer to your suggestion

                Thanks