You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@vxquery.apache.org by pr...@apache.org on 2012/08/17 21:50:36 UTC

svn commit: r1374408 - /incubator/vxquery/trunk/vxquery/vxquery-core/src/main/java/org/apache/vxquery/runtime/functions/cast/CastToStringOperation.java

Author: prestonc
Date: Fri Aug 17 19:50:35 2012
New Revision: 1374408

URL: http://svn.apache.org/viewvc?rev=1374408&view=rev
Log:
VXQUERY-47 Update the writing of double and float using an algorithm found in an older paper. The values are now being printed with the value saved.

Modified:
    incubator/vxquery/trunk/vxquery/vxquery-core/src/main/java/org/apache/vxquery/runtime/functions/cast/CastToStringOperation.java

Modified: incubator/vxquery/trunk/vxquery/vxquery-core/src/main/java/org/apache/vxquery/runtime/functions/cast/CastToStringOperation.java
URL: http://svn.apache.org/viewvc/incubator/vxquery/trunk/vxquery/vxquery-core/src/main/java/org/apache/vxquery/runtime/functions/cast/CastToStringOperation.java?rev=1374408&r1=1374407&r2=1374408&view=diff
==============================================================================
--- incubator/vxquery/trunk/vxquery/vxquery-core/src/main/java/org/apache/vxquery/runtime/functions/cast/CastToStringOperation.java (original)
+++ incubator/vxquery/trunk/vxquery/vxquery-core/src/main/java/org/apache/vxquery/runtime/functions/cast/CastToStringOperation.java Fri Aug 17 19:50:35 2012
@@ -28,11 +28,39 @@ import edu.uci.ics.hyracks.data.std.util
 import edu.uci.ics.hyracks.data.std.util.ByteArrayAccessibleOutputStream;
 
 public class CastToStringOperation extends AbstractCastToOperation {
+    private static long getPowerOf10(double value, long max, long min) {
+        for (long i = min; i < max; i++) {
+            if (Math.pow(10, i) > value)
+                return i;
+        }
+        return max;
+    }
+
+    /**
+     * Returns 0 if positive, nonzero if negative.
+     * 
+     * @param value
+     * @return
+     */
+    public static boolean isNumberPostive(long value) {
+        return ((value & 0x8000000000000000L) == 0 ? true : false);
+    }
+
     private ByteArrayAccessibleOutputStream baaos = new ByteArrayAccessibleOutputStream();
     private ArrayBackedValueStorage abvsInner = new ArrayBackedValueStorage();
     private DataOutput dOutInner = abvsInner.getDataOutput();
     int returnTag = ValueTag.XS_STRING_TAG;
     private final char[] hex = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+    private static final int DOUBLE_MANTISSA_BITS = 52; // size of the mantissa in bits
+    private static final int DOUBLE_MANTISSA_OFFSET = -1075;
+    private static final int DOUBLE_EXPONENT_MAX = 1023;
+    private static final int DOUBLE_EXPONENT_MIN = -1022;
+    private static final int FLOAT_MANTISSA_BITS = 23; // size of the mantissa in bits
+    private static final int FLOAT_MANTISSA_OFFSET = -150;
+    private static final int FLOAT_EXPONENT_MAX = 127;
+    private static final int FLOAT_EXPONENT_MIN = -126;
+    private static final int b = 2; // base of stored value
+    private static final int B = 10; // base of printed value
 
     @Override
     public void convertAnyURI(UTF8StringPointable stringp, DataOutput dOut) throws SystemException, IOException {
@@ -102,7 +130,7 @@ public class CastToStringOperation exten
             long pow10 = (long) Math.pow(10, nDigits - 1);
             int start = Math.max(decimalPlace, nDigits - 1);
             int end = Math.min(0, decimalPlace);
-            
+
             for (int i = start; i >= end; --i) {
                 if (i >= nDigits || i < 0) {
                     writeChar('0', dOutInner);
@@ -129,7 +157,8 @@ public class CastToStringOperation exten
             CastToDecimalOperation castToDecimal = new CastToDecimalOperation();
             castToDecimal.convertDouble(doublep, dOutInner);
             XSDecimalPointable decp = new XSDecimalPointable();
-            decp.set(abvsInner.getByteArray(), abvsInner.getStartOffset() + 1, abvsInner.getLength());
+            decp.set(abvsInner.getByteArray(), abvsInner.getStartOffset() + 1,
+                    XSDecimalPointable.TYPE_TRAITS.getFixedLength());
             convertDecimal(decp, dOut);
         } else {
             convertDoubleCanonical(doublep, dOut);
@@ -147,43 +176,101 @@ public class CastToStringOperation exten
             writeCharSequence("INF", dOutInner);
         } else if (Double.isNaN(value)) {
             writeCharSequence("NaN", dOutInner);
+        } else if (value == 0) {
+            if (!isNumberPostive((long) value)) {
+                writeChar('-', dOutInner);
+            }
+            writeCharSequence("0.0", dOutInner);
         } else {
+            /*
+             * The double to string algorithm is based on a paper by Robert G Burger and 
+             * R Kent Dybvig titled "Print Floating-Point Numbers Quickly and Accurately".
+             */
+            long bits = Double.doubleToLongBits(value);
+            boolean decimalPlaced = false;
+
+            int e = (int) ((bits >> 52) & 0x7ffL);
+            long f = (e == 0) ? (bits & 0xfffffffffffffL) << 1 : (bits & 0xfffffffffffffL) | 0x10000000000000L;
+            e = e + DOUBLE_MANTISSA_OFFSET;
+
+            // Initialize variables
+            double r, s, mPlus, mMinus;
+            if (e >= 0) {
+                if (f == Math.pow(b, DOUBLE_MANTISSA_BITS - 1)) {
+                    r = f * Math.pow(b, e) * 2;
+                    s = 2;
+                    mPlus = Math.pow(b, e);
+                    mMinus = Math.pow(b, e + 1);
+                } else {
+                    r = f * Math.pow(b, e + 1) * 2;
+                    s = b * 2;
+                    mPlus = Math.pow(b, e);
+                    mMinus = Math.pow(b, e);
+                }
+            } else {
+                if (e == DOUBLE_EXPONENT_MIN || f != Math.pow(b, DOUBLE_MANTISSA_BITS - 1)) {
+                    r = f * Math.pow(b, e) * 2;
+                    s = 2;
+                    mPlus = Math.pow(b, e);
+                    mMinus = Math.pow(b, e + 1);
+                } else {
+                    r = f * Math.pow(b, e + 1) * 2;
+                    s = b * 2;
+                    mPlus = Math.pow(b, e);
+                    mMinus = Math.pow(b, e);
+                }
+            }
+
+            double k = Math.ceil(Math.log10((r + mPlus) / s));
+            if (k >= 0) {
+                s = s * Math.pow(B, k);
+            } else {
+                r = r * Math.pow(B, -k);
+                mPlus = mPlus * Math.pow(B, -k);
+                mMinus = mMinus * Math.pow(B, -k);
+            }
+
             if (!isNumberPostive((long) value)) {
                 // Negative result, but the rest of the calculations can be based on a positive value.
                 writeChar('-', dOutInner);
                 value *= -1;
             }
-            byte decimalPlace = 0;
-            // Move the decimal
-            while (value % 1 != 0) {
-                --decimalPlace;
-                value *= 10;
-            }
-            // Remove extra zeros
-            while (value != 0 && value % 10 == 0) {
-                value /= 10;
-                ++decimalPlace;
-            }
-            // Print out the value.
-            int nDigits = (int) Math.log10(value) + 1;
-            long pow10 = (long) Math.pow(10, nDigits - 1);
-            if (nDigits < 0) {
-                writeCharSequence("0.0", dOutInner);
-            } else {
-                for (int i = nDigits - 1; i >= 0; --i) {
-                    writeChar((char) ('0' + Math.floor(value / pow10)), dOutInner);
-                    value %= pow10;
-                    pow10 /= 10;
-                    if (i == nDigits - 1) {
-                        writeChar('.', dOutInner);
-                    } else {
-                        ++decimalPlace;
+
+            double d;
+            while (!Double.isInfinite(mPlus) && !Double.isNaN(mPlus) && !Double.isInfinite(mMinus)
+                    && !Double.isNaN(mMinus)) {
+                if (s == r) {
+                    // Special case where the value is off by a factor of ten.
+                    d = 1;
+                } else {
+                    d = Math.floor((r * B) / s);
+                }
+                r = r * B % s;
+                mPlus = mPlus * B;
+                mMinus = mMinus * B;
+
+                if (r < mMinus && r + mPlus > s) {
+                    if (r * 2 > s) {
+                        d = d + 1;
                     }
+                    writeChar((char) ('0' + d), dOutInner);
+                    break;
+                } else if (r + mPlus > s) {
+                    d = d + 1;
+                    writeChar((char) ('0' + d), dOutInner);
+                    break;
+                } else if (r < mMinus) {
+                    writeChar((char) ('0' + d), dOutInner);
+                    break;
                 }
-                if (nDigits == 1) {
-                    writeChar('0', dOutInner);
+                writeChar((char) ('0' + d), dOutInner);
+                if (!decimalPlaced) {
+                    decimalPlaced = true;
+                    writeChar('.', dOutInner);
                 }
             }
+
+            long decimalPlace = getPowerOf10(value, DOUBLE_EXPONENT_MAX, DOUBLE_EXPONENT_MIN) - 1;
             writeChar('E', dOutInner);
             writeNumberWithPadding(decimalPlace, 1, dOutInner);
         }
@@ -333,43 +420,101 @@ public class CastToStringOperation exten
             writeCharSequence("INF", dOutInner);
         } else if (Float.isNaN(value)) {
             writeCharSequence("NaN", dOutInner);
+        } else if (value == 0) {
+            if (!isNumberPostive((long) value)) {
+                writeChar('-', dOutInner);
+            }
+            writeCharSequence("0.0", dOutInner);
         } else {
+            /*
+             * The double to string algorithm is based on a paper by Robert G Burger and 
+             * R Kent Dybvig titled "Print Floating-Point Numbers Quickly and Accurately".
+             */
+            long bits = Float.floatToIntBits(value);
+            boolean decimalPlaced = false;
+
+            int e = (int) ((bits >> 23) & 0xff);
+            int f = (int) ((e == 0) ? (bits & 0x7fffff) << 1 : (bits & 0x7fffff) | 0x800000);
+            e = e + FLOAT_MANTISSA_OFFSET;
+
+            // Initialize variables
+            double r, s, mPlus, mMinus;
+            if (e >= 0) {
+                if (f == Math.pow(b, FLOAT_MANTISSA_BITS - 1)) {
+                    r = f * Math.pow(b, e) * 2;
+                    s = 2;
+                    mPlus = Math.pow(b, e);
+                    mMinus = Math.pow(b, e + 1);
+                } else {
+                    r = f * Math.pow(b, e + 1) * 2;
+                    s = b * 2;
+                    mPlus = Math.pow(b, e);
+                    mMinus = Math.pow(b, e);
+                }
+            } else {
+                if (e == FLOAT_EXPONENT_MIN || f != Math.pow(b, FLOAT_MANTISSA_BITS - 1)) {
+                    r = f * Math.pow(b, e) * 2;
+                    s = 2;
+                    mPlus = Math.pow(b, e);
+                    mMinus = Math.pow(b, e + 1);
+                } else {
+                    r = f * Math.pow(b, e + 1) * 2;
+                    s = b * 2;
+                    mPlus = Math.pow(b, e);
+                    mMinus = Math.pow(b, e);
+                }
+            }
+
+            double k = Math.ceil(Math.log10((r + mPlus) / s));
+            if (k >= 0) {
+                s = s * Math.pow(B, k);
+            } else {
+                r = r * Math.pow(B, -k);
+                mPlus = mPlus * Math.pow(B, -k);
+                mMinus = mMinus * Math.pow(B, -k);
+            }
+
             if (!isNumberPostive((long) value)) {
                 // Negative result, but the rest of the calculations can be based on a positive value.
                 writeChar('-', dOutInner);
                 value *= -1;
             }
-            byte decimalPlace = 0;
-            // Move the decimal
-            while (value % 1 != 0) {
-                --decimalPlace;
-                value *= 10;
-            }
-            // Remove extra zeros
-            while (value != 0 && value % 10 == 0) {
-                value /= 10;
-                ++decimalPlace;
-            }
-            // Print out the value.
-            int nDigits = (int) Math.log10(value) + 1;
-            long pow10 = (long) Math.pow(10, nDigits - 1);
-            if (nDigits < 0) {
-                writeCharSequence("0.0", dOutInner);
-            } else {
-                for (int i = nDigits - 1; i >= 0; --i) {
-                    writeChar((char) ('0' + Math.floor(value / pow10)), dOutInner);
-                    value %= pow10;
-                    pow10 /= 10;
-                    if (i == nDigits - 1) {
-                        writeChar('.', dOutInner);
-                    } else {
-                        ++decimalPlace;
+
+            double d;
+            while (!Double.isInfinite(mPlus) && !Double.isNaN(mPlus) && !Double.isInfinite(mMinus)
+                    && !Double.isNaN(mMinus)) {
+                if (s == r) {
+                    // Special case where the value is off by a factor of ten.
+                    d = 1;
+                } else {
+                    d = Math.floor((r * B) / s);
+                }
+                r = r * B % s;
+                mPlus = mPlus * B;
+                mMinus = mMinus * B;
+
+                if (r < mMinus && r + mPlus > s) {
+                    if (r * 2 > s) {
+                        d = d + 1;
                     }
+                    writeChar((char) ('0' + d), dOutInner);
+                    break;
+                } else if (r + mPlus > s) {
+                    d = d + 1;
+                    writeChar((char) ('0' + d), dOutInner);
+                    break;
+                } else if (r < mMinus) {
+                    writeChar((char) ('0' + d), dOutInner);
+                    break;
                 }
-                if (nDigits == 1) {
-                    writeChar('0', dOutInner);
+                writeChar((char) ('0' + d), dOutInner);
+                if (!decimalPlaced) {
+                    decimalPlaced = true;
+                    writeChar('.', dOutInner);
                 }
             }
+
+            long decimalPlace = getPowerOf10(value, FLOAT_EXPONENT_MAX, FLOAT_EXPONENT_MIN) - 1;
             writeChar('E', dOutInner);
             writeNumberWithPadding(decimalPlace, 1, dOutInner);
         }
@@ -571,16 +716,6 @@ public class CastToStringOperation exten
         sendStringDataOutput(dOut);
     }
 
-    /**
-     * Returns 0 if positive, nonzero if negative.
-     * 
-     * @param value
-     * @return
-     */
-    public static boolean isNumberPostive(long value) {
-        return ((value & 0x8000000000000000L) == 0 ? true : false);
-    }
-
     private void sendStringDataOutput(DataOutput dOut) throws SystemException, IOException {
         dOut.write(returnTag);
         dOut.write((byte) ((abvsInner.getLength() >>> 8) & 0xFF));