5

I have just started to study about computer systems and I came across this line. How can the difference in the contexts in which we view data objects make this happen?

Bytes store numbers, now that number represents something, a symbol or a character or something else so how can the same sequence of bytes represent different things in different contexts?

6
  • This actually belongs on the Philosophy SE site. It's a valid question. But it's not related to software engineering. Jul 6, 2017 at 16:55
  • 1
    Short answer: we call it code because it represents something different than just the ones and zeros that are apparent. What it does represent is a matter of convention, and context. If the cat says "miauw" when it's at the door it means "open it please, I want to go out". If it says "miauw" when it's in the kitchen it means "give me food". If a series of bits are in a piece of memory agreed on to be a number, they may represent a digit. If the same bits are in a piece of memory agreed on to be text, they could represent a character. Jul 6, 2017 at 19:51
  • The question and answers in softwareengineering.stackexchange.com/questions/291950/… may have some useful insight Jul 7, 2017 at 6:27
  • 1
    @RibaldEddie: you might think this is a philosophical question, but it has a straight-forward technical answer, and is directly related to how the electronics of a computer operate. Jul 7, 2017 at 6:37
  • Bytes don't store numbers, they store patterns of bits. How each bit is interpreted, how they are combined and how their combinations are interpreted do not always give numbers. Bit patterns can even cross byte boundaries.
    – mouviciel
    Jul 7, 2017 at 9:22

5 Answers 5

8

The 8-bit binary pattern 10000000 (aka 0x80), can represent:

  • in Windows Code Page 1252: the EURO symbol
  • in Latin-1: a control character
  • in UTF-8: a continuation byte contributing 6 bits of zero to a code point

Even getting the value as a number requires an interpretation:

Some early computers even strung the bits backward, as in the Manchester Baby experimental computer system (of the late 1940's).

So, a given bit string can have many different interpretations. Whenever we retrieve or operate on information we consider the size (number of bits/bytes), the actual bit pattern (the value), and the type, which describes the interpretation of that bit pattern. That way we know how to manipulate the information such as being able to use arithmetic on a number, or display a representation of the value (as a symbol or number) on the screen.

Floating point values generally use either 4 (float) or 8 bytes (double), though there are others. The interpretation of those bits uses fields, dividing the bits into (1) an overall sign bit, (2) mantissa bits, (3) an exponent sign bit and exponent value.

Since 4 and 8 bytes are also popular sizes for integer values, the same 4 byte value can be interpreted as integer or float.

There is no way to examine a bit pattern or value to know what type is intended, that is, whether the value is meant to be an integer, a float, or a code point or symbol, or something else. The value alone is insufficient; we need something extra for differentiation.

We generally do this the other way around: rather than looking at the bits to guess what it might be, we, as programmers, tell the computer how to interpret information. Thus, it is by design that we know how to interpret the value.

Another approach is to tag a value by using (some additional) memory for a tag or descriptor; this allows for dynamic interpretation.

It is generally considered a logic error or flaw in program design or a flaw in programming to interpret the same value using a one interpretation at one point and switching interpretations to another at some other point.

Programs generally decide in advance how a given 4 byte value is going to be interpreted, as we cannot tell by the value. This is part of the type system of programming languages, and in turn of computer instruction sets.

Good type systems prevent illegal and undesired program states and logic errors by preventing the accidental mixing of interpretations, and broadly speaking, this is a very serious issue in programming, with a lot of active and ongoing research. Many type systems use a combination of design-time, fixed determination of type, together with some form of tagging for more dynamic capabilities. Some type systems also work at preventing other logic errors beyond accidental misinterpretation, such as array reference out of bounds, null pointer dereferencing, memory leaks, ownership of memory, race conditions, etc..

A computer instruction set typically provides different instructions for manipulating different sized items with a variety of interpretations (e.g. as signed integers, as unsigned bits, as float or double, etc..). A high-level programming language makes use of these varied instructions to accomplish the intent of the program.

6
  • "There is no way to examine a bit pattern or value to know if the value is meant to be an integer, a float, or a code point or symbol, or something else." ... In general, yes, but worth noting that many programming language implementations use a convention where each value is tagged with a representation of its type in some form, which means that as long as you're using such a language and know that your bitstring is aligned with the start of a value you should be able to determine the meaning.
    – Jules
    Jul 6, 2017 at 18:13
  • 5
    @Jules While that is true, the processor doesn't know bollocks about the programming language. It just knows it needs to add Register 2 and Register 5 and store the value on Register 9. Someone looking at the values stored in the registers can't tell what the hell they exactly are. I think that's the point Erik is coming from.
    – T. Sar
    Jul 6, 2017 at 18:19
  • 1
    @Jules, thanks for the mention of tagging. Yes, we can add some bits (e.g. another byte) to a 4 byte number, or we steal some bits of a 4 byte number (limiting its range) to dynamically tag the type of a value. Dynamic type systems do something like this.
    – Erik Eidt
    Jul 6, 2017 at 18:20
  • There are some tricks in C and C++ that perform floating point math by bit manipulation some of these use a union to map the bit value to the different interpretations needed e.g. float and int Jul 6, 2017 at 20:01
  • 3
    @Jules: even if a programming language uses some bits for tags, that's turtles all the way down because somehow the CPU has to know about those tag bits, and that is no different than knowing between an int and a float. Jul 7, 2017 at 6:35
0

As you said, the meaning of a specific bit pattern is context dependent. To understand what data is encoded in a particular bit pattern, you not only need to know the bit pattern, but also what's done to it.

If the memory location containing that binary data is passed as an address of an object, then that data is probably some kind of pointer.

If the memory is given to an add instruction, then it's probably a number.

If the memory location is pointed to by the program counter, then it's probably an instruction OP code that meant to be executed by the CPU.

0

Most programming languages have a concept known as "type," implemented via a "type system."

When a programmer writes a program, he will generally declare a set of variables and then write some source code that tells the computer to do something with the variables. Each variable, when declared, is associated with a type, such as integer, byte, or string. The type determines how the compiler will interpret the source code that works with the variables, which will determine what sort of machine code to emit during compilation.

For example, in c#, if I want to add or subtract two numbers I might write:

int a = 2;
int b = 2;
int c = a + b;
int d = a - b;

In this code, I have told c# that I want four memory locations, and I want them treated as integers. On the third line, I can use the + operator on a and b because addition is allowed for integers. And on the fourth line I can use -.

If I wanted to work with strings, I might write this instead:

string a = "2";
string b = "2";
string c = a + b;
string d = a - b;  //Error

In this example, I have told c# to set up four memory locations and treat them like strings. On line 3 I can add them together because addition is valid for strings (it will concatenate them). But on the fourth line the compiler will tell me I have made a mistake, because subtraction is not allowed for strings, since it is not exactly clear what it would do if it were allowed.

Similarly there are data types for dates, times, floating point numbers, and complex structures. In addition, a programmer can often define his own types, such as objects, which can be programmed to represent business concepts, database connections, or other more advanced structures, all of which require a memory location for storage, but have different rules for how to read those memory locations.

In some rare cases you can copy data of one type to a variable of another type. This is generally not allowed in "type safe" languages because the data could be misinterpreted. But in languages like assembly and c, you can do it, and you will usually need to be very careful not to cause problems.

Now what about code? Well, code is just another memory location, and it too gets a name ("symbol"), known as the entry point:

static void main()
{
    int a = 2;
    int b = 2;
    int c = a + b;
    int d = a - b;
}

In the above example, I declare a memory location with an entry point named "main." This memory location will contain the instructions to reserve memory for a, b, c, and d, and it will contain instructions to set them to 2 and to do the math. When I write source code that calls main, the compiler will emit machine-readable code that points the instruction pointer at the entry point main, where it will execute the instructions. Thus there is never any mixing between code and data, and the computer (hopefully) will never try to execute a number or try to do math on code.

5
  • 3
    Doesn't my answer address that question? The answer is: the CPU treats it the way it is told to, by the programmer, when he declares its type.
    – John Wu
    Jul 6, 2017 at 17:55
  • The CPU doesn't need to make any decisions about the type of data stored in a memory location. It does what it is told by the compiler. The compiler knows the type based on the programmer's instructions. I don't know what you mean by "see."
    – John Wu
    Jul 6, 2017 at 18:46
  • 1
    Eh... it's a bit hard to explain. I understand from where you coming from and you a 100% correct, but my point is that, at least for me, the point of confusion for the OP seems to a be a bit lower level that what is seem by the compiler. Or, phrasing it in another way - without any other information, how can one determine what exactly is the binary number stored on a specific position in memory? You really can't - you depend on the program to be able to understand what means what in a given stack.
    – T. Sar
    Jul 6, 2017 at 18:58
  • I guess it boils down to "you can't unless you know the context of the program that actually owns that value". Well, nervermind that, I'm removing my other comments.
    – T. Sar
    Jul 6, 2017 at 19:00
  • @T.Sar: I agree with your thought. I am not sure this would have answered it for me if I was the OP. Jul 7, 2017 at 6:32
0

Ben Eater has a nice series of videos on YouTube in which he builds an 8-bit computer using breadboards. The result is drastically simplified from what today's computers look like, and lacks a lot of stuff that's standard nowadays, but the underlying concepts are the same. In the videos, he explains machine instructions, memory addresses, storing simple data, etc - and these values are all just stored as bits in various registers and EEPROMs.

The same bits represent different things based on the context in which they live. So, a value of 0b0001 means instruction LDA ("Load A", meaning load a value into the A register) when that value is stored in the instruction register, but it means the number 1 when stored in memory, for example. There is some bit of circuity to decode the LDA instruction into a series of control logic that the computer executes - move a value onto the bus, move a value off the bus, etc.

The key to understanding this, IMO, is realizing that at some point you (as the designer / builder of the computer) get to decide all that. There is nothing intrinsic about the value 0b0001 meaning LDA - it's a decision that Ben Eater made in his videos. He could have just as easily decided that 0b0101 would be LDA, or that 0b0001 would be the ADD instruction. Even the set of instructions that are available need to be decided, as well as the arguments for the instruction, how many microinstructions it will have, etc.

And, as @ErikEidt mentioned in his answer, you have to decide in what format you store your values - unsigned, signed, twos-complement, ones-complement, etc. You may use different formats for different registers or types of memory (instructions probably don't need to be signed, for instance, but you probably want to perform computations involving negative numbers elsewhere in your computer). Again, though, this is a decision you make when you're building the computer.

Once you have all that, you can start to build on top of it with operating systems, programs, programming languages, etc.

0

At the lowest level (memory and processor) bytes does not intrinsically represent anything (not even numbers!), they are just patterns of bits. Bytes only get "meaning" in how programs operate on these bytes.

Processors take sets of bytes as input to instructions, and each instruction interprets the bytes a specific way, eg.

  • the ADD instruction interprets the bytes as integer numbers (where the bits are interpreted as binary digits), and adds them using integer arithmetic.
  • the FADD instruction interprets the bytes as floating-point numbers and adds them using floating point arithmetic (see this for how floating point numbers are represented as bits.)
  • the AND instruction treats the bytes as booleans or bit-flags (not numbers!) and performs bit-wise logical operations.
  • the MOV instruction copies bytes from one place to another, and doesn't interpret them at all.
  • the JMP instruction interpret the bytes as a memory address. But the bytes located at that memory address will be interpreted as instructions.

So in short the program code specifies how bits are as interpreted as different kinds of values.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.