Skip to content

pwall567/int-output

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

int-output

Build Status License: MIT Maven Central

Integer output functions

Background

Many projects have a requirement to output integer values in string form, either as variable-length strings with leading zeroes omitted, or as a fixed number of digits. This library consists of a number of static functions to output integer values to an Appendable, or using an IntConsumer function, without incurring the cost (in execution-time terms) of a memory allocation.

This may seem like a trivial operation, but because the length of the string representation is not known in advance, a simplistic implementation might allocate a memory buffer to hold the converted value, and then copy the result to the output. But memory allocation is a relatively costly operation compared to the basic numeric conversion, and an optimisation that avoids memory allocation can produce worthwhile CPU time savings.

The problem with optimisation, of course, is that the additional complexity of the code can be a source of errors. This library offers a set of well-tested, highly optimised conversion functions that give the efficiency gains with none of the risks.

Appendable or IntConsumer

The most common use of these functions is to build a string using a StringBuilder (an implementation of Appendable), but in some cases the requirement is to output the value in some other manner, for example using the pipelines library. For these cases, there are versions of all the functions which take an IntConsumer parameter; this lambda will be called with each character (as an int) in turn.

So why maintain both? After all, appending to an Appendable can always be achieved using the IntConsumer functions.

The fact is that the Appendable functions were implemented first, and they also represent the most common usage pattern for the functions – building a string using StringBuilder – so it makes sense to maintain the availability of both. Also, there is a slight performance advantage in using the Appendable directly, rather than through a lambda.

Decimal or Hexadecimal

There are functions to output left-trimmed strings of digits in decimal and hexadecimal, and in addition, there are several functions to output fixed-length strings left-padded with zeros.

Decimal

Function Parameter Output
appendInt int left-trimmed
appendPositiveInt int left-trimmed (value must be positive)
appendUnsignedInt int left-trimmed (value is treated as unsigned)
appendIntScaled int left-trimmed with decimal separator as indicated by scale
appendPositiveIntScaled int left-trimmed with decimal separator as indicated by scale (value must be positive)
appendLong long left-trimmed
appendPositiveLong long left-trimmed (value must be positive)
appendUnsignedLong long left-trimmed (value is treated as unsigned)
appendLongScaled long left-trimmed with decimal separator as indicated by scale
appendPositiveLongScaled long left-trimmed with decimal separator as indicated by scale (value must be positive)
append1Digit int 1 digit
append1DigitSafe int 1 digit (safe version; performs modulo on value)
append2Digits int 2 digits left filled with zeros
append2DigitsSafe int 2 digits left filled with zeros (safe version; performs modulo on value)
append3Digits int 3 digits left filled with zeros
append3DigitsSafe int 3 digits left filled with zeros (safe version; performs modulo on value)
appendIntGrouped int left-trimmed, output in 3-digit groups
appendPositiveIntGrouped int left-trimmed, output in 3-digit groups (value must be positive)
appendLongGrouped long left-trimmed, output in 3-digit groups
appendPositiveLongGrouped long left-trimmed, output in 3-digit groups (value must be positive)

(the "grouped" forms output digits in blocks of three, separated by a nominated separator character)

For each appendXxxx function there is an equivalent outputXxxx function, which instead of taking an Appendable parameter, takes an IntConsumer which will be called with each output character.

Hexadecimal

Function Parameter Output
appendIntHex int left-trimmed, hexadecimal
appendIntHexLC int left-trimmed, hexadecimal (using lower-case alphabetics)
appendLongHex long left-trimmed, hexadecimal
appendLongHexLC long left-trimmed, hexadecimal (using lower-case alphabetics)
append8Hex int 8 digits left-padded, hexadecimal
append8HexLC int 8 digits left-padded, hexadecimal (using lower-case alphabetics)
append4Hex int 4 digits left-padded, hexadecimal
append4HexLC int 4 digits left-padded, hexadecimal (using lower-case alphabetics)
append2Hex int 2 digits left-padded, hexadecimal
append2HexLC int 2 digits left-padded, hexadecimal (using lower-case alphabetics)
append1Hex int 1 digit, hexadecimal
append1HexLC int 1 digit, hexadecimal (using lower-case alphabetics)

(the functions all have two variants, one which uses upper-case characters for the hexadecimal digits and one which uses lower-case)

For each appendXxxx function there is an equivalent outputXxxx function, which instead of taking an Appendable parameter, takes an IntConsumer which will be called with each output character.

Usage

To output an int value to an Appendable:

        StringBuilder sb = new StringBuilder(12);
        int intValue = 12345;
        IntOutput.appendInt(sb, intValue);

If the number is guaranteed to be positive:

        StringBuilder sb = new StringBuilder(12);
        int intValue = 12345;
        IntOutput.appendPositiveInt(sb, intValue);

To output an int value to an Appendable, treating the int as unsigned:

        StringBuilder sb = new StringBuilder(12);
        int intValue = 0x89ABCDEF;
        IntOutput.appendUnsignedInt(sb, intValue);

To output an int value to an Appendable, with number of decimal places specified as a scale:

        StringBuilder sb = new StringBuilder(12);
        int intValue = 12345;
        IntOutput.appendIntScaled(sb, intValue, 2, '.'); // 2 decimal places with '.' as separator

Negative scale values (indicating that decimal point is to the right of the last digit) are ignored. It is left to the user to decide whether to output additional zeros following the number, or to add an exponent suffix.

To output a long value to an Appendable:

        StringBuilder sb = new StringBuilder(20);
        long longValue = 1234567812345678;
        IntOutput.appendLong(sb, longValue);

If the long value is guaranteed to be positive:

        StringBuilder sb = new StringBuilder(20);
        long longValue = 1234567812345678;
        IntOutput.appendPositiveLong(sb, longValue);

To output a long value to an Appendable, treating the long as unsigned:

        StringBuilder sb = new StringBuilder(20);
        long longValue = 1234567890123456789L;
        IntOutput.appendUnsignedLong(sb, longValue * 10);

To output a long value to an Appendable, with number of decimal places specified as a scale:

        StringBuilder sb = new StringBuilder(12);
        long longValue = 1234567890123456789L;
        IntOutput.appendLongScaled(sb, intValue, 3, '.'); // 3 decimal places with '.' as separator

See the note above (following the int version of the function) regarding negative scale values.

To output an int as 1 digit:

        StringBuilder sb = new StringBuilder(12);
        int count = 5;
        IntOutput.append2Digits(sb, hours);

To output an int as 2 digits, including leading zero if required (for example, when outputting a time):

        StringBuilder sb = new StringBuilder(12);
        int hours = 23;
        IntOutput.append2Digits(sb, hours);

To output an int as 3 digits, including leading zeroes if required (for example, when outputting the number of milliseconds in a time):

        StringBuilder sb = new StringBuilder(12);
        int millis = 567;
        IntOutput.append3Digits(sb, millis);

To output an int value to an Appendable, with the digits grouped in 3s:

        StringBuilder sb = new StringBuilder(12);
        int intValue = 12345;
        IntOutput.appendIntGrouped(sb, intValue, ',');

If the number is guaranteed to be positive:

        StringBuilder sb = new StringBuilder(12);
        int intValue = 12345;
        IntOutput.appendPositiveIntGrouped(sb, intValue, ',');

To output a long value to an Appendable, with the digits grouped in 3s:

        StringBuilder sb = new StringBuilder(12);
        long longValue = 1234567812345678;
        IntOutput.appendLongGrouped(sb, longValue, ',');

If the long value is guaranteed to be positive:

        StringBuilder sb = new StringBuilder(12);
        long longValue = 1234567812345678;
        IntOutput.appendPositiveLongGrouped(sb, longValue, ',');

To output a number as 2 hexadecimal digits:

        StringBuilder sb = new StringBuilder(12);
        int intValue = 160;
        IntOutput.append2Hex(sb, intValue);

To output a number as 2 hexadecimal digits using lower case for the alphabetic characters:

        StringBuilder sb = new StringBuilder(12);
        int intValue = 160;
        IntOutput.append2HexLC(sb, intValue);

To output a number as 4 hexadecimal digits:

        StringBuilder sb = new StringBuilder(12);
        int intValue = 65535;
        IntOutput.append4Hex(sb, intValue);

To output a number as 4 hexadecimal digits using lower case for the alphabetic characters:

        StringBuilder sb = new StringBuilder(12);
        int intValue = 65535;
        IntOutput.append4HexLC(sb, intValue);

To output an int value using an IntConsumer:

        int intValue = 12345;
        IntOutput.outputInt(intValue, (ch) -> channel.send(ch));

Exceptions

Unfortunately, the append() functions of the Appendable interface are declared as throwing an IOException, so all the functions in this library that use Appendable are similarly specified. The exception can safely be ignored if the Appendable is a StringBuilder, but if the Appendable is a Writer, or some other class that performs actual I/O, the caller of the functions must take that into account.

Extended Example

The following code formats a money value, with dollar sign, commas, decimal point and two digits of cents.

    private String formatMoney(long dollars, int cents) {
        StringBuilder sb = new StringBuilder(32);
        try {
            sb.append('$');
            IntOutput.appendPositiveLongGrouped(sb, dollars, ',');
            sb.append('.');
            IntOutput.append2Digits(sb, cents);
        }
        catch (IOException ignore) {
            // can't happen - StringBuilder doesn't throw IOException
        }
        return sb.toString();
    }

Dependency Specification

The latest version of the library is 2.1, and it may be obtained from the Maven Central repository.

Maven

    <dependency>
      <groupId>net.pwall.util</groupId>
      <artifactId>int-output</artifactId>
      <version>2.1</version>
    </dependency>

Gradle

    implementation 'net.pwall.util:int-output:2.1'

Gradle (kts)

    implementation("net.pwall.util:int-output:2.1")

Peter Wall

2023-12-02

About

Integer output functions

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages