diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ComplexVectorPrinter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ComplexVectorPrinter.java index 635ec096266e654196e0ba316bf4b1b073197384..1416ca77c41feada5d35aee74e388cf51f641710 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ComplexVectorPrinter.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ComplexVectorPrinter.java @@ -427,27 +427,14 @@ public final class ComplexVectorPrinter extends VectorPrinter<RAbstractComplexVe @TruffleBoundary private static String encodeComplex(RComplex x, int wr, int dr, int er, int wi, int di, int ei, char cdec, int digits, String naString) { - String buff; - String im; - String re; - boolean flagNegIm = false; - RComplex y; - - double xr = x.getRealPart(); - double xi = x.getImaginaryPart(); - /* IEEE allows signed zeros; strip these here */ - if (xr == 0.0) { - xr = 0.0; - } - if (xi == 0.0) { - xi = 0.0; - } + double xr = RRuntime.normalizeZero(x.getRealPart()); + double xi = RRuntime.normalizeZero(x.getImaginaryPart()); if (RRuntime.isNA(xr) || RRuntime.isNA(xi)) { int g = Math.min(wr + wi + 2, (NB - 1)); String fmt = "%" + Utils.asBlankArg(g) + "s"; - buff = Utils.snprintf(NB, + return Utils.snprintf(NB, fmt, /* was "%*s%*s", R_print.gap, "", */ naString); } else { @@ -456,24 +443,25 @@ public final class ComplexVectorPrinter extends VectorPrinter<RAbstractComplexVe * get strange trailing zeros. But we do want to avoid printing small exponentials that * are probably garbage. */ - y = zprecr(x, digits); + RComplex y = zprecr(x, digits); + String re; if (y.getRealPart() == 0.) { re = DoubleVectorPrinter.encodeReal(y.getRealPart(), wr, dr, er, cdec, naString); } else { re = DoubleVectorPrinter.encodeReal(xr, wr, dr, er, cdec, naString); } - flagNegIm = xi < 0; + boolean flagNegIm = xi < 0; if (flagNegIm) { xi = -xi; } + String im; if (y.getImaginaryPart() == 0.) { im = DoubleVectorPrinter.encodeReal(y.getImaginaryPart(), wi, di, ei, cdec, naString); } else { im = DoubleVectorPrinter.encodeReal(xi, wi, di, ei, cdec, naString); } - buff = snprintf(NB, "%s%s%si", re, flagNegIm ? "-" : "+", im); + return snprintf(NB, "%s%s%si", re, flagNegIm ? "-" : "+", im); } - return buff; } public static String[] format(RAbstractComplexVector value, boolean trim, int nsmall, int width, char decimalMark, PrintParameters pp) { diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/DoubleVectorPrinter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/DoubleVectorPrinter.java index 9ee413f6e6f9a50696f92847f6361c4628c1038d..d3ebedd8fe3c597764ce73f4e78956df9d057640 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/DoubleVectorPrinter.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/DoubleVectorPrinter.java @@ -11,11 +11,7 @@ */ package com.oracle.truffle.r.nodes.builtin.base.printer; -import static com.oracle.truffle.r.nodes.builtin.base.printer.Utils.snprintf; - import java.io.IOException; -import java.math.RoundingMode; -import java.text.DecimalFormat; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.r.runtime.RRuntime; @@ -312,9 +308,9 @@ public final class DoubleVectorPrinter extends VectorPrinter<RAbstractDoubleVect * assumption that R_dec_min_exponent+303 is in range. Representation of 1e+303 has * low error. */ - rPrec = (rPrec * 1e+303) / Math.pow(10, kp + 303); + rPrec = (rPrec * 1e+303) / DECIMAL_WEIGHTS[kp + 303 + DECIMAL_SHIFT]; } else { - rPrec /= Math.pow(10, kp); + rPrec /= DECIMAL_WEIGHTS[kp + DECIMAL_SHIFT]; } if (rPrec < tbl[digits]) { rPrec *= 10.0; @@ -354,7 +350,6 @@ public final class DoubleVectorPrinter extends VectorPrinter<RAbstractDoubleVect double fuzz = 0.5 / tbl[1 + rgt]; // kpower can be bigger than the table. roundingwidens = kpower > 0 && kpower <= KP_MAX && r < tbl[kpower + 1] - fuzz; - } return new ScientificDouble(sgn, kpower, nsig, roundingwidens); @@ -386,14 +381,27 @@ public final class DoubleVectorPrinter extends VectorPrinter<RAbstractDoubleVect return encodeReal(x, dm.maxWidth, dm.d, dm.e, '.', pp); } - // caching some commonly used formats - private static final DecimalFormat[] CACHED_FORMATS = new DecimalFormat[32]; + private static final int DECIMAL_SHIFT = 350; + private static final double[][] DECIMAL_VALUES = new double[700][10]; + private static final double[] DECIMAL_WEIGHTS = new double[700]; + + static { + for (int i = 0; i < DECIMAL_WEIGHTS.length; i++) { + DECIMAL_WEIGHTS[i] = Math.pow(10, i - DECIMAL_SHIFT); + } + for (int i = 0; i < DECIMAL_VALUES.length; i++) { + for (int i2 = 0; i2 < 10; i2++) { + DECIMAL_VALUES[i][i2] = Math.pow(10, i - DECIMAL_SHIFT) * i2; + } + } + } @TruffleBoundary static String encodeReal(double initialX, int w, int d, int e, char cdec, String naString) { /* IEEE allows signed zeros (yuck!) */ double x = RRuntime.normalizeZero(initialX); + StringBuilder str = new StringBuilder(w); if (!RRuntime.isFinite(x)) { String id; if (RRuntime.isNA(x)) { @@ -403,49 +411,120 @@ public final class DoubleVectorPrinter extends VectorPrinter<RAbstractDoubleVect } else { id = x > 0 ? "Inf" : "-Inf"; } - return prependBlanks(w, id); - } else if (e != 0) { - String fmt = String.format((d != 0) ? "%%#%d.%de" : "%%%d.%de", Math.min(w, (NB - 1)), d); - String result; - if (Math.abs(x) < 1e-300 && Math.abs(x) >= Double.MIN_VALUE) { - // work around java formatting bug for small numbers like 1.53160350210786e-322 - result = snprintf(NB, fmt, x * 1e100); - StringBuilder str = new StringBuilder(result); - assert str.charAt(str.length() - 3) == '2'; - str.setCharAt(str.length() - 3, '3'); - result = str.toString(); - } else { - result = snprintf(NB, fmt, x); + int blanks = w - id.length(); + for (int i = 0; i < blanks; i++) { + str.append(' '); } - return result.replace('.', cdec); - } else { /* e = 0 */ - DecimalFormat df = null; - if (d < CACHED_FORMATS.length) { - df = CACHED_FORMATS[d]; + str.append(id); + } else { + boolean negated = x < 0; + if (negated) { + x = -x; } - if (df == null) { - df = new DecimalFormat("#.#"); - df.setRoundingMode(RoundingMode.HALF_EVEN); - df.setDecimalSeparatorAlwaysShown(false); - df.setMinimumFractionDigits(d); - df.setMaximumFractionDigits(d); - if (d < CACHED_FORMATS.length) { - CACHED_FORMATS[d] = df; + if (e != 0) { + + boolean shifted = false; + int log10; + int adjustedE = e; + if (x == 0) { + log10 = 0; + } else { + if (x < 1e-300) { + shifted = true; + x *= 1e100; + } + log10 = (int) Math.log10(x); + if (DECIMAL_WEIGHTS[log10 + DECIMAL_SHIFT] > x) { + // log10 behaves differently for < 1.0 + log10--; + } + if (log10 <= -100 || log10 >= 100) { + adjustedE = 3; + } + } + int blanks = w // target width + - (negated ? 1 : 0) // "-" + - 1 // digits before "." + - (d > 0 ? 1 : 0) // "." + - d // digits after "." + - 1 // "e" + - 1 // "+/-" for exponent + - Math.max(2, adjustedE); // digits for exponent + for (int i = 0; i < blanks; i++) { + str.append(' '); + } + // round towards next digit instead of truncating + double rounded = x + DECIMAL_VALUES[log10 - d - 1 + DECIMAL_SHIFT][5]; + if (Double.isFinite(rounded)) { + x = rounded; + // the rounding might have modified the exponent + if (DECIMAL_WEIGHTS[log10 + 1 + DECIMAL_SHIFT] <= x) { + log10++; + } + } + if (negated) { + str.append('-'); + } + x = appendDigit(x, log10, str); + if (d > 0) { + str.append(cdec); + for (int i = 1; i <= d; i++) { + x = appendDigit(x, log10 - i, str); + } + } + str.append('e'); + if (log10 < 0) { + str.append('-'); + log10 = -log10; + } else { + str.append('+'); + } + if (shifted) { + log10 += 100; + } + if (adjustedE >= 3) { + str.append((char) ('0' + (log10 / 100))); + log10 = log10 % 100; + } + str.append((char) ('0' + (log10 / 10))); + str.append((char) ('0' + (log10 % 10))); + } else { /* e == 0 */ + // round towards next digit instead of truncating + x += DECIMAL_VALUES[-d - 1 + DECIMAL_SHIFT][5]; + + int log10 = x == 0 ? 0 : Math.max((int) Math.log10(x), 0); + int blanks = w // target width + - (negated ? 1 : 0) // "-" + - (log10 + 1) // digits before "." + - (d > 0 ? 1 : 0) // "." + - d; // digits after "." + + for (int i = 0; i < blanks; i++) { + str.append(' '); + } + if (negated) { + str.append('-'); + } + for (int i = log10; i >= 0; i--) { + x = appendDigit(x, i, str); + } + if (d > 0) { + str.append(cdec); + for (int i = 1; i <= d; i++) { + x = appendDigit(x, -i, str); + } } } - return prependBlanks(w, df.format(x)).replace('.', cdec); } + assert str.length() >= w; + return str.toString(); } - private static String prependBlanks(int width, String id) { - if (id.length() >= width) { - return id; - } - StringBuilder str = new StringBuilder(width); - for (int i = 0; i < width - id.length(); i++) { - str.append(' '); - } - return str.append(id).toString(); + private static double appendDigit(double x, int digit, StringBuilder str) { + int c = (int) (x / DECIMAL_WEIGHTS[digit + DECIMAL_SHIFT]); + assert c >= 0 && c <= 9; + str.append((char) ('0' + c)); + return x - DECIMAL_VALUES[digit + DECIMAL_SHIFT][c]; } public static String[] format(RAbstractDoubleVector value, boolean trim, int nsmall, int width, char decimalMark, PrintParameters pp) { diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/FormatMetrics.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/FormatMetrics.java index 052d4675874c6853669d8229963763e069e200ca..c0f020d91e9eaba2f3b4519c7d60493d2ef0c40b 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/FormatMetrics.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/FormatMetrics.java @@ -25,7 +25,7 @@ package com.oracle.truffle.r.nodes.builtin.base.printer; /** * The generic formatting metrics. N.B. This class is public since it is used in the PrettyWriter * public API. - * + * * @see PrettyWriter */ public class FormatMetrics { @@ -34,7 +34,6 @@ public class FormatMetrics { int maxWidth; FormatMetrics(int maxWidth) { - super(); this.originalMaxWidth = maxWidth; this.maxWidth = maxWidth; } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/IntegerVectorPrinter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/IntegerVectorPrinter.java index 5453a2fa40ab27cf826ab8da4ca2a6f58cda10b8..f9d0ea7c4f89cb0cf9b1470a644d0615fee0eed6 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/IntegerVectorPrinter.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/IntegerVectorPrinter.java @@ -46,7 +46,7 @@ public final class IntegerVectorPrinter extends VectorPrinter<RAbstractIntVector @Override protected FormatMetrics formatVector(int offs, int len) { - return formatIntVector(vector, offs, len, printCtx.parameters().getNaWidth()); + return new FormatMetrics(formatIntVectorInternal(vector, offs, len, printCtx.parameters().getNaWidth())); } @Override @@ -66,7 +66,7 @@ public final class IntegerVectorPrinter extends VectorPrinter<RAbstractIntVector } } - public static FormatMetrics formatIntVector(RAbstractIntVector x, int offs, int n, int naWidth) { + static int formatIntVectorInternal(RAbstractIntVector x, int offs, int n, int naWidth) { int xmin = RRuntime.INT_MAX_VALUE; int xmax = RRuntime.INT_MIN_VALUE; boolean naflag = false; @@ -105,21 +105,62 @@ public final class IntegerVectorPrinter extends VectorPrinter<RAbstractIntVector fieldwidth = l; } } + return fieldwidth; + } + + private static final int[][] DECIMAL_VALUES = new int[10][10]; + private static final int[] DECIMAL_WEIGHTS = new int[10]; - return new FormatMetrics(fieldwidth); + static { + for (int i = 0; i < DECIMAL_WEIGHTS.length; i++) { + DECIMAL_WEIGHTS[i] = (int) Math.pow(10, i); + } + for (int i = 0; i < DECIMAL_VALUES.length; i++) { + for (int i2 = 0; i2 < 10; i2++) { + DECIMAL_VALUES[i][i2] = (int) (Math.pow(10, i) * i2); + } + } } - /* - * There is no documented (or enforced) limit on 'w' here, so use snprintf - */ - static int NB = 1000; + public static String encodeInteger(int initialX, int w, PrintParameters pp) { + StringBuilder str = new StringBuilder(w); - public static String encodeInteger(int x, int w, PrintParameters pp) { - if (x == RRuntime.INT_NA) { - return Utils.snprintf(NB, "%" + Utils.asBlankArg(Math.min(w, (NB - 1))) + "s", pp.getNaString()); + int x = initialX; + if (RRuntime.isNA(x)) { + String id = pp.getNaString(); + for (int i = w - id.length(); i > 0; i--) { + str.append(' '); + } + str.append(id); } else { - return Utils.snprintf(NB, "%" + Utils.asBlankArg(Math.min(w, (NB - 1))) + "d", x); + boolean negated = false; + if (x < 0) { + negated = true; + x = -x; + } + int log10 = x == 0 ? 0 : (int) Math.log10(x); + int blanks = w // target width + - (negated ? 1 : 0) // "-" + - (log10 + 1); // digits + + for (int i = 0; i < blanks; i++) { + str.append(' '); + } + if (negated) { + str.append('-'); + } + for (int i = log10; i >= 0; i--) { + x = appendDigit(x, i, str); + } } + return str.toString(); + } + + private static int appendDigit(int x, int digit, StringBuilder str) { + int c = x / DECIMAL_WEIGHTS[digit]; + assert c >= 0 && c <= 9; + str.append((char) ('0' + c)); + return x - DECIMAL_VALUES[digit][c]; } public static String[] format(RAbstractIntVector value, boolean trim, int width, PrintParameters pp) { @@ -127,8 +168,7 @@ public final class IntegerVectorPrinter extends VectorPrinter<RAbstractIntVector if (trim) { w = 1; } else { - FormatMetrics metrics = formatIntVector(value, 0, value.getLength(), pp.getNaWidth()); - w = metrics.maxWidth; + w = formatIntVectorInternal(value, 0, value.getLength(), pp.getNaWidth()); } w = Math.max(w, width); diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ListPrinter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ListPrinter.java index 3cde3e059f0377c95c9090efa054f8b38b05f3e0..143286861af82a99f79bd35bbf5d0f5d780dacc7 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ListPrinter.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ListPrinter.java @@ -78,8 +78,8 @@ final class ListPrinter extends AbstractValuePrinter<RAbstractListVector> { } else if (tmp instanceof RAbstractLogicalVector) { RAbstractLogicalVector lv = (RAbstractLogicalVector) tmp; if (lv.getLength() == 1) { - FormatMetrics fm = LogicalVectorPrinter.formatLogicalVector(lv, 0, 1, pp.getNaWidth()); - pbuf = LogicalVectorPrinter.encodeLogical(lv.getDataAt(0), fm.maxWidth, pp); + int width = LogicalVectorPrinter.formatLogicalVectorInternal(lv, 0, 1, pp.getNaWidth()); + pbuf = LogicalVectorPrinter.encodeLogical(lv.getDataAt(0), width, pp); } else { pbuf = "Logical," + lv.getLength(); } @@ -90,8 +90,8 @@ final class ListPrinter extends AbstractValuePrinter<RAbstractListVector> { pbuf = "factor," + iv.getLength(); } else { if (iv.getLength() == 1) { - FormatMetrics fm = IntegerVectorPrinter.formatIntVector(iv, 0, 1, pp.getNaWidth()); - pbuf = IntegerVectorPrinter.encodeInteger(iv.getDataAt(0), fm.maxWidth, pp); + int width = IntegerVectorPrinter.formatIntVectorInternal(iv, 0, 1, pp.getNaWidth()); + pbuf = IntegerVectorPrinter.encodeInteger(iv.getDataAt(0), width, pp); } else { pbuf = "Integer," + iv.getLength(); } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/LogicalVectorPrinter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/LogicalVectorPrinter.java index 29231624992cce41ae975e5c03bbc1b88038f6fa..3421a15d5b01f989d577a8e5b274bea67a117002 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/LogicalVectorPrinter.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/LogicalVectorPrinter.java @@ -11,9 +11,6 @@ */ package com.oracle.truffle.r.nodes.builtin.base.printer; -import static com.oracle.truffle.r.nodes.builtin.base.printer.IntegerVectorPrinter.NB; -import static com.oracle.truffle.r.nodes.builtin.base.printer.Utils.snprintf; - import java.io.IOException; import com.oracle.truffle.r.runtime.RRuntime; @@ -47,13 +44,12 @@ public final class LogicalVectorPrinter extends VectorPrinter<RAbstractLogicalVe @Override protected FormatMetrics formatVector(int offs, int len) { - return formatLogicalVector(vector, offs, len, printCtx.parameters().getNaWidth()); + return new FormatMetrics(formatLogicalVectorInternal(vector, offs, len, printCtx.parameters().getNaWidth())); } @Override protected void printElement(int i, FormatMetrics fm) throws IOException { - String v = encodeLogical(vector.getDataAt(i), fm.maxWidth, printCtx.parameters()); - out.print(v); + out.print(encodeLogical(vector.getDataAt(i), fm.maxWidth, printCtx.parameters())); } @Override @@ -67,7 +63,7 @@ public final class LogicalVectorPrinter extends VectorPrinter<RAbstractLogicalVe } } - static FormatMetrics formatLogicalVector(RAbstractLogicalVector x, int offs, int n, int naWidth) { + static int formatLogicalVectorInternal(RAbstractLogicalVector x, int offs, int n, int naWidth) { int fieldwidth = 1; for (int i = 0; i < n; i++) { byte xi = x.getDataAt(offs + i); @@ -83,18 +79,29 @@ public final class LogicalVectorPrinter extends VectorPrinter<RAbstractLogicalVe /* this is the widest it can be, so stop */ } } - return new FormatMetrics(fieldwidth); + return fieldwidth; } static String encodeLogical(byte x, int w, PrintParameters pp) { - final String fmt = "%" + Utils.asBlankArg(Math.min(w, (NB - 1))) + "s"; + String id; if (x == RRuntime.LOGICAL_NA) { - return snprintf(NB, fmt, pp.getNaString()); - } else if (x != 0) { - return snprintf(NB, fmt, "TRUE"); + id = pp.getNaString(); + } else if (x != RRuntime.LOGICAL_FALSE) { + id = "TRUE"; } else { - return snprintf(NB, fmt, "FALSE"); + id = "FALSE"; + } + if (id.length() == w) { + return id; + } + int blanks = w // target width + - id.length(); // text + StringBuilder str = new StringBuilder(); + for (int i = 0; i < blanks; i++) { + str.append(' '); } + str.append(id); + return str.toString(); } public static String[] format(RAbstractLogicalVector value, boolean trim, int width, PrintParameters pp) { @@ -102,8 +109,7 @@ public final class LogicalVectorPrinter extends VectorPrinter<RAbstractLogicalVe if (trim) { w = 1; } else { - FormatMetrics metrics = formatLogicalVector(value, 0, value.getLength(), pp.getNaWidth()); - w = metrics.maxWidth; + w = formatLogicalVectorInternal(value, 0, value.getLength(), pp.getNaWidth()); } w = Math.max(w, width);