# Splitting Int8s?

**URL:** <https://discourse.julialang.org/t/splitting-int8s/54290>\
**Category:** Performance\
**Tags:** question\
**Created:** [January 30, 2021, 7:04pm UTC](https://discourse.julialang.org/t/splitting-int8s/54290 "2021-01-30T19:04:00Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![Ian\_Slagle](https://avatars.discourse-cdn.com/v4/letter/i/b5a626/32.png) [@Ian\_Slagle](https://discourse.julialang.org/u/Ian_Slagle)\
**Post date:** [January 30, 2021, 7:04pm UTC](https://discourse.julialang.org/t/splitting-int8s/54290/1 "2021-01-30T19:04:00Z")

</div>

I have a struct that is copied a lot in my program, so reducing the size of this struct has very noticeable impacts on performance. Of the variables I have, some are an integer (currently represented as Int8) that are bounded between -4 and 4 inclusive (so 9 possible options; each could theoretically be referred to with 4 bits). Pairs of these numbers are always accessed together. Is there a way to combine two of these numbers together in one byte that is both 1) performant and 2) still readable?

---

<div class="post-metadata">

**Author:** ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)\
**Post date:** [January 30, 2021, 7:44pm UTC](https://discourse.julialang.org/t/splitting-int8s/54290/2 "2021-01-30T19:44:15Z")

</div>

If you define something like

```julia
struct smallPair
    val::UInt8
end
function smallPair(part1::Integer, part2::Integer)
    smallPair((part1+8) + (part2+8)<<4)
end
get_part1(x::smallPair) = (x.val & 0x0F) - 8
get_part2(x::smallPair) = (x.val >> 4) - 8

```

I think it should work.  
If you actually wanted to use this, you would probably want to customize printing, and enforce bounds on the parts in the constructor, but this general idea hopefully helps.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [January 30, 2021, 8:02pm UTC](https://discourse.julialang.org/t/splitting-int8s/54290/3 "2021-01-30T20:02:22Z")

</div>

> [@Oscar\_Smith](#):
>
> `get_part1(x::smallPair) = (x.val & 0x0F) - 8`

Note that doing `- 8` here promotes the result to `Int`, which may be undesirable.

---

<div class="post-metadata">

**Author:** ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)\
**Post date:** [January 30, 2021, 8:09pm UTC](https://discourse.julialang.org/t/splitting-int8s/54290/4 "2021-01-30T20:09:34Z")

</div>

The theory behind that was that if you want -4 to 4 as your output range, you want some variety of signed `Integer`, and that since you are presumably only extracting when you are doing logic after you do the extract `Int` is probably the best type to use.

---

<div class="post-metadata">

**Author:** ![Ian\_Slagle](https://avatars.discourse-cdn.com/v4/letter/i/b5a626/32.png) [@Ian\_Slagle](https://discourse.julialang.org/u/Ian_Slagle)\
**Post date:** [January 30, 2021, 8:12pm UTC](https://discourse.julialang.org/t/splitting-int8s/54290/5 "2021-01-30T20:12:07Z")

</div>

I think maybe in this specific case, wrapping it like `- Int8(8)` might be more performant, since the other numbers I’ll be doing arithmetic with them are Int8’s as well

---

<div class="post-metadata">

**Author:** ![Ian\_Slagle](https://avatars.discourse-cdn.com/v4/letter/i/b5a626/32.png) [@Ian\_Slagle](https://discourse.julialang.org/u/Ian_Slagle)\
**Post date:** [January 30, 2021, 8:49pm UTC](https://discourse.julialang.org/t/splitting-int8s/54290/6 "2021-01-30T20:49:01Z")

</div>

Out of curiosity, is there a more efficient way to add these together? The basic way that doesn’t take advantage of the built-in structure is shown below

```julia
Base.:+(x::smallPair, y::smallPair) = smallPair(get_part1(x) + get_part1(y), get_part2(x) + get_part2(y))

```

---

<div class="post-metadata">

**Author:** ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)\
**Post date:** [January 30, 2021, 8:54pm UTC](https://discourse.julialang.org/t/splitting-int8s/54290/7 "2021-01-30T20:54:15Z")

</div>

If you know that overflow won’t occur, you could just use  
`Base.:+(x::smallPair, y::smallPair) = smallPair(x.val+y.val)`

---

<div class="post-metadata">

**Author:** ![Ian\_Slagle](https://avatars.discourse-cdn.com/v4/letter/i/b5a626/32.png) [@Ian\_Slagle](https://discourse.julialang.org/u/Ian_Slagle)\
**Post date:** [January 30, 2021, 9:21pm UTC](https://discourse.julialang.org/t/splitting-int8s/54290/8 "2021-01-30T21:21:58Z")

</div>

> [@Oscar\_Smith](#):
>
> `Base.:+(x::smallPair, y::smallPair) = smallPair(x.val+y.val)`

This doesn’t seem to work as expected.

Here is my setup (which should avoid overflow:

```julia
struct smallPair
    val::UInt8
end

function smallPair(part1::Int8, part2::Int8)
    smallPair((clamp(part1, Int8(-4), Int8(4)) + Int16(8)) + 
        (clamp(part2, Int8(-4), Int8(4)) + Int16(8))<<Int16(4))
end

get_part1(x::smallPair) = Int8(x.val & 0x0F) - Int8(8)
get_part2(x::smallPair) = Int8(x.val >> 4) - Int8(8)

```

This works as expected, returning (1, 1):

```julia
Base.:+(x::smallPair, y::smallPair) = smallPair(get_part1(x) + get_part1(y), get_part2(x) + get_part2(y))
x = smallPair(Int8(1), Int8(0))
y = smallPair(Int8(0), Int8(1))
z = x + y
println("$(get_part1(z)), $(get_part2(z))")

```

This does not, and returns (-7, -6)

```julia
Base.:+(x::smallPair, y::smallPair) = smallPair(x.val+y.val)
x = smallPair(Int8(1), Int8(0))
y = smallPair(Int8(0), Int8(1))
z = x + y
println("$(get_part1(z)), $(get_part2(z))")

```

Am I missing some overflow?

---

<div class="post-metadata">

**Author:** ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)\
**Post date:** [January 30, 2021, 9:36pm UTC](https://discourse.julialang.org/t/splitting-int8s/54290/9 "2021-01-30T21:36:25Z")

</div>

Oh yeah, good point. I forgot to compensate the offsetting. It should be `Base.:+(x::smallPair, y::smallPair) = smallPair(x.val+y.val+UInt8(136))`.  
(`136==8<<4 +8`)

---

<div class="post-metadata">

**Author:** ![Ian\_Slagle](https://avatars.discourse-cdn.com/v4/letter/i/b5a626/32.png) [@Ian\_Slagle](https://discourse.julialang.org/u/Ian_Slagle)\
**Post date:** [January 30, 2021, 9:39pm UTC](https://discourse.julialang.org/t/splitting-int8s/54290/10 "2021-01-30T21:39:19Z")

</div>

I think it should be `Base.:+(x::smallPair, y::smallPair) = smallPair(x.val+y.val+UInt8(120))` but otherwise works great!

---

<div class="post-metadata">

**Author:** ![Ian\_Slagle](https://avatars.discourse-cdn.com/v4/letter/i/b5a626/32.png) [@Ian\_Slagle](https://discourse.julialang.org/u/Ian_Slagle)\
**Post date:** [January 30, 2021, 9:41pm UTC](https://discourse.julialang.org/t/splitting-int8s/54290/11 "2021-01-30T21:41:17Z")

</div>

Unfortunately it doesn’t preserve the bounds like the other version though.

---

<div class="post-metadata">

**Author:** ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)\
**Post date:** [January 30, 2021, 11:04pm UTC](https://discourse.julialang.org/t/splitting-int8s/54290/12 "2021-01-30T23:04:39Z")

</div>

Yeah, if you want to check for overflow, I don’t think you’ll be able to do notably better than your version.

---

<div class="post-metadata">

**Author:** ![FPGro](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fpgro/32/20822_2.png) [@FPGro](https://discourse.julialang.org/u/FPGro)\
**Post date:** [January 31, 2021, 4:31am UTC](https://discourse.julialang.org/t/splitting-int8s/54290/13 "2021-01-31T04:31:00Z")

</div>

You don’t even need to convert anything to UInt. Combination of signed and unsigned bitshifts allows this:

```julia
julia> struct IntPair
           val::Int8
       end

julia> IntPair(left::Int8, right::Int8) = IntPair((left << 4) | (right << 4 >>> 4))
IntPair

julia> IntPair(l,r) = IntPair(Int8(l),Int8(r))
IntPair

julia> decode(p::IntPair) = (p.val >> 4, p.val << 4 >> 4)
decode (generic function with 1 method)

```

check for correctness:

```julia
julia> all(p == decode(IntPair(p...)) for p in Iterators.product(-8:7, -8:7))
true

```

and piecewise addition becomes:

```julia
julia> Base.:+(l::IntPair, r::IntPair) = IntPair(((l.val & Int8(-16))+(r.val & Int8(-16))) + ((l.val+r.val) & Int8(15)))
# can't use UInt literals here, because they lead to conversion errors and this is even typestable throughout

```

check:

```julia
julia> all( (l .+ r) == decode(IntPair(l...) + IntPair(r...))
       for l in Iterators.product(-8:7, -8:7) for r in Iterators.product(-8:7, -8:7)
       if (-8≤(first(l)+first(r))≤7)&&(-8≤(last(l)+last(r))≤7)) # check that this combination won't overflow our 4bit range
true

```
