Say I have this byte: 1011 1100. Now say I want to split it this way
0000 1011
0000 1100
And most importantly how do I specify bit by bit a Byte? I could achieve the split in msb and lsb in a breze if i could do something like val msb = '1011 1100' and '1111 0000' ushr 4
Thanks Btw, the toByte() function truncates the lsbs? Because the docs mentions a rounding as well
And why is not possible to represent a byte that starts with 1? I get the sign stuff but why should i use a complete different class to let me set the first bit (I assume for consistency when casting to Int, Long, ecc…)?
That’s quite true. The binary NUMBER 10111100 bin = 188 dec cannot be represented by a Byte, but the data 1011 1100 is a value that a Byte can store, representing the number -68 (which, in this case, we don’t particularly care about since we’re only concerned with the bits).
fun main() {
val b: Byte = 0b10111100.toByte()
println(b)
}
The correct terminology for the 4 bit groupings is nibble.
As others have shown you specify a literal bit by bit you use the 0b prefix as in 0b01101101, but in usual Java fashion bytes are signed and just like you can’t say val b: Byte = 255 you will have trouble if that bit for bit literal has bit 7 set and you will have to say .toByte() or use the negative of the 2’s complement. In most byte oriented work where you are dealing with data just as 8 bits at a time you might want to switch to Kotlin’s experimental support for unsigned values.
If I needed this functionality, I would implement it as extension properties:
val Byte.upperNibble get() = (this.toInt() shr 4 and 0b1111).toByte()
val Byte.lowerNibble get() = (this.toInt() and 0b1111).toByte()
val UByte.upperNibble get() = (this.toInt() shr 4 and 0b1111).toUByte()
val UByte.lowerNibble get() = (this.toInt() and 0b1111).toUByte()
That was exactly what i was trying to do! One last question, when calling toInt() or toByte() from an higher byte sized data structure like a Double or Long or someInt.toByte() which bits am I keeping, msbs or lsbs?
First off realize that such an operation is providing a specific ordering to the bytes. Your choice of ordering is not the only valid ordering. In your case, you have chosen little endian ordering. All I am saying is that I would make sure to indicate that it is little endian or LE in the method name and also provide a big endian version as well.
If you were on the Java platform I would steer you toward a java.nio.ByteBuffer for doing this form of manipulation.
Having this function create the byteArray seems a bad idea and inefficient since you might have an array you want to reuse or you might have a big array into which you want to multiple values. Because of this I would make the target the array and code it like this:
Shouldn’t you write this[i+offset] instead of just this[i]? Like:
fun ByteArray.writeBytesLE(value: Long, offset : Int = 0) : Int {
if (this.size - offset >= 8)
throw IndexOutOfBoundsException("The remaining space is less then 8")
var buff = value
repeat(8){
this[it+offset] = buff.toByte()
buff = buff shr 8
}
return offset + 8
}