在 C / C ++ 中是否有一种良好而快速的方法来测试多个变量是包含所有正值还是所有负值?
说要测试5个变量:
变体1
int test(int a[5]) {
if (a[0] < 0 && a[1] < 0 && a[2] < 0 && a[3] < 0 && a[4] < 0) {
return -1;
} else if (a[0] > 0 && a[1] > 0 && a[2] > 0 && a[3] > 0 && a[4] > 0) {
return 1;
} else {
return 0;
}
}
变体2
int test(int a[5]) {
unsigned int mask = 0;
mask |= (a[0] >> numeric_limits<int>::digits) << 1;
mask |= (a[1] >> numeric_limits<int>::digits) << 2;
mask |= (a[2] >> numeric_limits<int>::digits) << 3;
mask |= (a[3] >> numeric_limits<int>::digits) << 4;
mask |= (a[4] >> numeric_limits<int>::digits) << 5;
if (mask == 0) {
return 1;
} else if (mask == (1 << 5) - 1) {
return -1;
} else {
return 0;
}
}
变体2a
int test(int a[5]) {
unsigned int mask = 0;
for (int i = 0; i < 5; i++) {
mask <<= 1;
mask |= a[i] >> numeric_limits<int>::digits;
}
if (mask == 0) {
return 1;
} else if (mask == (1 << 5) - 1) {
return -1;
} else {
return 0;
}
}
我更喜欢哪个版本?使用变体2 / 2a超过1是否有任何优势?还是有更好/更快/更清洁的方式?
答案 0 :(得分:8)
我认为你的问题以及你所寻找的不同意。您询问如何检测它们是否已签名或未签名,但看起来您的意思是如何测试它们是正面还是负面。
对所有否定的快速测试:
if ((a[0]&a[1]&a[2]&a[3]&a[4])<0)
和所有非负(&gt; = 0):
if ((a[0]|a[1]|a[2]|a[3]|a[4])>=0)
我想不出一个好方法来测试他们所有严格正(非零),但应该有一个。
请注意,这些测试对于二进制补码系统(您需要关注的现实世界中的任何内容)都是正确且可移植的,但对于补码或符号幅度而言,它们略有错误。如果你真的在乎,它们可能会被修复。
答案 1 :(得分:5)
我猜你的意思是消极/积极,(un)签名意味着是否存在符号。这适用于任何可迭代的(假设您将0
视为正数):
template <class T>
bool allpos(const T start, const T end) {
T it;
for (it = start; it != end; it++) {
if (*it < 0) return false;
}
return true;
}
// usage
int a[5] = {-5, 3, 1, 0, 4};
bool ispos = allpos(a, a + 5);
这可能不是绝对极其超级健康的方式,但它确实可读且非常快。优化它是不值得的。
答案 2 :(得分:4)
变体1是唯一可读的。
但是,你可以使用循环更好:
int test(int *a, int n) {
int neg = 0;
for(int i = 0; i < n; i++) {
if(a[i] < 0) neg++;
}
if(neg == 0) return 1;
else if(neg == n) return -1;
else return 0;
}
答案 3 :(得分:1)
我同意以前的海报,循环更简单。以下解决方案结合了Nightcracker的模板和ThiefMaster的完整解决方案,如果在循环变量(早期外出)时检测到符号更改,则提前结束。它适用于浮点值。
template<typename T>
int testConsistentSigns(const T* i_begin, const T* i_end)
{
bool is_positive = !(*i_begin < 0);
for(const T* it = i_begin + 1; it < i_end; ++it)
{
if((*it < 0) && is_positive)
return 0;
}
if(is_positive)
return 1;
return -1;
}
答案 4 :(得分:0)
就速度而言,我建议您轮流分析每个示例,以发现哪个是您特定平台上最快的。
就易于理解而言,我会说第一个例子是最明显的,尽管这只是我的看法。
当然,如果您有超过5个变量,第一个版本是维护噩梦。第二个和第三个变体对此更好,但显然有32个变量的限制。为了使它们完全灵活,我建议在循环中保持计数器的正负变量数。在循环结束后,只需检查一个或其他计数器是否为零。
答案 5 :(得分:0)
首先,创建一个方法\过程。这将大大提高可读性(无论你如何实现它,它都会比上面的所有选项更清晰。)
其次,我认为功能:
bool isNeg(int x) { return x < 0;}
然后使用位掩码清理器,所以我将使用选项1,当涉及到速度时,让编译器在这种低级别情况下为你工作。
最终代码应如下所示:
int test(int a[5]) {
bool allNeg = true;
bool allPos = true;
for (i = 0; i < 5; i++){
if (isNeg(a[i]) allPos = false;
if (isPos(a[i]) allNeg = false;
}
if (allNeg) return -1;
if (allPos) return 1;
return 0;
}
答案 6 :(得分:0)
你可以找到最大元素,如果它是负数,那么所有元素都是负数:
template<typename T>
bool all_negative( const T* first, const T* last )
{
const T* max_el = std::max_element( first, last );
if ( *max_el < T(0) ) return true;
else return false;
}
您可以使用boost::minmax_element
在一个循环中查找所有元素是否为负数:
template<typename T>
int positive_negative( const T* first, const T* last )
{
std::pair<const T*,const T*> min_max_el = boost::minmax_element( first, last );
if ( *min_max_el.second < T(0) ) return -1;
else if ( *min_max_el.first > T(0) ) return 1;
else return 0;
}
如果序列非空,则函数minmax_element
最多执行3 * (last - first - 1) / 2
次比较。
答案 7 :(得分:0)
如果您只需要一次知道少于/大于零,或者可以满足<
和>=
,那么您可以使用find_if
轻松完成这样的操作:< / p>
#include <iostream>
template <class Iter>
int all_neg(Iter begin, Iter end)
{
return std::find_if(begin, end, std::bind2nd(std::greater_equal<int>(), 0)) == end;
}
int main()
{
int a1[5] = { 1, 2, 3, 4, 5 };
int a2[5] = { -1, 2, 3, 4, 5 };
int a3[5] = { -1, -2, -3, -4, -5 };
int a4[5] = { 0 };
std::cout << all_neg(a1, a1 + 5) << ":"
<< all_neg(a2, a2 + 5) << ":"
<< all_neg(a3, a3 + 5) << ":"
<< all_neg(a4, a4 + 5) << std::endl;
}
如果您确实需要这样的详细程度,您还可以使用更复杂的谓词来跟踪任何pos / neg以回答原始问题。