0
/* 
 * isLessOrEqual - if x <= y  then return 1, else return 0 
 *   Example: isLessOrEqual(4,5) = 1.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 24
 *   Rating: 3
 */
    int isLessOrEqual(int x, int y)
{

  int msbX = x>>31;
  int msbY = y>>31;
  int sum_xy = (y+(~x+1));

  int twoPosAndNegative = (!msbX & !msbY) & sum_xy; //isLessOrEqual is FALSE. 
  // if = true, twoPosAndNegative = 1; Overflow true
  // twoPos = Negative means y < x which means that this 
  int twoNegAndPositive = (msbX & msbY) & !sum_xy;//isLessOrEqual is FALSE
  //We started with two negative numbers, and subtracted X, resulting in positive. Therefore, x is bigger.
  int isEqual = (!x^!y); //isLessOrEqual is TRUE
  return (twoPosAndNegative | twoNegAndPositive | isEqual);
    }

Currently, I am trying to work through how to carry bits in this operator. The purpose of this function is to identify whether or not int y >= int x.

This is part of a class assignment, so there are restrictions on casting and which operators I can use.

I'm trying to account for a carried bit by applying a mask of the complement of the MSB, to try and remove the most significant bit from the equation, so that they may overflow without causing an issue.

I am under the impression that, ignoring cases of overflow, the returned operator would work.

EDIT: Here is my adjusted code, still not working. But, I think this is progress? I feel like I'm chasing my own tail.

Derp
  • 143
  • 1
  • 2
  • 11
  • Given that this is homework, I am not sure how much hinting is desired. Some hints: a <= b is equivalent to r = (a - b) <= 0. lessThanOrEqual is true when sign_bit(r) != overflow_flag. The overflow flag is set based on carry-in and carry-out of the MSB (most significant bit, here bit 31): It is set when they differ, cleared otherwise. Also, a-b = a + ~b +1. At most you should need 12 operations (counting `~` as a separate operation); one solution would use 11 ALU operations plus one shift (10 ALU operations if you can guarantee the shift is logical, not arithmetic). – njuffa Jan 31 '17 at 19:55
  • Correction: lessThanOrEqual is true when sign_bit(r) != overflow_flag OR when r==0. Sorry for the confusion. – njuffa Jan 31 '17 at 20:40
  • If you make use of an observation by Peter L. Montgomery in a [newsgroup posting](https://groups.google.com/d/msg/comp.arch/gXFuGZtZKag/_5yrz2zDbe4J) in the year 2000, and you have direct control over whether shifts are of the arithmetic or logical kind, you can get down to six operations (four ALU operations and two shifts). – njuffa Jan 31 '17 at 22:31
  • I'm still working on this- I'm reading through the comments you had linked. Give me a few! – Derp Jan 31 '17 at 22:40
  • Keep in mind that right shifts of signed integers with negative value lead to undefined behavior in C/C++. Only right shifts of unsigned integers are well-defined for all values (unless the shift count is equal to or greater the number of bits in the integer) – njuffa Jan 31 '17 at 22:51
  • @njuffa Could you elaborate further on the "Carry in and carry out" of MSB? The way I understand your solution is that, when the MSB(a) == MSB(b), the overflow flag is 1, essentially, (a&b), else, 0 and no need to account for overflow. – Derp Jan 31 '17 at 22:54
  • @njuffa I am totally lost, still! – Derp Feb 01 '17 at 00:49
  • You mostly only care about the MSB (bit 31) here. In a typical CPU, the overflow flag (OF) indicates whether there was an overflow during signed integer arithmetic, here: subtraction. This is generated by XORing the carry-in to the MSB (so carry generated at bit 30, and propagated to bit 31) and the carry-out from the MSB (so the carry generated at bit 31 , and being propagated into bit 32, which doesn't exist, but on many processor feeds into the carry flag CF). To get the carry-in and carry-out, you need to separate the MSBs from the 31 low-order bits, then add. a-b <= 0; a+~b+1<=0; a+~b<0 – njuffa Feb 01 '17 at 01:24

2 Answers2

1
int isLessOrEqual(int x, int y)
{
    int msbX = x >> 31;
    int msbY = y >> 31;
    int sign_xy_sum = (y + (~x + 1)) >> 31;
    return ((!msbY & msbX) | (!sign_xy_sum & (!msbY | msbX)));
}

I figured it out with the assistance of one of my peers, alongside the commentators here on StackOverflow.

The solution is as seen above.

njuffa
  • 23,970
  • 4
  • 78
  • 130
Derp
  • 143
  • 1
  • 2
  • 11
  • 1
    The code doesn't compile, there is a syntax error: `(~x + !)` – njuffa Feb 01 '17 at 06:04
  • 1
    @njuffa Whoops! I placed an ! where I wanted a 1. Thanks for the heads up. – Derp Feb 01 '17 at 07:29
  • 1
    I fixed a typo (`mxbY`) and now the code seems to work fine when compiled for an x84 platform. – njuffa Feb 01 '17 at 08:30
  • `x >> 31` is not defined by the C++ standard. (Is it supposed to produce 1 or –1?) Better to use `x >> 31U` or `(unsigned) x >> 31`. – Potatoswatter Feb 01 '17 at 08:50
  • @njuffa I can copy+paste the code from my work station later. I wrote the code from my personal computer (as I was away from the computer I program on). I'll be sure to edit this and paste that one to be sure it operates full. – Derp Feb 01 '17 at 15:15
  • @Potatoswatter Your point is totally valid- however, given this is a class assignment I have some restrictions on what I may or may not do. Casting is one of those things that I may not do- as a result I have to cross my fingers and hope it works out. – Derp Feb 01 '17 at 15:18
  • Right shifting of signed integers with negative value is implementation defined per the ISO C++ standard, section 5.8. In practice this means a compiler will emit either an arithmetic or logical right shift, and the compiler docs should say which it is. In this code, the kind of shift doesn't seem to matter for computation of `msbX` and `msbY`, I tested with both kind of shifts. C++ has no way of portably forcing an arithmetic shift, but it can be simulated where required (requiring a cast to unsigned type), e.g. see my answer [here](http://stackoverflow.com/a/31883971/780717) – njuffa Feb 01 '17 at 17:20
0

The asker has self-answered their question (a class assignment), so providing alternative solutions seems appropriate at this time. The question clearly assumes that integers are represented as two's complement numbers.

One approach is to consider how CPUs compute predicates for conditional branching by means of a compare instruction. "signed less than" as expressed in processor condition codes is SF ≠ OF. SF is the sign flag, a copy of the sign-bit, or most significant bit (MSB) of the result. OF is the overflow flag which indicates overflow in signed integer operations. This is computed as the XOR of the carry-in and the carry-out of the sign-bit or MSB. With two's complement arithmetic, a - b = a + ~b + 1, and therefore a < b = a + ~b < 0. It remains to separate computation on the sign bit (MSB) sufficiently from the lower order bits. This leads to the following code:

int isLessOrEqual (int a, int b)
{
    int nb = ~b;
    int ma = a  & ((1U << (sizeof(a) * CHAR_BIT - 1)) - 1);
    int mb = nb & ((1U << (sizeof(b) * CHAR_BIT - 1)) - 1);
    // for the following, only the MSB is of interest, other bits are don't care
    int cyin = ma + mb;
    int ovfl = (a ^ cyin) & (a ^ b);
    int sign = (a ^ nb ^ cyin);
    int lteq = sign ^ ovfl;
    // desired predicate is now in the MSB (sign bit) of lteq, extract it
    return (int)((unsigned int)lteq >> (sizeof(lteq) * CHAR_BIT - 1));
}

The casting to unsigned int prior to the final right shift is necessary because right-shifting of signed integers with negative value is implementation-defined, per the ISO-C++ standard, section 5.8. Asker has pointed out that casts are not allowed. When right shifting signed integers, C++ compilers will generate either a logical right shift instruction, or an arithmetic right shift instruction. As we are only interested in extracting the MSB, we can isolate ourselves from the choice by shifting then masking out all other bits besides the LSB, at the cost of one additional operation:

    return (lteq >> (sizeof(lteq) * CHAR_BIT - 1)) & 1;

The above solution requires a total of eleven or twelve basic operations. A significantly more efficient solution is based on the 1972 MIT HAKMEM memo, which contains the following observation:

ITEM 23 (Schroeppel): (A AND B) + (A OR B) = A + B = (A XOR B) + 2 (A AND B).

This is straightforward, as A AND B represent the carry bits, and A XOR B represent the sum bits. In a newsgroup posting to comp.arch.arithmetic on February 11, 2000, Peter L. Montgomery provided the following extension:

If XOR is available, then this can be used to average two unsigned variables A and B when the sum might overflow:

 (A+B)/2 = (A AND B) + (A XOR B)/2

In the context of this question, this allows us to compute (a + ~b) / 2 without overflow, then inspect the sign bit to see if the result is less than zero. While Montgomery only referred to unsigned integers, the extension to signed integers is straightforward by use of an arithmetic right shift, keeping in mind that right shifting is an integer division which rounds towards negative infinity, rather than towards zero as regular integer division.

int isLessOrEqual (int a, int b)
{
    int nb = ~b;
    // compute avg(a,~b) without overflow, rounding towards -INF; lteq(a,b) = SF
    int lteq = (a & nb) + arithmetic_right_shift (a ^ nb, 1);
    return (int)((unsigned int)lteq >> (sizeof(lteq) * CHAR_BIT - 1));
}

Unfortunately, C++ itself provides no portable way to code an arithmetic right shift, but we can emulate it fairly efficiently using this answer:

int arithmetic_right_shift (int a, int s)
{
    unsigned int mask_msb = 1U << (sizeof(mask_msb) * CHAR_BIT - 1);
    unsigned int ua = a;
    ua = ua >> s;
    mask_msb = mask_msb >> s;
    return (int)((ua ^ mask_msb) - mask_msb);
}

When inlined, this adds just a couple of instructions to the code when the shift count is a compile-time constant. If the compiler documentation indicates that the implementation-defined handling of signed integers of negative value is accomplished via arithmetic right shift instruction, it is safe to simplify to this six-operation solution:

int isLessOrEqual (int a, int b)
{
    int nb = ~b;
    // compute avg(a,~b) without overflow, rounding towards -INF; lteq(a,b) = SF
    int lteq = (a & nb) + ((a ^ nb) >> 1);
    return (int)((unsigned int)lteq >> (sizeof(lteq) * CHAR_BIT - 1));
}

The previously made comments regarding use of a cast when converting the sign bit into a predicate apply here as well.

Community
  • 1
  • 1
njuffa
  • 23,970
  • 4
  • 78
  • 130