org.sejda.sambox.util.NumberFormatUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sambox Show documentation
Show all versions of sambox Show documentation
An Apache PDFBox fork intended to be used as PDF processor for Sejda and PDFsam
related projects
/*
* 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.sejda.sambox.util;
/**
* This class contains methods to format numbers.
*
* @author Michael Doswald
*/
public final class NumberFormatUtil
{
private 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;
}
}
/**
* 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;
}
}