pavlin
asked on
Number 'normalisation' in XSL
Hello!
I have to normalise the number that i have in XML, i.e. i have to calculate its mantissa and exponent.
So if i have 123456 it should be normalised as 1,23456e+5.
Unfortunatelly, i have to use only pure xsl, no extension functions or scripts are allowed.
Please give me working example asap.
Thanks.
I have to normalise the number that i have in XML, i.e. i have to calculate its mantissa and exponent.
So if i have 123456 it should be normalised as 1,23456e+5.
Unfortunatelly, i have to use only pure xsl, no extension functions or scripts are allowed.
Please give me working example asap.
Thanks.
You should look at the xsl:decimal-format element and the associated function format-number. I have studied both of these and I can't see anyway of doing it :(
I've looked at the number formatting stuff as well, and it doesn't appear to work the way we'd like. However, in an extreme masochistic XSL session, I've managed to build a transform that will normalize a number.
Here it is in all its grotesque glory:
-------------------------- ---------- ---------- ---------- ---
<!--
Generic template to normalize a number
Pass a param called "num" containing the number you wish
to normalize
-->
<xsl:template name="normalize-number">
<xsl:param name="num" select="0"/>
<xsl:call-template name="do-normalize">
<xsl:with-param name="mantissa" select="$num"/>
<xsl:with-param name="exp" select="0"/>
</xsl:call-template>
</xsl:template>
<!--
Simple template for formatting the exponent. This
is needed to ensure that the '+' sign shows up for
positive exponents.
-->
<xsl:template name="format-exp">
<xsl:param name="exp" select="0"/>
<xsl:if test="$exp >= 0">
<xsl:text>+</xsl:text>
</xsl:if>
<xsl:value-of select="$exp"/>
</xsl:template>
<!--
This is the body of the normalizer. It recursively
processes the number until its in the correct form
-->
<xsl:template name="do-normalize">
<xsl:param name="mantissa" select="0"/>
<xsl:param name="exp" select="0"/>
<xsl:choose>
<!-- If the mantissa is 0, we're done. Just print it out -->
<xsl:when test="$mantissa = 0.0 or $mantissa = -0.0">
<xsl:value-of select="format-number(0.0, '#,##0.00000')"/>
<xsl:text>e</xsl:text>
<xsl:call-template name="format-exp">
<xsl:with-param name="exp" select="$exp"/>
</xsl:call-template>
</xsl:when>
<!-- If we've gotten the number normalized, then print it out.
This is for a positive number -->
<xsl:when test="$mantissa >= 1.0 and $mantissa < 10.0">
<xsl:value-of select="format-number (number($mantissa), '#,##0.00000')"/>
<xsl:text>e</xsl:text>
<xsl:call-template name="format-exp">
<xsl:with-param name="exp" select="$exp"/>
</xsl:call-template>
</xsl:when>
<!-- We've gotten a negative number normalized, print it -->
<xsl:when test="$mantissa > -10.0 and $mantissa <= -1.0">
<xsl:value-of select="format-number (number($mantissa), '#,##0.00000')"/>
<xsl:text>e</xsl:text>
<xsl:call-template name="format-exp">
<xsl:with-param name="exp" select="$exp"/>
</xsl:call-template>
</xsl:when>
<!-- In this case we've got a number greater than abs(10.0). We
add one to the exponent, divide the mantissa by 10, and recurse -->
<xsl:when test="$mantissa >= 10.0 or $mantissa <= -10.0">
<xsl:call-template name="do-normalize">
<xsl:with-param name="mantissa" select="$mantissa div 10"/>
<xsl:with-param name="exp" select="$exp + 1"/>
</xsl:call-template>
</xsl:when>
<!-- In this case we have a number whose abs() < 1.0. We subtract 1
from the exponent, multiply the mantissa by 10 and recurse. -->
<xsl:when test="$mantissa > -1.0 and $mantissa < 1.0">
<xsl:call-template name="do-normalize">
<xsl:with-param name="mantissa" select="$mantissa * 10"/>
<xsl:with-param name="exp" select="$exp - 1"/>
</xsl:call-template>
</xsl:when>
</xsl:choose>
</xsl:template>
-------------------------- ---------- ---------- ---------- ---
It's probably one of the most wretched things one would ever want to do with XSL, but it works. I've tested it using Stylus and it handled every test case I threw at it.
--Steve
Here it is in all its grotesque glory:
--------------------------
<!--
Generic template to normalize a number
Pass a param called "num" containing the number you wish
to normalize
-->
<xsl:template name="normalize-number">
<xsl:param name="num" select="0"/>
<xsl:call-template name="do-normalize">
<xsl:with-param name="mantissa" select="$num"/>
<xsl:with-param name="exp" select="0"/>
</xsl:call-template>
</xsl:template>
<!--
Simple template for formatting the exponent. This
is needed to ensure that the '+' sign shows up for
positive exponents.
-->
<xsl:template name="format-exp">
<xsl:param name="exp" select="0"/>
<xsl:if test="$exp >= 0">
<xsl:text>+</xsl:text>
</xsl:if>
<xsl:value-of select="$exp"/>
</xsl:template>
<!--
This is the body of the normalizer. It recursively
processes the number until its in the correct form
-->
<xsl:template name="do-normalize">
<xsl:param name="mantissa" select="0"/>
<xsl:param name="exp" select="0"/>
<xsl:choose>
<!-- If the mantissa is 0, we're done. Just print it out -->
<xsl:when test="$mantissa = 0.0 or $mantissa = -0.0">
<xsl:value-of select="format-number(0.0,
<xsl:text>e</xsl:text>
<xsl:call-template name="format-exp">
<xsl:with-param name="exp" select="$exp"/>
</xsl:call-template>
</xsl:when>
<!-- If we've gotten the number normalized, then print it out.
This is for a positive number -->
<xsl:when test="$mantissa >= 1.0 and $mantissa < 10.0">
<xsl:value-of select="format-number (number($mantissa), '#,##0.00000')"/>
<xsl:text>e</xsl:text>
<xsl:call-template name="format-exp">
<xsl:with-param name="exp" select="$exp"/>
</xsl:call-template>
</xsl:when>
<!-- We've gotten a negative number normalized, print it -->
<xsl:when test="$mantissa > -10.0 and $mantissa <= -1.0">
<xsl:value-of select="format-number (number($mantissa), '#,##0.00000')"/>
<xsl:text>e</xsl:text>
<xsl:call-template name="format-exp">
<xsl:with-param name="exp" select="$exp"/>
</xsl:call-template>
</xsl:when>
<!-- In this case we've got a number greater than abs(10.0). We
add one to the exponent, divide the mantissa by 10, and recurse -->
<xsl:when test="$mantissa >= 10.0 or $mantissa <= -10.0">
<xsl:call-template name="do-normalize">
<xsl:with-param name="mantissa" select="$mantissa div 10"/>
<xsl:with-param name="exp" select="$exp + 1"/>
</xsl:call-template>
</xsl:when>
<!-- In this case we have a number whose abs() < 1.0. We subtract 1
from the exponent, multiply the mantissa by 10 and recurse. -->
<xsl:when test="$mantissa > -1.0 and $mantissa < 1.0">
<xsl:call-template name="do-normalize">
<xsl:with-param name="mantissa" select="$mantissa * 10"/>
<xsl:with-param name="exp" select="$exp - 1"/>
</xsl:call-template>
</xsl:when>
</xsl:choose>
</xsl:template>
--------------------------
It's probably one of the most wretched things one would ever want to do with XSL, but it works. I've tested it using Stylus and it handled every test case I threw at it.
--Steve
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
<xsl:when test="($man > 10) or ($man < -10)">
should be:
<xsl:when test="($man >= 10) or ($man <= -10)">
sorry.
should be:
<xsl:when test="($man >= 10) or ($man <= -10)">
sorry.
It seems as if my choice of the comments was misleading. When I said "print" in the comments I was really referring to formatting the normalized number. One could easily use the code to assign a value to a variable as in the following:
<xsl:variable name="normalized">
<xsl:call-template name="normalize-number">
<xsl:with-param name="num" value="$value-to-normalize "/>
</xsl:call-template>
</xsl:variable>
Sorry for the confusion...
--Steve
<xsl:variable name="normalized">
<xsl:call-template name="normalize-number">
<xsl:with-param name="num" value="$value-to-normalize
</xsl:call-template>
</xsl:variable>
Sorry for the confusion...
--Steve