3

I came across this: Simplest way to check if two integers have same sign?

How can this be extended to more than two numbers (not necessarily integers)? Say, check if 4 numbers have the same sign (+ve or -ve).

I don't want to use bit operations as far as possible ... only logical conditions.

Thanks.

Community
  • 1
  • 1

6 Answers6

7

Let's say numbers are passed into a vector<int>.

Iterate through the array, check if the two consequent number have the same sign, if not return false at the first non-equal-sign couple.

Federico klez Culloca
  • 26,308
  • 17
  • 56
  • 95
2

Something like this?

if ( n1 > 0 && n2 > 0 ... && nN > 0 )
  // all positives
else if ( n1 < 0 && n2 < 0 ... && nN < 0 )
  // all negatives
else
  // mixed signs

Due to short-circuit evaluation it will be fast.

Nick Dandoulakis
  • 42,588
  • 16
  • 104
  • 136
  • What if n1..nN-1 all equal to 0, and nN = -1 ? – Kirill V. Lyadvinsky Aug 16 '09 at 18:31
  • Please, add part that will check all negatives? `if ( n1 <= 0 && .. nN <= 0 )` ? For the sample above it will take N comparisons. Not so fast. – Kirill V. Lyadvinsky Aug 16 '09 at 18:35
  • it'll take N comparisons if all numbers have the same sign. You have to check all numbers, in the worst case though. – Nick Dandoulakis Aug 16 '09 at 18:46
  • zero is neither a negative or a positive number – Nick Dandoulakis Aug 16 '09 at 18:52
  • 1
    But zero has a sign (at least, in C-speak it does). OP will have to decide how zeros should be treated. To handle 2's complement it's easy enough either to change > to >=, or to add another clause with == 0, if required. If you want to treat +0 and -0 differently on 1's complement or sign-mag implementations, then I think OP will just have to use bitwise ops, despite stated desire not to. – Steve Jessop Aug 16 '09 at 19:02
2

Unless I misunderstand the question, the following should do the job for a vector of numbers:

#include <iostream>
#include <vector>
using namespace std;

template <typename T> int Sign( T t ) {
    return t < 0 ? -1 : 1;
}

template <typename T> bool SameSign( std::vector <T> & v ) {
    if ( v.size() == 0 )  {
        throw "invalid size";
    }
    int sign = Sign( v[0] );
    for ( int i = 1; i < v.size(); i++ ) {
        if ( Sign( v[i]) != sign ) {
            return false;
        }
    }
    return true;
}


int main() {
    std::vector <int> v;
    v.push_back(1);
    v.push_back(1);
    v.push_back(-1);
    bool same = SameSign( v );
    std::cout << (same ? "same" : "not same") <<"\n";
}
2

This is a generic implementation. This will check if the elements of a container all share the same property.

One version returns an iterator to the first mismatched element, the concise version only returns true or false.

Because it's generic, you can also check if they are all even numbers, for example:

// shared_property.hpp
#ifndef SHARED_PROPERTY_HPP
#define SHARED_PROPERTY_HPP

// checks if all elements share a single property
// if one element does not share a property,
// it returns an iterator to that element
template <typename Iter, typename Func>
Iter share_property(Iter first, Iter last, Func func)
{
    for(; first != last; ++first)
    {
        if (!func(*first))
        {
            // differs
            break;
        }
    }

    return first;
}

// only indicates if all elements share a property
template <typename Iter, typename Func>
bool share_property_concise(Iter first, Iter last, Func func)
{
    return share_property(first, last, func) == last;
}

#endif

Example program:

#include "shared_property.hpp"

#include <functional>
#include <iostream>
#include <vector>

typedef std::vector<int> container;

void negative_test(void)
{
    container negatives;
    negatives.push_back(-1);
    negatives.push_back(-2);
    negatives.push_back(-3);

    if (share_property_concise(negatives.begin(), negatives.end(),
        std::bind2nd(std::less<int>(), 0)))
    {
        std::cout << "All elements are less than 0." << std::endl;
    }
    else
    {
        std::cout << "Not all elements are less than 0." << std::endl;
    }
}

bool is_even(int i)
{
    return i % 2 == 0;
}

void even_test(void)
{
    container evens;
    evens.push_back(2);
    evens.push_back(4);
    evens.push_back(10);

    if (share_property_concise(evens.begin(), evens.end(),
        is_even))
    {
        std::cout << "All elements are even." << std::endl;
    }
    else
    {
        std::cout << "Not all elements are even." << std::endl;
    }
}


int main(void)
{
    negative_test();    
    even_test();
}

It also works well for filtering containers based off a property:

#include "shared_property.hpp"

#include <algorithm>
#include <cstdlib>
#include <ctime>
#include <functional>
#include <iostream>
#include <iterator>
#include <vector>

typedef std::vector<int> container;

template <typename Iter, typename OutIter, typename Func>
Func filter_container(Iter first, Iter last, OutIter out, Func func)
{
    first = share_property(first, last, func);
    while (first != last)
    {
        *out++ = *first++;

        first = share_property(first, last, func);
    }

    return func;
};

int make_number(void)
{
    return std::rand() % 20 - 10;
}

void find_negatives(void)
{
    container numbers;
    std::generate_n(std::back_inserter(numbers), 20, make_number);

    container negatives;
    filter_container(numbers.begin(), numbers.end(), std::back_inserter(negatives),
        std::bind2nd(std::greater<int>(), 0));

    std::cout << "List: " << std::endl;
    std::copy(numbers.begin(), numbers.end(),
        std::ostream_iterator<int>(std::cout, "\n"));
    std::cout << std::endl;

    std::cout << "Negatives:" << std::endl;
    std::copy(negatives.begin(), negatives.end(),
        std::ostream_iterator<int>(std::cout, "\n"));
}

int main(void)
{
    std::srand(static_cast<unsigned>(std::time(0)));

    find_negatives();   
}

Consistency

This algorithm will test a set for consistency:

// consistent_property.hpp
#ifndef CONSISTENT_PROPERTY_HPP
#define CONSISTENT_PROPERTY_HPP

#include <boost/logic/tribool.hpp>

// checks if all elements consistently pass/fail a property
// if it returns indeterminate, then the results are mixed,
// otherwise all results pass or failed as indicated
template <typename Iter, typename Func>
boost::logic::tribool consistent_property(Iter first, Iter last, Func func)
{
    bool result = func(*first++);

    for(; first != last; ++first)
    {
        if (func(*first) != result)
        {
            // differs
            return boost::logic::indeterminate;
        }
    }

    return result;
}

#endif

This will check if a property is consistent throughout a container, not if it just holds true. That is, it will return false if all the elements do not pass the property, true if they all do (same result as share_property_concise), and indeterminate if the result is mixed.

Here is an example:

#include "consistent_property.hpp"

#include <algorithm>
#include <functional>
#include <iostream>
#include <vector>

typedef std::vector<int> container;

void check_sign(void)
{
    container positives;
    positives.push_back(1);
    positives.push_back(2);
    positives.push_back(3);

    boost::logic::tribool result =
        consistent_property(positives.begin(), positives.end(),
        std::bind2nd(std::greater<int>(), 0));

    if (boost::logic::indeterminate(result))
    {
        std::cout << "Mixed signs." << std::endl;
    }
    else if (result)
    {
        std::cout << "All positive." << std::endl;
    }
    else
    {
        std::cout << "All negative." << std::endl;
    }
}

int main(void)
{
    check_sign();
}

Lastly, I provide a functor that can get a less specific result to consistency. While the above will tell if the consistency failed, passed, or was mixed, using this functor with share_property will only determine if the set is consistent, or mixed. You cannot deduce in which way it was consistent.

This is probably the simplest solution to the original problem. To have the same sign, without the need to know what that sign is, can be done this way, giving a quick & simple result; either they are all the same sign or not.

Add the following to consistent_property.hpp:

// the functor to determine if the property that all elements
// in a set share, is the property that they all pass or fail
// a secondary property. there is no way to deduce what the
// status was (if they all passed or all failed), only if
// all the elements were consistent. true indicates consistency,
// false indicates lack thereof
template <typename T, typename Func>
class consistent_shared_property_binder
{
public:
    consistent_shared_property_binder(const Func& func) :
    _func(func)
    {
    }

    bool operator()(const T& t)
    {
        bool result = _func(t);

        static bool status = result; // only initialized first run

        return status == result;
    }

private:
    Func _func;
};

// bind a function to the functor
template <typename T, typename Func>
consistent_shared_property_binder<T, Func> consistent_property(Func func)
{
    return consistent_shared_property_binder<T, Func>(func);
}

And the example program:

#include "consistent_property.hpp"
#include "shared_property.hpp"

#include <algorithm>
#include <functional>
#include <iostream>
#include <vector>

typedef std::vector<int> container;

bool is_positive(int i)
{
    return i > 0; 
}

void check_sign_anonymous(void)
{
    container positives;
    positives.push_back(-1);
    positives.push_back(-2);
    positives.push_back(-3);

    if (share_property_concise(positives.begin(), positives.end(),
        consistent_property<int>(is_positive)))
    {
        std::cout << "All the same sign." << std::endl;
    }
    else
    {
        std::cout << "Mixed signs." << std::endl;
    }
}

int main(void)
{
    check_sign_anonymous();
}

And I think I'm done. :P

(Note: If anyone sees a better way to do something, or a bug, please tell me!)

Community
  • 1
  • 1
GManNickG
  • 494,350
  • 52
  • 494
  • 543
1

signof() will return -1 for any negative, 0 for 0 and 1 for any positive number.

Edit:

In case of signof() or signbit() are not present, you may try using a bitwise operation to determine that. (number >> 31) shall return 1 for negative 32-bits integers.

Havenard
  • 27,022
  • 5
  • 36
  • 62
  • AFAIK, `signof()` is not part of C++. Are you thinking of `signbit()`? If so, this converts from integer to a single-precision before checking the bit. It would probably be faster to use boolean comparisons. – D.Shawley Aug 16 '09 at 18:31
  • Can you provide a reference for this as a part of the language? I've never heard of it. Is it part of the forthcoming standard? – Steve Fallows Aug 16 '09 at 18:31
  • No such function in sStandard C++. –  Aug 16 '09 at 18:33
  • Oops... `signbit()` was added to C99 and doesn't seem to be in the current C++ standard. IIRC, it will be in the C++0x. – D.Shawley Aug 16 '09 at 18:36
  • http://www.opengroup.org/onlinepubs/000095399/functions/signbit.html, but signbit returns 0 for positive and non-zero for negative (including negative 0). – ysth Aug 16 '09 at 18:40
  • Theres another way, (n >> 31) will return 1 if its negative. – Havenard Aug 16 '09 at 19:01
  • Assuming an int is 32 bits wide. – GManNickG Aug 16 '09 at 22:41
1

Here's an example for multiple values. You just need make a connected graph of same sign comparisons from the two-value case:

(a<0) == (b<0) && (b<0) == (c<0) && (c<0) == (d<0) && ...

By connected, I mean "a" is checked with "b", then "b" with "c", etc.

Inverse
  • 4,408
  • 2
  • 26
  • 35