/** * Format a number supplied as a decimal * * @param dval the decimal value * @param fsb the FastStringBuffer to contain the result */ private void formatDecimal(BigDecimal dval, FastStringBuffer fsb) { dval = dval.setScale(maxFractionPartSize, BigDecimal.ROUND_HALF_EVEN); DecimalValue.decimalToString(dval, fsb); int point = fsb.indexOf('.'); int intDigits; if (point >= 0) { int zz = maxFractionPartSize - minFractionPartSize; while (zz > 0) { if (fsb.charAt(fsb.length() - 1) == '0') { fsb.setLength(fsb.length() - 1); zz--; } else { break; } } intDigits = point; if (fsb.charAt(fsb.length() - 1) == '.') { fsb.setLength(fsb.length() - 1); } } else { intDigits = fsb.length(); if (minFractionPartSize > 0) { fsb.append('.'); for (int i = 0; i < minFractionPartSize; i++) { fsb.append('0'); } } } if (minWholePartSize == 0 && intDigits == 1 && fsb.charAt(0) == '0') { fsb.removeCharAt(0); } else { fsb.prependRepeated('0', minWholePartSize - intDigits); } }
/** * Convert a double to a BigDecimal. In general there will be several BigDecimal values that are * equal to the supplied value, and the one we want to choose is the one with fewest non-zero * digits. The algorithm used is rather pragmatic: look for a string of zeroes or nines, try * rounding the number down or up as approriate, then convert the adjusted value to a double to * see if it's equal to the original: if not, use the original value unchanged. * * @param value the double to be converted * @param precision 2 for a double, 1 for a float * @return the result of conversion to a double */ public static BigDecimal adjustToDecimal(double value, int precision) { final String zeros = (precision == 1 ? "00000" : "000000000"); final String nines = (precision == 1 ? "99999" : "999999999"); BigDecimal initial = new BigDecimal(value); BigDecimal trial = null; FastStringBuffer fsb = new FastStringBuffer(20); DecimalValue.decimalToString(initial, fsb); String s = fsb.toString(); int start = (s.charAt(0) == '-' ? 1 : 0); int p = s.indexOf("."); int i = s.lastIndexOf(zeros); if (i > 0) { if (p < 0 || i < p) { // we're in the integer part // try replacing all following digits with zeros and seeing if we get the same double back FastStringBuffer sb = new FastStringBuffer(s.length()); sb.append(s.substring(0, i)); for (int n = i; n < s.length(); n++) { sb.append(s.charAt(n) == '.' ? '.' : '0'); } trial = new BigDecimal(sb.toString()); } else { // we're in the fractional part // try truncating the number before the zeros and seeing if we get the same double back trial = new BigDecimal(s.substring(0, i)); } } else { i = s.indexOf(nines); if (i >= 0) { if (i == start) { // number starts with 99999... or -99999. Try rounding up to 100000.. or -100000... FastStringBuffer sb = new FastStringBuffer(s.length() + 1); if (start == 1) { sb.append('-'); } sb.append('1'); for (int n = start; n < s.length(); n++) { sb.append(s.charAt(n) == '.' ? '.' : '0'); } trial = new BigDecimal(sb.toString()); } else { // try rounding up while (i >= 0 && (s.charAt(i) == '9' || s.charAt(i) == '.')) { i--; } if (i < 0 || s.charAt(i) == '-') { return initial; // can't happen: we've already handled numbers starting 99999.. } else if (p < 0 || i < p) { // we're in the integer part FastStringBuffer sb = new FastStringBuffer(s.length()); sb.append(s.substring(0, i)); sb.append((char) ((int) s.charAt(i) + 1)); for (int n = i; n < s.length(); n++) { sb.append(s.charAt(n) == '.' ? '.' : '0'); } trial = new BigDecimal(sb.toString()); } else { // we're in the fractional part - can ignore following digits String s2 = s.substring(0, i) + (char) ((int) s.charAt(i) + 1); trial = new BigDecimal(s2); } } } } if (trial != null && (precision == 1 ? trial.floatValue() == value : trial.doubleValue() == value)) { return trial; } else { return initial; } }