/******************************************************************************* * Copyright (c) 2007, 2018 Association for Decentralized Information Management * in Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation * Semantum Oy - improved output precision control, trailing zero control *******************************************************************************/ package org.simantics.utils.format; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.text.NumberFormat; import java.util.Locale; /** * @author Antti Villberg * @author Tuukka Lehtonen */ public class FormattingUtil { private int floatDigits; private int doubleDigits; private double lowLimit; private double highLimit; private boolean trailingZeroes; private DecimalFormatSymbols decimalFormatSymbols; private DecimalFormat flow; private DecimalFormat[] fmiddles; private DecimalFormat[] fhighs; private DecimalFormat dlow; private DecimalFormat[] dmiddles; private DecimalFormat[] dhighs; public FormattingUtil(int floatDigits, int doubleDigits) { this(floatDigits, doubleDigits, Locale.getDefault()); } public FormattingUtil(int floatDigits, int doubleDigits, boolean addTrailingZeroes) { this(floatDigits, doubleDigits, Locale.getDefault(), addTrailingZeroes); } public FormattingUtil(int floatDigits, int doubleDigits, Locale locale) { this(floatDigits, doubleDigits, 0.01, 1e6, locale, false); } public FormattingUtil(int floatDigits, int doubleDigits, double lowLimit, double highLimit, Locale locale) { this(floatDigits, doubleDigits, lowLimit, highLimit, locale, false); } public FormattingUtil(int floatDigits, int doubleDigits, Locale locale, boolean addTrailingZeroes) { this(floatDigits, doubleDigits, 0.01, 1e6, locale, addTrailingZeroes); } public FormattingUtil(int floatDigits, int doubleDigits, double lowLimit, double highLimit, Locale locale, boolean addTrailingZeroes) { this.floatDigits = floatDigits; this.doubleDigits = doubleDigits; this.lowLimit = lowLimit; this.highLimit = highLimit; this.trailingZeroes = addTrailingZeroes; this.decimalFormatSymbols = DecimalFormatSymbols.getInstance(locale != null ? locale : Locale.getDefault()); initFormats(); } private void initFormats() { double exp = Math.log10(highLimit); int formatCount = Math.max(1, (int) Math.ceil(exp)); //System.out.println("initFormats(FLOAT; " + floatDigits + ")"); this.flow = createLowFormat(floatDigits); this.fmiddles = createMiddleFormats(formatCount, floatDigits); this.fhighs = createHighFormats(floatDigits); //System.out.println("initFormats(DOUBLE; " + doubleDigits + ")"); this.dlow = createLowFormat(doubleDigits); this.dmiddles = createMiddleFormats(formatCount, doubleDigits); this.dhighs = createHighFormats(doubleDigits); } private DecimalFormat createLowFormat(int digitCount) { StringBuilder fmt = new StringBuilder(); fmt.append("0."); for (int i = 0; i < digitCount; ++i) fmt.append(trailingZeroes ? '0' : '#'); //System.out.println("lowFormat(" + digitCount + "): " + fmt); return new DecimalFormat(fmt.toString(), decimalFormatSymbols); } private DecimalFormat[] createMiddleFormats(int formatCount, int digitCount) { DecimalFormat[] middles = new DecimalFormat[formatCount]; for (int exp10 = 0; exp10 < formatCount; ++exp10) { StringBuilder fmt = new StringBuilder(); int digits = digitCount; int i = 0; for (; i <= exp10 && digits > 0; ++i, --digits) fmt.append('0'); if (digits <= 0) { if (i <= exp10) fmt.append("E0"); } else { if (digits > 0) fmt.append('.'); for (; digits > 0; --digits) fmt.append(trailingZeroes ? '0' : '#'); } //System.out.println("middleFormat(" + exp10 + "; " + digitCount + "): " + fmt); middles[exp10] = new DecimalFormat(fmt.toString(), decimalFormatSymbols); } return middles; } private DecimalFormat[] createHighFormats(int digitCount) { // [0] = for formatting 1.234567e{6,9,...} // [1] = for formatting 12.34567e{6,9,...} // [2] = for formatting 123.4567e{6,9,...} DecimalFormat[] highs = new DecimalFormat[3]; for (int exp10 = 0; exp10 < 3; ++exp10) { StringBuilder fmt = new StringBuilder(); int digits = digitCount; for (int i = 0; i <= exp10 && digits > 0; ++i, --digits) fmt.append('0'); if (digits > 0) { fmt.append("."); for (; digits > 0; --digits) fmt.append(trailingZeroes ? '0' : '#'); } fmt.append("E0"); //System.out.println("highFormat(" + exp10 + "; " + digitCount + "): " + fmt); highs[exp10] = new DecimalFormat(fmt.toString(), decimalFormatSymbols); } return highs; } public String engineeringFormat(Object value) { if (value == null) return ""; Class clazz = value.getClass(); if (clazz == Double.class) { return formatNumber((Double) value); } else if (clazz == Float.class) { return formatNumber((Float)value); } else if (clazz == double[].class) { double[] doubles = (double[])value; StringBuilder b = new StringBuilder(); b.append("["); boolean first = true; for (double d : doubles) { if(!first) b.append(","); b.append(formatNumber(d)); first = false; } b.append("]"); return b.toString(); } else if (clazz == float[].class) { float[] floats = (float[])value; StringBuilder b = new StringBuilder(); b.append("["); boolean first = true; for (float f : floats) { if(!first) b.append(","); b.append(formatNumber(f)); first = false; } b.append("]"); return b.toString(); } else if (clazz == int[].class) { int[] ints = (int[])value; StringBuilder b = new StringBuilder(); b.append("["); boolean first = true; for (int d : ints) { if(!first) b.append(","); b.append(d); first = false; } b.append("]"); return b.toString(); } else if (clazz == String.class) { return (String) value; } else { return value.toString(); } } private static int highFormatterIndex(int exp10) { return exp10 < 0 ? (exp10 % 3 + 3) % 3 : exp10 % 3; } private static int highFormatterIndex(float absValue) { return highFormatterIndex((int) Math.floor((float) Math.log10(absValue))); } private static int highFormatterIndex(double absValue) { return highFormatterIndex((int) Math.floor(Math.log10(absValue))); } private static int middleFormatterIndex(float absValue) { return (int) Math.floor((float) Math.log10(absValue)); } private static int middleFormatterIndex(double absValue) { return (int) Math.floor(Math.log10(absValue)); } public String formatNumber(float v) { if (Float.isInfinite(v)) { return (v == Float.POSITIVE_INFINITY) ? "\u221E" : "-\u221E"; } else if (Float.isNaN(v)) { return "NaN"; } else { float abs = Math.abs(v); if ((abs < 1.0f && abs >= (float)lowLimit) || abs == 0.0f) { return flow.format(v); } else if (abs >= 1.0f && abs < (float)highLimit) { int expi = middleFormatterIndex(abs); if (expi < fmiddles.length) return fmiddles[expi].format(v); } return postprocess( fhighs[ highFormatterIndex(abs) ].format(v) ); } } public String formatNumber(double v) { if (Double.isInfinite(v)) { return (v == Double.POSITIVE_INFINITY) ? "\u221E" : "-\u221E"; } else if (Double.isNaN(v)) { return "NaN"; } else { double abs = Math.abs(v); if ((abs < 1.0 && abs >= lowLimit) || abs == 0.0) { return dlow.format(v); } else if (abs >= 1.0 && abs < highLimit) { int expi = middleFormatterIndex(abs); if (expi < dmiddles.length) return dmiddles[expi].format(v); } return postprocess( dhighs[ highFormatterIndex(abs) ].format(v) ); } } private static String postprocess(String s) { if (s.endsWith("E0")) return s.substring(0, s.length() - 2); return s.replace('E', 'e'); } public static String formatNumberLocale(Object num, int minPrecision, int maxPrecision, String languageTag) { NumberFormat format = NumberFormat.getNumberInstance(Locale.forLanguageTag(languageTag)); format.setMaximumFractionDigits(maxPrecision); format.setMinimumFractionDigits(minPrecision); if (num instanceof java.lang.Number) { double doubleValue = ((java.lang.Number)num).doubleValue(); if(Double.isFinite(doubleValue)) return format.format(doubleValue); else if(Double.isNaN(doubleValue)) return "NaN"; else if(Double.isInfinite(doubleValue)) return "\u221e"; } throw new NumberFormatException("Number " + num + ", instanceOf " + num.getClass().getName()); } public static void main(String[] args) { FormattingUtil fu = new FormattingUtil(7, 15, true); System.out.println("=== DOUBLE ==="); System.out.println(fu.formatNumber(123e-3)); System.out.println(fu.formatNumber(-123e-3)); System.out.println(fu.formatNumber(Double.POSITIVE_INFINITY)); System.out.println(fu.formatNumber(Double.NEGATIVE_INFINITY)); System.out.println(fu.formatNumber(Double.NaN)); System.out.println(fu.formatNumber(0.0)); System.out.println(fu.formatNumber(0.25)); System.out.println(fu.formatNumber(0.1)); System.out.println(fu.formatNumber(1.0)); System.out.println(fu.formatNumber(-0.25)); System.out.println(fu.formatNumber(-0.1)); System.out.println(fu.formatNumber(-1.0)); System.out.println(fu.formatNumber(0.9999)); System.out.println(fu.formatNumber(0.0999999999999999999)); System.out.println(fu.formatNumber(0.0099999999999999999999)); System.out.println(fu.formatNumber(0.004541234)); System.out.println(fu.formatNumber(0.00099999999999999999999)); System.out.println(fu.formatNumber(0.000099999999999999999999)); System.out.println(fu.formatNumber(0.0000099999999999999999999)); System.out.println(fu.formatNumber(0.00000099999999999999999999)); System.out.println(fu.formatNumber(-0.9999)); System.out.println(fu.formatNumber(-0.0999999999999999999)); System.out.println(fu.formatNumber(-0.0099999999999999999999)); System.out.println(fu.formatNumber(-0.00099999999999999999999)); System.out.println(fu.formatNumber(-0.000099999999999999999999)); System.out.println(fu.formatNumber(1.234567891)); System.out.println(fu.formatNumber(12.34567891)); System.out.println(fu.formatNumber(123.4567891)); System.out.println(fu.formatNumber(1234.567891)); System.out.println(fu.formatNumber(12345.67891)); System.out.println(fu.formatNumber(123456.7891)); System.out.println(fu.formatNumber(1234567.891)); System.out.println(fu.formatNumber(1234567.8912345678)); System.out.println(fu.formatNumber(12345678.912345678)); System.out.println(fu.formatNumber(123456789.12345678)); System.out.println(fu.formatNumber(1234567891.2345678)); System.out.println(fu.formatNumber(12345678912.345678)); System.out.println(fu.formatNumber(100.0000000000000)); System.out.println(fu.formatNumber(100000.0000000000)); System.out.println(fu.formatNumber(1000000000.000000)); System.out.println(fu.formatNumber(100000000000.0000)); System.out.println(fu.formatNumber(10000000000000.00)); System.out.println(fu.formatNumber(999999.99999999999999)); System.out.println(fu.formatNumber(999999.9999999999999)); System.out.println(fu.formatNumber(999999.999999999999)); System.out.println(fu.formatNumber(999999.99999999999)); System.out.println(fu.formatNumber(999999.9999999999)); System.out.println(fu.formatNumber(999999.999999999)); System.out.println(fu.formatNumber(999999.99999999)); System.out.println(fu.formatNumber(999999.9999999)); System.out.println(fu.formatNumber(999999.999999)); System.out.println(fu.formatNumber(999999.99999)); System.out.println(fu.formatNumber(999999.9999)); System.out.println(fu.formatNumber(999999.999)); System.out.println(fu.formatNumber(999999.99)); System.out.println(fu.formatNumber(999999.9)); System.out.println(fu.formatNumber(999999.0)); // Note that this will invoke formatNumber(float), not formatNumber(double)! System.out.println(fu.formatNumber(999999)); System.out.println("=== FLOAT ==="); System.out.println(fu.formatNumber(123e-3f)); System.out.println(fu.formatNumber(-123e-3f)); System.out.println(fu.formatNumber(Float.POSITIVE_INFINITY)); System.out.println(fu.formatNumber(Float.NEGATIVE_INFINITY)); System.out.println(fu.formatNumber(Float.NaN)); System.out.println(fu.formatNumber(0f)); System.out.println(fu.formatNumber(0.25f)); System.out.println(fu.formatNumber(0.1f)); System.out.println(fu.formatNumber(1f)); System.out.println(fu.formatNumber(-0.25f)); System.out.println(fu.formatNumber(-0.1f)); System.out.println(fu.formatNumber(-1f)); System.out.println(fu.formatNumber(0.9999f)); System.out.println(fu.formatNumber(0.0999999999999999999f)); System.out.println(fu.formatNumber(0.0099999999999999999999f)); System.out.println(fu.formatNumber(0.004541234f)); System.out.println(fu.formatNumber(0.00099999999999999999999f)); System.out.println(fu.formatNumber(0.000099999999999999999999f)); System.out.println(fu.formatNumber(0.0000099999999999999999999f)); System.out.println(fu.formatNumber(0.00000099999999999999999999f)); System.out.println(fu.formatNumber(-0.9999f)); System.out.println(fu.formatNumber(-0.0999999999999999999f)); System.out.println(fu.formatNumber(-0.0099999999999999999999f)); System.out.println(fu.formatNumber(-0.00099999999999999999999f)); System.out.println(fu.formatNumber(-0.000099999999999999999999f)); System.out.println(fu.formatNumber(1.234567891f)); System.out.println(fu.formatNumber(12.34567891f)); System.out.println(fu.formatNumber(123.4567891f)); System.out.println(fu.formatNumber(1234.567891f)); System.out.println(fu.formatNumber(12345.67891f)); System.out.println(fu.formatNumber(123456.7891f)); System.out.println(fu.formatNumber(1234567.891f)); System.out.println(fu.formatNumber(1234567.8912345678f)); System.out.println(fu.formatNumber(12345678.912345678f)); System.out.println(fu.formatNumber(123456789.12345678f)); System.out.println(fu.formatNumber(1234567891.2345678f)); System.out.println(fu.formatNumber(12345678912.345678f)); System.out.println(fu.formatNumber(100.0000000000000f)); System.out.println(fu.formatNumber(100000.0000000000f)); System.out.println(fu.formatNumber(1000000000.000000f)); System.out.println(fu.formatNumber(100000000000.0000f)); System.out.println(fu.formatNumber(10000000000000.00f)); System.out.println(fu.formatNumber(999999.999f)); System.out.println(fu.formatNumber(999999.99f)); System.out.println(fu.formatNumber(999999.9f)); System.out.println(fu.formatNumber(999999f)); } }