Next: , Previous: , Up: Basic Editing   [Contents][Index]


2.8 Negative Integers

2.8.1 Negative Encodings

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.

GNU poke supports both interpretations, and the interpretation that is used depends on the global setting that can be changed using the .set nenc dot-command. The possible values are 2c for two’s complement and 1c for one’s complement.

(poke) .set nenc
2c
(poke) .set nenc 1c

The default is two’s complement, which is the negative encoding used in the vast majority of modern computers and operating systems.

2.8.2 Signed Integers

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. It follows that 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 it is still positive, so let’s try to get an actual negative value:

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

2.8.3 Mixing Signed and Unsigned Integers

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 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. 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!


Next: , Previous: , Up: Basic Editing   [Contents][Index]