ByteArray to DoubleArray in Kotlin

I want to extrapolate a byte array into a double array.

I know how to do it in Java. But the AS converter doesn't work for this... :-D

This is the class I want to write in Kotlin:

class ByteArrayToDoubleArrayConverter {

    public double[] invoke(byte[] bytes) {
        double[] doubles = new double[bytes.length / 2];
        int i = 0;

        for (int n = 0; n < bytes.length; n = n + 2) {
            doubles[i] = (bytes[n] & 0xFF) | (bytes[n + 1] << 8);
            i = i + 1;
        }

        return doubles;
    }
}

This would be a typical example of what results are expected:

class ByteArrayToDoubleArrayConverterTest {

    @Test
    fun `check typical values`() {
        val bufferSize = 8
        val bytes = ByteArray(bufferSize)
        bytes[0] = 1
        bytes[1] = 0

        bytes[2] = 0
        bytes[3] = 1

        bytes[4] = 0
        bytes[5] = 2

        bytes[6] = 1
        bytes[7] = 1
        val doubles = ByteArrayToDoubleArrayConverter().invoke(bytes)
        assertTrue(1.0 == doubles[0])
        assertTrue(256.0 == doubles[1])
        assertTrue(512.0 == doubles[2])
        assertTrue(257.0 == doubles[3])
    }
}

Any idea? Thanks!!!

1 answer

  • answered 2020-10-01 08:46 gidds

    I think this would be clearest with a helper function.  Here's an extension function that uses a lambda to convert pairs of bytes into a DoubleArray:

    inline fun ByteArray.mapPairsToDoubles(block: (Byte, Byte) -> Double)
        = DoubleArray(size / 2){ i -> block(this[2 * i], this[2 * i + 1]) }
    

    That uses the DoubleArray constructor which takes an initialisation lambda as well as a size, so you don't need to loop through setting values after construction.

    The required function then simply needs to know how to convert each pair of bytes into a double.  Though it would be more idiomatic as an extension function rather than a class:

    fun ByteArray.toDoubleSamples() = mapPairsToDoubles{ a, b ->
        (a.toInt() and 0xFF or (b.toInt() shl 8)).toDouble()
    }
    

    You can then call it with e.g.:

    bytes.toDoubleSamples()
    

    (.toXxx() is the conventional name for a function which returns a transformed version of an object.  The standard name for this sort of function would be toDoubleArray(), but that normally converts each value to its own double; what you're doing is more specialised, so a more specialised name would avoid confusion.)

    The only awkward thing there (and the reason why the direct conversion from Java fails) is that Kotlin is much more fussy about its numeric types, and won't automatically promote them the way Java and C do; it also doesn't have byte overloads for its bitwise operators.  So you need to call toInt() explicitly on each byte before you can call and and shl, and then call toDouble() on the result.

    The result is code that is a lot shorter, hopefully much more readable, and also very efficient!  (No intermediate arrays or lists, and — thanks to the inline — not even any unnecessary function calls.)

    (It's a bit more awkward than most Kotlin code, as primitive arrays aren't as well-supported as reference-based arrays — which are themselves not as well-supported as lists.  This is mainly for legacy reasons to do with Java compatibility.  But it's a shame that there's no chunked() implementation for ByteArray, which could have avoided the helper function, though at the cost of a temporary list.)