9

Hi all I was wondering if there is a way to implement this method without casting to a wider data type (e.g. long, double, etc)?

CanTimes(int a, int b){
    returns true if a * b is within the range of -2^31 to 2^31-1, else false;
}

For example, we could implement one for the method CanAdd (without casts) as such:

    public static boolean CanPlus(int a, int b) {
        if (b >= 0) {
            return a <= Integer.MAX_VALUE - b
        } else {
            return a >= Integer.MIN_VALUE - b
        }
    }

Implementation language is Java, though of course this is more of a language-agnostic problem.

I was thinking if there's some logic we can employ to decide if a * b fits the range of an integer without casting it to a wider data type?

Solution ! based on Strelok's comment:

public static boolean CanTimes(int a, int b) {
    if (a == 0 || b == 0) {
        return true;
    }
    if (a > 0) {
        if (b > 0) {
            return a <= Integer.MAX_VALUE / b;
        } else {
            return a <= Integer.MIN_VALUE / b;
        }
    } else {
        if (b > 0) {
            return b <= Integer.MIN_VALUE / a;
        } else {
            return a <= -Integer.MAX_VALUE / b;
        }
    }
}
Pacerier
  • 86,231
  • 106
  • 366
  • 634
  • Why? Why the artificial restriction? – user207421 Dec 05 '11 at 06:07
  • Maybe you can do something with `Integer.numberOfLeadingZeros`. If the sum of both leading zeros is greater than 31 it will still be an int, or something like this. – Thilo Dec 05 '11 at 06:10
  • @EJP It's not an artificial restriction, I'm wondering if there's a way to do it without having to cast to a wider data type. For example, HeadGeek has a half-solution here http://stackoverflow.com/a/199455/632951. His is an estimate though I was interested in how we could improve it such that it actually works. – Pacerier Dec 05 '11 at 06:13
  • 4
    A small amount of Googling turned up this: http://www.java2s.com/Tutorial/Java/0040__Data-Type/Multiplytwolongintegerscheckingforoverflow.htm which is for longs, but obviously it is only seconds from being adapted to ints. – Strelok Dec 05 '11 at 06:18
  • @Strelok Wow good stuff! sometimes the simple things are the hard ones to think about. – Pacerier Dec 05 '11 at 06:43
  • @Pacerier OK - I've posted a simple, one-line and precise solution for you - see my answer – Bohemian Dec 05 '11 at 23:13

4 Answers4

1

You can do the multiplication and then check whether dividing by one factor still gives the other.

EDIT

The above doesn't work all the time, as Dietrich Epp points out; it fails for -1 and Integer.MIN_VALUE. I don't know if there are any other edge cases. If not, then it would be easy to check for this one case.

Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
  • Interesting. Does this work reliably? Or are there edge cases that result in false positives? – Thilo Dec 05 '11 at 06:05
  • The algorithm will fail for `a=-1`, `b=-0x80000000`. `a*b=-0x80000000` (overflow), but `(a*b)/a == b` because overflow happens during the check as well. – Dietrich Epp Dec 05 '11 at 06:18
  • 1
    @Thilo - I thought it was reliable, but a little example (if I did it right) makes me think it isn't. In 2-bit, 2's complement arithmetic, 11 * 10 (i.e., -1 * -2) gives 10 (which is wrong). But 10 / 11 gives 10, so it would pass the test. A better test would be to divide the largest (magnitude) number allowed by one factor and test that the result is less (in magnitude) than the other factor. Separate tests are needed for each sign combination, I think. – Ted Hopp Dec 05 '11 at 06:27
1

As per my comment, here is the adapted version, with some unit tests:

public static int mulAndCheck( int a, int b )
{
    int ret;
    String msg = "overflow: multiply";
    if ( a > b )
    {
        // use symmetry to reduce boundry cases
        ret = mulAndCheck( b, a );
    }
    else
    {
        if ( a < 0 )
        {
            if ( b < 0 )
            {
                // check for positive overflow with negative a, negative b
                if ( a >= Integer.MAX_VALUE / b )
                {
                    ret = a * b;
                }
                else
                {
                    throw new ArithmeticException( msg );
                }
            }
            else if ( b > 0 )
            {
                // check for negative overflow with negative a, positive b
                if ( Integer.MIN_VALUE / b <= a )
                {
                    ret = a * b;
                }
                else
                {
                    throw new ArithmeticException( msg );

                }
            }
            else
            {
                // assert b == 0
                ret = 0;
            }
        }
        else if ( a > 0 )
        {
            // assert a > 0
            // assert b > 0

            // check for positive overflow with positive a, positive b
            if ( a <= Integer.MAX_VALUE / b )
            {
                ret = a * b;
            }
            else
            {
                throw new ArithmeticException( msg );
            }
        }
        else
        {
            // assert a == 0
            ret = 0;
        }
    }
    return ret;
}

@Test( expected = ArithmeticException.class )
public void testOverflow()
{
    mulAndCheck( Integer.MAX_VALUE, Integer.MAX_VALUE );
}

@Test( expected = ArithmeticException.class )
public void testOverflow1()
{
    mulAndCheck( Integer.MIN_VALUE, Integer.MAX_VALUE );
}

@Test
public void testTimesMinus1()
{
    Assert.assertEquals( Integer.MIN_VALUE + 1, mulAndCheck( Integer.MAX_VALUE, -1 ) );
    Assert.assertEquals( Integer.MAX_VALUE, mulAndCheck( Integer.MIN_VALUE + 1, -1 ) );
}
Strelok
  • 50,229
  • 9
  • 102
  • 115
  • This is blatant [plagiarism](http://dictionary.reference.com/browse/plagiarism) from [this post](http://www.java2s.com/Tutorial/Java/0040__Data-Type/Multiplytwolongintegerscheckingforoverflow.htm)! – Bohemian Dec 05 '11 at 23:11
  • @Bohemian I'm pretty sure I posted where this is adapted from in my comment to the OP: `"A small amount of Googling turned up this: java2s.com/Tutorial/Java/0040__Data-Type/… which is for longs, but obviously it is only seconds from being adapted to ints."` Anyway, you are not adding anything valuable to the discussion here. – Strelok Dec 05 '11 at 23:22
1

Since the multiplication of a*b is the same as a+a+a+... repeated b times (and vice-versa), you can do something like this:

(I renamed your CanMultiple() function to isIntMultiplication(), since I think its more clear )

public boolean isIntMultiplication(int a, int b) {
    // signs are not important in this context
    a = Math.abs(a);
    b = Math.abs(b);
    // optimization: I want to calculate a*b as the sum of a by itself repeated b times, so make sure b is the smaller one
    // i.e., 100*2 is calculated as 100+100 which is faster than summing 2+2+2+... a hundred times
    if (b > a) { int swap = a; a = b; b = swap; }

    int n = 0, total = a;
    while(++n < b) {
        if (total <= Integer.MAX_VALUE - a) {
            total += a;
        } else {
            return false;
        }
    }
    return true;
}

You see it in action:

// returns true, Integer.MAX_VALUE * 1 is still an int    
isIntMultiplication(Integer.MAX_VALUE, 1);

// returns false, Integer.MAX_VALUE * 2 is a long    
isIntMultiplication(Integer.MAX_VALUE, 2);

// returns true, Integer.MAX_VALUE/2 * 2 is still an int
isIntMultiplication(Integer.MAX_VALUE/2, 2);

// returns false, Integer.MAX_VALUE * Integer.MAX_VALUE is a long
isIntMultiplication(Integer.MAX_VALUE, Integer.MAX_VALUE);

This solution does not use long types, as required.

Jose Rui Santos
  • 15,009
  • 9
  • 58
  • 71
  • Signs are important because -Integer.MIN_VALUE > Integer.MAX_VALUE (mathematically). – Ted Hopp Dec 05 '11 at 07:05
  • Hmm, the approach works (although the sign does need sorting out), but I think 64k additions in the worst case can probably be beaten for performance... – Steve Jessop Dec 05 '11 at 10:22
  • I like the idea of swapping the left and right choosing the smaller one to loop on, though it's still pretty slow. – Pacerier Dec 05 '11 at 15:57
  • @Pacerier: could be worse, the check for addition overflow could be implemented as repeated `++total`, with a check that `total != Integer.MAX_VALUE` ;-) – Steve Jessop Dec 06 '11 at 10:39
-1

Mathematically, the sum of the log-base-2 should be less than 232. Unfortunately, Math doesn't give us log base 2, but this is still simple enough:

static boolean canMultiply(int a, int b) {
    return Math.log(Math.abs(a)) + Math.log(Math.abs(b)) <= Math.log(Integer.MAX_VALUE);
}

EDITED: Due to (fair) flak, how about this simple approach that addresses OP's question exactly?

static boolean canMultiply(int a, int b) {
    return a == 0 || ((a * b) / a) == b;
}

If there's an overflow, dividing by the original number won't bring us back to starting number.
Importantly, this will work for longs, which can't be cast up.

Bohemian
  • 412,405
  • 93
  • 575
  • 722
  • This doesn't work all the time. If the (mathematical) product is negative, the limit to check against is `-Integer.MIN_VALUE`. – Ted Hopp Dec 05 '11 at 07:03
  • @TedHopp Yes - there is an edge case of the product being *exactly* `Integer.MIN_VALUE` not being covered, but `log` is not that precise anyway. If you can live with this edge case, this solution would be acceptable. (The use of `Math.abs()` makes negative results still correctly checked) – Bohemian Dec 05 '11 at 07:09
  • I don't think "`log` is not that precise anyway" is much of a defense of `Integer.MIN_VALUE` not being covered. You're just saying "yes, OK, there's an input it gets wrong, but then there are other inputs it gets wrong anyway". What if the questioner wants the solution to be correct? – Steve Jessop Dec 05 '11 at 10:10
  • Btw is it guaranteed that if there's an overflow, dividing by the original number wouldn't bring us back to the starting number regardless of the inputs? – Pacerier Dec 07 '11 at 01:46
  • @Pacerier - It is not guaranteed. Try `-1` and `Integer.MIN_VALUE` for `a` and `b`. – Ted Hopp Dec 07 '11 at 02:26
  • @TedHopp Actually, it *is* guaranteed. You *can* multiply `-1` and `Integer.MIN_VALUE`. The fact that the result is `Integer.MIN_VALUE` (a quirk of java's 2's-compliment implementation) is irrelevant. You can still do it and technically it's not an overflow. Kudos for thinking of that peculiarity tho! – Bohemian Dec 07 '11 at 05:24
  • @Bohemian Why is that not an overflow? Multiplying two negative numbers should not produce a negative number unless there's overflow. It's trying to represent 2^31 and it's too large to fit into an `int`. – Ted Hopp Dec 07 '11 at 05:36
  • @TedHopp Why is this not an overflow? Glad you asked... Because `Math.abs(Integer.MIN_VALUE)` returns `Integer.MIN_VALUE`! Like I said, it's a quirk. In a way I don't disagree with you, but that's just the way java works... it's technically *not* an overflow because there is no corresponding positive equal for `Integer.MIN_VALUE`. There are 2^32 values for ints, but *one* value is used for zero, so that leaves an odd number to share between positive and negative numbers. Using 2's compliment means the logical choice is that there's one more negative number than positive numbers. – Bohemian Dec 07 '11 at 07:38