Up to this point we have worked with unsigned integers, *i.e.* whole
numbers which are zero or bigger than zero. Much like it happened
with endianness, the interpretation of the value of several bytes as a
negative number depends on the specific interpretation.

In computing there are two main ways to interpret the values of a
group of bytes as a negative number: *one’s complement* and
*two’s complement*.

At the moment GNU poke supports the two complement interpretation, which is really ubiquitous and is the negative encoding used by the vast majority of modern computers and operating systems.

We may consider adding support for one’s complement in the future, but only if there are real needs that would justify the effort (which wouldn’t be a small one ;)).

Unsigned values are never negative. For example:

(poke) 0UB - 1UB 0xffUB

Instead of getting a -1, we get the result of an unsigned underflow, which is the biggest possible value for an unsigned integer of size 8 bits: 0xff.

When using type specifiers like `uint<8>`

or `uint<16>`

in a
map, we get unsigned values such as 0UB. We need other type
specifiers to map signed values. These look like `int<8>`

and
`int<16>`

.

For example, let’s map a signed 16-bit value from `foo.o`:

(poke) .set obase 10 (poke) int<16> @ 0#B 28515H

Note how the suffix of the value is now `H`

and not `UH`

.
This means that the value is signed! But in this case the mapped
integer is still positive, so let’s try to get an actual negative
value:

(poke) var h = int<16> @ 0#B (poke) h - h - 1H -1H

Adding two signed integers gives you a signed integer:

(poke) 1 + 2 3

Likewise, adding two unsigned integers results in an unsigned integer:

(poke) 1U + 2U 3U

But, what happens if we mix signed and unsigned values in an expression? Is the result signed, or unsigned? Let’s find out:

(poke) 1U + 2 3U

Looks like combining an unsigned value with a signed value gives us an
unsigned value. This actually applies to all the operators that work
on integer values: multiplication, division, exponentiation, *etc*.

What actually happens is that the signed operand is converted to an
unsigned value before executing the expression. You can also convert
signed values into unsigned values (and vice-versa) using *cast
constructions*:

(poke) 2 as uint<32> 2U

Therefore, the expression `1U + 2`

is equivalent to ```
1U + 2
as uint<32>
```

:

(poke) 1U + 2 as uint<32> 3U

You may be wondering: why not doing it the other way around? Why not converting the unsigned operand into a signed value and then operate? The reason is that, given an integer of some particular size, the positive range that you can store in it is bigger when interpreted as an unsigned integer than when interpreted as a signed integer. Therefore, converting signed into unsigned before operating reduces the risk of positive overflow, which by the way is not allowed in poke. This of course assumes that we, as users, will be working with positive numbers more often than with negative numbers, but that is a reasonable assumption to do, as it is often the case!