9

Are there any semantics differences? Is one of them likely to be faster under specific circumstances?

sanjoyd
  • 3,260
  • 2
  • 16
  • 22
  • Sorry I was wrong before. [Check this answer](http://stackoverflow.com/a/5793952/1231073). – sgarizvi Jan 19 '13 at 17:00
  • are you talking about how a given language will compile (a+b) vs (a+(-b)) or are you talking about direct assembly? Most compilers, I imagine would compile (a+(-b)) directly to a single fsub, but that, I guess, would depend on which language and compiler we're talking about. In direct assembly adding a negative would take two instructions (in the x87 unit this would be fchs followed by fadd instead of a direct fsub. fadd and fsub both execute in the same time so the former would be slower. – J... Jan 19 '13 at 17:02

3 Answers3

14

If you have -x precomputed, then sub smth, x and add smth, -x are going to execute equally quickly.

Semantically, there will be a difference in terms of the flags state.

Consider doing 8-bit addition and subtraction:

0x01 - 0x01 = 0x00, CF = 0
0x01 + 0xFF = 0x00, CF = 1
Alexey Frunze
  • 61,140
  • 12
  • 83
  • 180
  • 3
    Actually in hardware they are both implemented the same (via a logical ADD unit). If you send SUBTRACT to the processor, it twos-compliments it before it hits the processor, but since the hardware is all in parallel you get no performance hit so they are exactly as fast as each other :) – SecurityMatt Jan 22 '13 at 06:12
  • 1
    @SecurityMatt [I know](http://stackoverflow.com/questions/8034566/overflow-and-carry-flags-on-z80/8037485#8037485). – Alexey Frunze Jan 22 '13 at 06:20
  • 3
    Cool. It just wasn't obvious from your post that's all. – SecurityMatt Jan 23 '13 at 02:19
  • 1
    If the implemented hardware is the same, how does it know when to set the flag appropriately? – Tung Nguyen Feb 15 '18 at 16:05
  • @TungNguyen See the code in [this](https://stackoverflow.com/a/8037485/968261) answer. – Alexey Frunze Feb 15 '18 at 18:55
  • @ Alexey Frunze: Suppose I substract 126 from 0. If I understand correctly, 126 would be 1000 0000 in computer and the 2 complement is the same. So the 126 operand would go through a gate to be negated and then goes to the addition unit in the ALU as usual, right ?. Then it would be 0000 0000 + 1000 0000. Certainly there is no carry at the 8th bit here. But as I test it, the carry flag is still set on. – Tung Nguyen Feb 15 '18 at 19:13
  • ^: typo, I meant 128, not 126 – Tung Nguyen Feb 15 '18 at 19:19
  • @TungNguyen You completely ignored the carry/borrow. Look at Sbb() again. It inverts carry-in before Adc() and inverts carry-out after Adc(). See, it correctly produces the line ` 0( 0) - 128(-128) - 0 = 128(-128) CY=1 OV=1`. Your gates invert 3 things: the subtrahend and the carry before and after the addition. – Alexey Frunze Feb 16 '18 at 13:46
6

In addition to the flags being different, if x is a constant, choosing one or the other may lead to a shorter encoding (rarely).

Add and subtract both have a op r/m32, imm8 form where the immediate operand is a byte, but that byte is sign-extended. So add edx, 128 would have to be encoded with a dword immediate, but sub edx, -128 could be encoded with a sign-extended byte (saving 3 bytes).

harold
  • 61,398
  • 6
  • 86
  • 164
3

As Alexey Frunze has pointed out, there is a difference in the flag state. In addition, there is a difference in the number of possible representable values. In a two's complement number system, there is one more negative value than positive values. Adding a negative number will let you take advantage of this, subtracting a positive number will not.

EDIT:

The underlying issue is that what we usually think of as "integers" aren't actually integers. In mathematical terms, they are members of a quotient ring, a concept from abstract algebra. This means that for every 32-bit "integer" a, there is another 32-bit "integer" b, such that a + b = 0. Whether these numbers are regarded as "positive" or "negative" is merely an interpretation. This means that my point is both correct and incorrect. In terms of the quotient ring it is incorrect but in terms of our usual interpretation it is correct. There must be some number we can subtract and get the same result as if we added -2147483248. That number isn't 2147483248, however, which seems counter-intuitive.

Jørgen Fogh
  • 7,516
  • 2
  • 36
  • 46
  • Subtracting 0x80 is going to be the same as adding 0x80 (with 8-bit add/sub) unless we look at flags. In 8 bits 0x80 and -0x80 are the same bit pattern 10000000B. Could you elaborate on the advantage/disadvantage? – Alexey Frunze Jan 20 '13 at 11:39
  • @Alexey Frunze: Consider the smallest representable value in a signed 32 bit integer: -2147483248. Now compare that to the largest representable value, 2147483647. It is clear that the magnitude of the former is one larger than the magnitude of the latter. This means that you can subtract a slightly larger value by adding -2147483248 than by subtracting 2147483647. – Jørgen Fogh Jan 20 '13 at 12:36
  • You can subtract -2147483248 or +2147483248 (in modulo-2^32 arithmetic) and arrive at the same result (except flags) as with adding -2147483248. – Alexey Frunze Jan 20 '13 at 12:52
  • +2147483248 doesn't fit in a 32-bit signed integer. -2147483248 does. – Jørgen Fogh Jan 20 '13 at 13:15
  • The CPU does not distinguish signed from unsigned in addition and subtraction. – Alexey Frunze Jan 20 '13 at 13:17
  • True. Usually numbers are regarded as either signed or unsigned when we reason about programs though. – Jørgen Fogh Jan 20 '13 at 13:21
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/23048/discussion-between-jorgen-fogh-and-alexey-frunze) – Jørgen Fogh Jan 20 '13 at 13:33