# What is the purpose of the "difference of absolute values" instruction?

The IBM NORC computer, among others, had an arithmetic instruction computing the difference of the absolute values of its operands (|x|-|y|, see NORC Programming Manual, page 11, opcode 28), which seems less useful than the "absolute difference", aka "distance" (|x-y|).

Apparently, at the time the instruction was considered useful enough for Sergey Lebedev to include it in the M-20 and the BESM-6.

What calculations was the "difference of absolute values" instruction intended for?

• Some of the other arithmetic opcodes, e.g. separate instructions for x+y and -(x+y) and similarly for subtraction etc, suggest the sign data was processed separately from the numeric value. Possibly it was cheaper to implement |x|-|y| than |x-y|. For testing the convergence of numerical algorithms, it probably makes little practical difference anyway except for the (unlikely?) edge case where +x is close to -y. Oct 13, 2020 at 1:36
• @alephzero I've noticed the separate instruction with negation in NORC as well, but that could have been an attempt to reduce the instruction count, given the memory constraints of the era; also, the "subtraction with inverse result sign" does not make sense, as its functionality is equivalent to the regular subtraction with the operands reversed. |x|-|y| is a poor replacement for |x-y|; there must have been a good reason to include it at all. Oct 13, 2020 at 3:48
• That manual is really a joy to read: Thus, when a stop occurs,...the operator's feeling of security and euphoria is not disturbed. Or: The non-conformist,....is inhibited only by the frowns of the rest of staff... [from doing something wrong] Oct 13, 2020 at 7:51
• How are signed values implemented on this system? Oct 13, 2020 at 9:21

The programming manual does not explicitly give examples of how the difference of absolute values instructions were to be used. However, it appears to be the easiest and fastest way to perform an absolute value, by using zero as the other operand. You can also perform a negative absolute value, which is consistent with the fact that all of the other arithmetic operations have negative versions.

The NORC has 2000 decimal words of memory, with each word being 16 binary-coded-decimal digits. That makes 64 bits per word. A "bit count" of the number of bits set to one, modulo 4, was also stored in 2 additional bits as a form of parity error checking. The memory is painted as 2000 dots (one dot per address) on the face of 66 cathode ray tubes (one per bit). The CRTs were periodically refreshed. If the 64 bits of a word did not match the bit count, or if a nibble encoded outside the binary-coded-decimal range, then an error indicator was lit and the machine halted.

Memory was shared for instructions and data. The first two of the 16 decimal digits of a word (called the `P` field) specified the contents of the word:

• `P` from 00 to 30 represents a floating point value with an exponent from 100 to 1030. The next decimal digit of the word is the sign (0 for positive, 1 for negative). This made inverting the sign easy, and most arithmetic operations had both normal and inverted sign versions. The remaining 13 decimal digits of the word are the mantissa.

• `P` from 70 to 99 represents a floating point value with an exponent from 10-30 to 10-1. The next decimal digit of the word is the sign (0 for positive, 1 for negative). The remaining 13 decimal digits of the word are the mantissa.

• `P` from 40 to 59 represents an instruction word. It appears that the instruction decoder completely ignored the `P` field; rather, it was a hint for the human operators examining memory that such a word was instruction rather than data. By convention, only the number 50 was actually used for `P`.

The remaining 14 decimal digits of an instruction word were then split into fields:

• Two decimal digits called `Q` specified the opcode of the instruction.

• Four decimal digits called `R` specified the memory address of the first operand of the instruction.

• Four decimal digits called `S` specified the memory address of the second operand of the instruction.

• Four decimal digits called `T` specified the memory address to store the result.

Values of `R`, `S`, or `T` in the range 0000 to 1999 were treated as direct memory addresses. Values above 3999 selected an index register, which was added to the value, and then modulo 2000 specified the actual memory address.

`Q` opcodes below 20 seem to be unused. Opcodes from 20 to 28 performed floating-point arithmetic on the `R` and `S` operands, with a rounded result stored in `T`:

``````20  Addition                                   R+S
21  Addition with inverse result sign        -(R+S)
22  Subtraction                                R-S
23  Subtraction with inverse result sign     -(R-S)
24  Multiplication                             R*S
25  Multiplication with inverse result sign  -(R*S)
26  Division                                   R/S
27  Division with inverse result sign        -(R/S)
28  Difference of absolute values            |R| - |S|
``````

It's worth noting that only subtraction was directly implemented in hardware, and that the other arithmetic operations were implemented by subtraction:

The arithmetic unit adds, multiplies, and divides with subtraction performed by adding the tens complement of one operand to another.

`Q` opcodes from 30 to 38 are identical to the 2X opcodes, except that the floating-point result is not rounded.

Opcode 40 performs an unsigned, 16-decimal-digit integer addition of the operands in `R` and `S`, storing the result in `T`. Opcode 41 similarly performs and integer subtraction. The manual suggests that this was intended for self-modifying code.

Opcodes in the 5X range modify the index registers.

Opcodes in the 6X and 7X range are conditional and unconditional jumps, and halt. It is possible to implement subroutine calls and returns. There are jumps conditioned on an operand's sign, but implementing absolute value using conditional jumps requires several instructions, compared to the one instruction of the difference of absolute values.

The 8X opcodes operate the printer, and the 9X opcodes operate magnetic tape storage.

There are 24 suboperations that are used for instruction execution. As previously mentioned, the only floating-point arithmetic suboperation was subtraction.

• To compute the absolute value of a number by using zero as the other operand, the instruction is a very poor choice, as it is either slower than necessary (requiring pre-normalizing the zero to the right if the exponent is positive), or it loses precision (due to pre-normalizing the argument to the right if the exponent is negative). Oct 13, 2020 at 16:29
• @LeoB.: Nope. Page 8 of the manual states that if an operand is zero, the result is directly obtained from the other operand, and that the regular arithmetic operation is not performed. Oct 13, 2020 at 17:01
• Okay, then. Although it still seems like an overkill if the main intended purpose was to always use the instruction with one of the operands being zero. On the other hand, having two separate subtraction instructions (regular and negated) is no less of an overkill, so your conjecture is plausible. Oct 13, 2020 at 18:41
• There is a wrinkle: on the BESM-6, there is a separate "negate conditional" instruction, which negates the accumulator based on the sign of its operand, that Acc = ABS(X) is done as "LOAD X; NEG X", or, to compute the absolute value of Acc, as "STORE (SP); NEG(SP)". Therefore, if the only purpose of the NORC's "difference of absolute values" was to compute absolute values, there would be no need to copy it, given the conditional negation instruction. Apparently, Lebedev had some independent use for "difference of absolute values" in mind. Oct 14, 2020 at 20:19
• "only subtraction was directly implemented in hardware": surely the following quote means exactly the opposite of that (i.e. what would have been more clearly indicated by "... divides, with subtraction ..."). It's very unusual phrasing otherwise. Oct 24, 2020 at 0:04

A variant of the compensated summation algorithm needs to perform comparisons of absolute values of the running sum and the current summand:

``````function KahanBabushkaNeumaierSum(input)
var sum = 0.0
var c = 0.0                       // A running compensation for lost low-order bits.

for i = 1 to input.length do
var t = sum + input[i]
if |sum| >= |input[i]| then   // <===== Comparison of absolute values
c += (sum - t) + input[i] // If sum is bigger, low-order digits of input[i] are lost.
else
c += (input[i] - t) + sum // Else low-order digits of sum are lost.
endif
sum = t
next i

return sum + c                    // Correction only applied once in the very end.
``````

While the Neumaier's version of the algorithm was published in 1974, it is conceivable that pre-existing numerical algorithms, including extended-precision algorithms, benefitted from computing a difference of absolute values in one instruction as well.

An example would be sorting array values by magnitude before summing.