NumberFormatUtil.java
/*
* Copyright 2016 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.pdfbox.util;
/**
* This class contains methods to format numbers.
*
* @author Michael Doswald
*/
public class NumberFormatUtil
{
/**
* Maximum number of fraction digits supported by the format methods
*/
private static final int MAX_FRACTION_DIGITS = 5;
/**
* Contains the power of ten values for fast lookup in the format methods
*/
private static final long[] POWER_OF_TENS;
private static final int[] POWER_OF_TENS_INT;
static
{
POWER_OF_TENS = new long[19];
POWER_OF_TENS[0] = 1;
for (int exp = 1; exp < POWER_OF_TENS.length; exp++)
{
POWER_OF_TENS[exp] = POWER_OF_TENS[exp - 1] * 10;
}
POWER_OF_TENS_INT = new int[10];
POWER_OF_TENS_INT[0] = 1;
for (int exp = 1; exp < POWER_OF_TENS_INT.length; exp++)
{
POWER_OF_TENS_INT[exp] = POWER_OF_TENS_INT[exp - 1] * 10;
}
}
private NumberFormatUtil()
{
}
/**
* Fast variant to format a floating point value to a ASCII-string. The format will fail if the
* value is greater than {@link Long#MAX_VALUE}, smaller or equal to {@link Long#MIN_VALUE}, is
* {@link Float#NaN}, infinite or the number of requested fraction digits is greater than
* {@link #MAX_FRACTION_DIGITS}.
*
* When the number contains more fractional digits than {@code maxFractionDigits} the value will
* be rounded. Rounding is done to the nearest possible value, with the tie breaking rule of
* rounding away from zero.
*
* @param value The float value to format
* @param maxFractionDigits The maximum number of fraction digits used
* @param asciiBuffer The output buffer to write the formatted value to
*
* @return The number of bytes used in the buffer or {@code -1} if formatting failed
*/
public static int formatFloatFast(float value, int maxFractionDigits, byte[] asciiBuffer)
{
if (Float.isNaN(value) ||
Float.isInfinite(value) ||
value > Long.MAX_VALUE ||
value <= Long.MIN_VALUE ||
maxFractionDigits > MAX_FRACTION_DIGITS)
{
return -1;
}
int offset = 0;
long integerPart = (long) value;
//handle sign
if (value < 0)
{
asciiBuffer[offset++] = '-';
integerPart = -integerPart;
}
//extract fraction part
long fractionPart = (long) ((Math.abs((double)value) - integerPart) * POWER_OF_TENS[maxFractionDigits] + 0.5d);
//Check for rounding to next integer
if (fractionPart >= POWER_OF_TENS[maxFractionDigits]) {
integerPart++;
fractionPart -= POWER_OF_TENS[maxFractionDigits];
}
//format integer part
offset = formatPositiveNumber(integerPart, getExponent(integerPart), false, asciiBuffer, offset);
if (fractionPart > 0 && maxFractionDigits > 0)
{
asciiBuffer[offset++] = '.';
offset = formatPositiveNumber(fractionPart, maxFractionDigits - 1, true, asciiBuffer, offset);
}
return offset;
}
/**
* Formats a positive integer number starting with the digit at {@code 10^exp}.
*
* @param number The number to format
* @param exp The start digit
* @param omitTrailingZeros Whether the formatting should stop if only trailing zeros are left.
* This is needed e.g. when formatting fractions of a number.
* @param asciiBuffer The buffer to write the ASCII digits to
* @param startOffset The start offset into the buffer to start writing
*
* @return The offset into the buffer which contains the first byte that was not filled by the
* method
*/
private static int formatPositiveNumber(long number, int exp, boolean omitTrailingZeros, byte[] asciiBuffer, int startOffset)
{
int offset = startOffset;
long remaining = number;
while (remaining > Integer.MAX_VALUE && (!omitTrailingZeros || remaining > 0))
{
long digit = remaining / POWER_OF_TENS[exp];
remaining -= (digit * POWER_OF_TENS[exp]);
asciiBuffer[offset++] = (byte) ('0' + digit);
exp--;
}
//If the remaining fits into an integer, use int arithmetic as it is faster
int remainingInt = (int) remaining;
while (exp >= 0 && (!omitTrailingZeros || remainingInt > 0))
{
int digit = remainingInt / POWER_OF_TENS_INT[exp];
remainingInt -= (digit * POWER_OF_TENS_INT[exp]);
asciiBuffer[offset++] = (byte) ('0' + digit);
exp--;
}
return offset;
}
/**
* Returns the highest exponent of 10 where {@code 10^exp < number} for numbers > 0
*/
private static int getExponent(long number)
{
for (int exp = 0; exp < (POWER_OF_TENS.length - 1); exp++)
{
if (number < POWER_OF_TENS[exp + 1])
{
return exp;
}
}
return POWER_OF_TENS.length - 1;
}
}