How to convert number from scientific notation to decimal number in XSLT

XSL template fragment free to use.

Why this implementation?

When googling I've found only one working implementation written by Michael Case from University of California. Unfortunately I've reached some errors (e.g. working with positive exponents). Bellow is a XSL template fragment used in ORM Designer import which better suits our needs. If it's helpful to you feel free to use it in your code, it's released under the MIT licence (Free for commercial or non-commercial use).

How to use template

Execute template with inputVal parameter:

<xsl:call-template name="convertSciToNumString">
    <xsl:with-param name="inputVal">-0.312E1</xsl:with-param>
</xsl:call-template>

Result:

-3.12

Implementation notes

This template is developed and tested only on C++ XERCES 3.0 and C++ XALAN 1.1. Templates doesn't contains any XERCES / XALAN language specifics, so should work on any other XSLT transformer. If you find any error or create an interesting extension, please let me know I'll update this post.

XSLT template for scientific number to decimal conversion

Copy and paste the code bellow to your XSL template:

<xsl:variable name="max-exp">
  <xsl:value-of select="'0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'"/>
</xsl:variable>

<xsl:template name="convertSciToNumString" >
  <xsl:param name="inputVal" select="0"/>

  <xsl:variable name="numInput">
    <xsl:value-of select="translate(string($inputVal),'e','E')"/>
  </xsl:variable>

  <xsl:choose>
    <xsl:when test="number($numInput)">
        <xsl:value-of select="$numInput"/>
    </xsl:when> 
    <xsl:otherwise>

        <!-- ==== Mantisa ==== -->
        <xsl:variable name="numMantisa">
            <xsl:value-of select="number(substring-before($numInput,'E'))"/>
        </xsl:variable>

        <!-- ==== Exponent ==== -->
        <xsl:variable name="numExponent">
            <xsl:choose>
                <xsl:when test="contains($numInput,'E+')">
                    <xsl:value-of select="substring-after($numInput,'E+')"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="substring-after($numInput,'E')"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:variable>

         <!-- ==== Coefficient ==== -->
        <xsl:variable name="numCoefficient">
            <xsl:choose>
                <xsl:when test="$numExponent > 0">
                    <xsl:text>1</xsl:text>
                    <xsl:value-of select="substring($max-exp, 1, number($numExponent))"/>
                </xsl:when>
                <xsl:when test="$numExponent < 0">
                    <xsl:text>0.</xsl:text>
                    <xsl:value-of select="substring($max-exp, 1, -number($numExponent)-1)"/>
                    <xsl:text>1</xsl:text>
                </xsl:when>
                <xsl:otherwise>1</xsl:otherwise>
            </xsl:choose>
        </xsl:variable>
        <xsl:value-of select="number($numCoefficient) * number($numMantisa)"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

Updated template version

Today I received updated version XSLT template from Krzysztof Fink-Finowicki. This updated version should work correctly also with numbers starting and ending with dot. Ending dot: "0." or "3.E-5", starting dot: ".3" or "-.3" or "+.3" an finally, zero case: ".0", "0.", "0.0"

I have no time to test it, so please use it on your own risk ;-)

<xsl:template name="convertSciToNumString" >
  <xsl:param name="inputVal" select="0"/>

  <xsl:variable name="inputCor">
    <xsl:choose>
<!-- <xsl:when test="starts-with($inputVal,'.')">  inaccessible in XSLT 1.0  -->
      <xsl:when test="substring($inputVal, 1, 1) = '.'"><xsl:value-of select="concat('0', $inputVal)"/></xsl:when>
      <xsl:when test="substring($inputVal, 1, 2) = '+.'"><xsl:value-of select="concat('0', substring($inputVal, 2))"/></xsl:when>
      <xsl:when test="substring($inputVal, 1, 2) = '-.'"><xsl:value-of select="concat('-0', substring($inputVal, 2))"/></xsl:when>
      <xsl:otherwise><xsl:value-of select="$inputVal"/></xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:variable name="numInput">
    <xsl:value-of select="translate(string($inputCor),'e','E')"/>
  </xsl:variable>

  <xsl:choose>
<!-- <xsl:when test="ends-with($numInput,'.')">  inaccessible in XSLT 1.0  -->
    <xsl:when test="substring($numInput, string-length($numInput), 1) = '.'">
      <xsl:value-of select="substring-before($numInput,'.')"/>
    </xsl:when> 
    <xsl:when test="$numInput = '0.0'">
      <xsl:text>0</xsl:text>
    </xsl:when> 
    <xsl:when test="number($numInput)">
      <xsl:value-of select="$numInput"/>
    </xsl:when> 
    <xsl:otherwise>

      <!-- ==== Mantisa ==== -->
      <xsl:variable name="numMantisa">
        <xsl:choose>
            <xsl:when test="contains($numInput,'.E')">
                <xsl:value-of select="substring-before($numInput,'.E')"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="substring-before($numInput,'E')"/>
            </xsl:otherwise>
        </xsl:choose>
      </xsl:variable>

...and so on.

Tests for converting template

Bellow is a small XML and XSLT which I used to test if the template is correct.

Testing data


<data>
    <value expected="9" value="900E-2" />
    <value expected="9" value=".009E+3" />
    <value expected="0.09" value="9E-2" />
    <value expected="900" value="0.9E+3" />
    <value expected="3000" value="3E+3" />
    <value expected="40" value="4.e+001" />
    <value expected="40" value="4.0e+001" />
    <value expected="40.099999999999994" value="4.01e+001" />
    <value expected="41" value="4.1e+001"/>
    <value expected="168" value="1.68e+002"/>
    <value expected="9" value="900E-2" />
    <value expected="16.8" value="1.68e+001"/>
    <value expected="1" value="1"/>
    <value expected="-2" value="-2"/>
    <value expected="0.003" value="3E-3"/>
    <value expected="-0.0002" value="-2E-4"/>
    <value expected="0.0005" value=".5E-3"/>
    <value expected="3000" value=".3E4"/>
    <value expected="0.00002" value=".2E-4"/>
    <value expected="-600" value="-.6E3"/>
    <value expected="-0.00006" value="-.6E-4"/>
    <value expected="602199999999999960000000" value="6.022E23"/>
    <value expected="3.21" value=".321E1"/>
    <value expected="3.12" value="312E-2"/>
    <value expected="-3.12" value="-0.312E1"/>
    <value expected="-3.12" value="-312E-2"/>
    <value expected="92.39" value="923.9E-1"/>
    <value expected="0.01625" value="1.625E-2"/>
    <value expected="0.00001" value="1.E-5"/>
    <value expected="0.0021416" value="2.14160E-3"/>
    <value expected="0.0021416" value="2.14160e-3"/>
</data>

Testing routine

<!-- ============================================================ TEST -->
<xsl:template match="data">
    <results>
        <xsl:apply-templates/>
    </results>
</xsl:template>

<xsl:template match="value">
    <xsl:param name="result">
        <xsl:call-template name="convertSciToNumString">
            <xsl:with-param name="inputVal" select="@value"/>
        </xsl:call-template>
    </xsl:param>

    <xsl:if test="$result = @expected">
        <result state="OK" value="{@value}" result="{$result}"/>
    </xsl:if>

    <xsl:if test="$result != @expected">
        <result state="FAILED" value="{@value}" result="{$result}" expected="{@expected}"/>
    </xsl:if>
</xsl:template>

Result for this tests

<results xmlns:Atx="http://www.inventic.cz" xmlns:xalan="http://xml.apache.org/xalan" xmlns:dyn="http://exslt.org/dynamic" xmlns:yaml="http://www.inventic.cz/yaml2xml">
 <result state="OK" value="900E-2" result="9"/>
 <result state="OK" value=".009E+3" result="9"/>
 <result state="OK" value="9E-2" result="0.09"/>
 <result state="OK" value="0.9E+3" result="900"/>
 <result state="OK" value="3E+3" result="3000"/>
 <result state="OK" value="4.e+001" result="40"/>
 <result state="OK" value="4.0e+001" result="40"/>
 <result state="OK" value="4.01e+001" result="40.099999999999994"/>
 <result state="OK" value="4.1e+001" result="41"/>
 <result state="OK" value="1.68e+002" result="168"/>
 <result state="OK" value="900E-2" result="9"/>
 <result state="OK" value="1.68e+001" result="16.8"/>
 <result state="OK" value="1" result="1"/>
 <result state="OK" value="-2" result="-2"/>
 <result state="OK" value="3E-3" result="0.003"/>
 <result state="OK" value="-2E-4" result="-0.0002"/>
 <result state="OK" value=".5E-3" result="0.0005"/>
 <result state="OK" value=".3E4" result="3000"/>
 <result state="OK" value=".2E-4" result="0.00002"/>
 <result state="OK" value="-.6E3" result="-600"/>
 <result state="OK" value="-.6E-4" result="-0.00006"/>
 <result state="OK" value="6.022E23" result="602199999999999960000000"/>
 <result state="OK" value=".321E1" result="3.21"/>
 <result state="OK" value="312E-2" result="3.12"/>
 <result state="OK" value="-0.312E1" result="-3.12"/>
 <result state="OK" value="-312E-2" result="-3.12"/>
 <result state="OK" value="923.9E-1" result="92.39"/>
 <result state="OK" value="1.625E-2" result="0.01625"/>
 <result state="OK" value="1.E-5" result="0.00001"/>
 <result state="OK" value="2.14160E-3" result="0.0021416"/>
 <result state="OK" value="2.14160e-3" result="0.0021416"/>
</results>

Author: Ludek Vodicka