除了以下两个方法之外,还有一种更优雅的方法来检查两个数字是否具有相同的符号吗?例如,检查正确的情况,最后返回false。)

通过设计,两个整数永远不会包含零值。

评论

如果根据设计,这两个整数永远不会包含零值,那么是否应该对此进行检查/例外(以确保这种情况永远不会发生)?

这是在功能之前检查的

然后应该在函数中声明它。

从这个问题和(一些已删除的)答案中可以汲取的教训是,尝试通过清除,调试,工作的代码并应用一些怪异的技巧使代码“更优雅”,通常会引入一个错误。尽管有一些小方法可以改进您的代码,但是它可以工作,并且读者很清楚。下面的许多尝试使用代数或表示属性的尝试都是奥秘的,错误的,或者至少很难看出它们是正确的。我会坚持使用您拥有的代码。

我认为在if语句后使用不带大括号的换行符可能存在危险,因为有人可以稍后再引入第二行代码,该代码将始终被执行,但看起来像是有条件的。这就是苹果著名的“ goto fail”错误发生的原因。

#1 楼

首先,您的代码有错误。如果数字之一为0怎么办? 0应该为正,但是您的代码在某些测试中将其视为负,而在其他测试中则将其视为正。与0进行比较时,您应该使用>=,而不仅仅是>。但我建议使用单个return语句。如果您检查“相同符号”而不是“相反符号”,则单个返回语句会更容易。

接受代码:


bool sameSign(int num1, int num2)
{
    if (num1 > 0 && num2 < 0)
        return false;
    if (num1 < 0 && num2 > 0)
        return false;
    return true;
}



修正0处理,您将拥有:

bool sameSign(int num1, int num2)
{
    return num1 >= 0 && num2 >= 0 || num1 < 0 && num2 < 0
}


现在,这很好,我很乐意在代码审查中“通过”,但是,您可以做些技巧吗?

最简单的方法(使用代码)是使用XOR:

return (num1 ^ num2) >= 0


比较位,如果位相同,则将结果位设置为0。位相同,结果符号位为0,因此为正(或0)值。

评论


\ $ \ begingroup \ $
这是我从未想过的XOR用法。
\ $ \ endgroup \ $
–龙胆K
15年10月15日在11:18

\ $ \ begingroup \ $
请注意,XOR技巧仅适用于整数类型变量。如果您想要一个适用于float或double的版本,则可以使用其他版本之一。
\ $ \ endgroup \ $
–达雷尔·霍夫曼(Darrel Hoffman)
2015年10月15日14:03



\ $ \ begingroup \ $
为什么0应该为正?没有理由将0视为正数。从数学上讲,0既不是正数也不是负数。
\ $ \ endgroup \ $
–贾斯汀
2015年10月15日14:33

\ $ \ begingroup \ $
@Justin-请注意,要求是检查“相同的符号”,而不是“正”或“负”。碰巧,尽管有任何数学应用/理论,二进制0的符号与正整数相同。
\ $ \ endgroup \ $
–rolfl
2015年10月15日14:36

\ $ \ begingroup \ $
我不是想用二进制整数,而是整数。我将其视为num1 == 0 && num2 == 0 || num1 <0 && num2 <0 || num1> 0 && num2>0。我想这取决于解释。
\ $ \ endgroup \ $
–贾斯汀
2015年10月15日14:39

#2 楼

为什么不比较布尔值本身呢?

return ((num1 < 0) == (num2 < 0));


这将零视为“正数”。对于更严格的解释,认为零既不是正也不是负,请考虑:


评论


\ $ \ begingroup \ $
这也是一个很好的方法
\ $ \ endgroup \ $
–约翰·德米特里(John Demetriou)
2015年10月15日上午10:39

\ $ \ begingroup \ $
这让我投票。我希望它比XOR技巧效率稍低,但是它确切说明了它的意思,因此可读性更高。
\ $ \ endgroup \ $
–mc0e
15-10-16在15:23

\ $ \ begingroup \ $
@ mc0e,XOR也不适用于浮点数。因此,这是浮点情况下最有效的选择。
\ $ \ endgroup \ $
–此处some1
20 Dec 24 '14:28

#3 楼

是的,通过


在方法中添加可访问性修饰符,有一种更优雅的方法
使用PascalCase框命名方法
命名方法HasSameSign

使用Math.Sign()方法

private static bool HasSameSign(int num1, int num2)
{
    return Math.Sign(num1) == Math.Sign(num2);
}


评论


\ $ \ begingroup \ $
如果num1或num2为0,则Math.Sign()方法将产生不同的结果。您对“相同符号”的含义的解释较为传统,因此可以说原始代码有错误,或者至少是该方法名字不好。
\ $ \ endgroup \ $
– 200_success
15年10月15日在7:31

\ $ \ begingroup \ $
我的印象是,您和我的答案提供了OOP和FP之间的区别。
\ $ \ endgroup \ $
– kojiro
15-10-16在13:44

\ $ \ begingroup \ $
@ 200_success,如果您希望0为正或负,则可以调整代码以返回Math.Abs​​(Math.Sign(num1)-Math.Sign(num2))> 1。
\ $ \ endgroup \ $
–马修·斯蒂夫斯(Matthew Steeples)
2015年10月16日15:37



#4 楼

您可以向右移动,最后将为-1或零,分别为负和正。 (算术移位)
这将传播最高有效位(符号位),从而产生0xFFFFFFFF(-1)或0x00000000(0)。

return num1 >> 31 == num2 >> 31;


评论


\ $ \ begingroup \ $
请更多解释。如果他们都是积极的,我会得到什么?如果它们都是负面的,我将得到什么?还要解释为什么换班有效
\ $ \ endgroup \ $
– John Demetriou
2015年10月15日在7:20



\ $ \ begingroup \ $
感谢您的修改。提出了您的答案。但是另一个似乎更容易被他人理解,因此我接受了那个。虽然您与语言无关
\ $ \ endgroup \ $
– John Demetriou
15年10月15日在7:25

\ $ \ begingroup \ $
该技术将0当作正数对待,这不是我认为的预期行为,也不会重现原始代码的行为(可疑)。
\ $ \ endgroup \ $
– 200_success
15年10月15日在7:33

\ $ \ begingroup \ $
我喜欢这种方法,但是它确实依赖于int的基础实现,通常不应该依赖于它。例如,不能长时间直接使用。现在很明显,c#(最可能)将始终是System.Int32,但有些人可能还记得当引入vb7(vb.net)时有很多哭声和咬牙切齿的感觉,而2个字节的int突然变为4个字节! omg因此,在X年中,当C#128引入并且您的继任者的毕业女儿在不了解移位的情况下移植了您的任务关键代码时,当世界经济崩溃时不要来找我们...
\ $ \ endgroup \ $
–freedomn -m
15年10月16日在12:28

\ $ \ begingroup \ $
@freedomn -m可以通过使用sizeof(int)或通过在方法签名中显式使用System.Int32来防止(尽管由于int的大小是C#规范的一部分,所以它不会被更改,因此我更担心这种解决方案的安全性,并且希望具有更高的可读性)。
\ $ \ endgroup \ $
–萨拉
2015年10月16日15:57



#5 楼

缺少的是函数上方的注释,该注释确切指定了函数对零输入的作用:零值可能被(a)解释为正数,可能被(b)解释为无符号(具有相同的符号) (任意数字),或者(c)对于零输入,该函数的行为可能不确定。

需要将其记录下来,作为功能的一部分进行注释。如果这样做了,那么在(b)或(c)的情况下,您的代码将是一个很好的实现,而在(a)的情况下,它将有一个严重的错误。

现在,任何人都盲目地将数字相乘(存在严重的溢出风险,并且同样的问题处理零),或者转移技巧(并添加关于负数向右移位的假设,以及int = 32位):请勿在工作中尝试!尽可能多地在家中进行操作,但不要求可读性和代码质量。

#6 楼

可以将代码写成一行: 。

评论


\ $ \ begingroup \ $
shift方法将零视为正数。那么math.sign吗?
\ $ \ endgroup \ $
–约翰·德米特里(John Demetriou)
2015年10月15日在8:11

\ $ \ begingroup \ $
我认为那是一个错误。为了保留原始代码,还可以使用De Morgan定律,并且最终结果应为return(num1 <= 0 || num2> = 0)&&(num1> = 0 || num2 <= 0);
\ $ \ endgroup \ $
–龙胆K
2015年10月15日在8:51

\ $ \ begingroup \ $
@GentianKasa等效。两者都检查数字是正数还是负数。我的是“乘积之和”,即它会测试符号相等的两种情况。原始代码(和您的一行)是“和的乘积”,即它通过消除符号不相等的两种情况(一个正号和一个负号)来工作。
\ $ \ endgroup \ $
–秒杀
15-10-15在9:18



\ $ \ begingroup \ $
是的,你是对的。更好地看待它们是等效的。我的错
\ $ \ endgroup \ $
–龙胆K
2015年10月15日在9:59

#7 楼

使用简单数学的简单方法。如果两个数字具有相同的符号,则除法的结果符号将为正,如果两个数字相同则为负:

        bool sameSign(int num1, int num2)
        {
            num2 = num2==0 ? 1 : num2;
            return ((float)num1 / num2) > 0;
        }


第一行num2 = num2==0 ? 1 : num2;取决于零对您而言意味着:


零既不是正数也不是负数
或者零既是正数也不是负数
,或者您认为它可能更重要。 br />在这种情况下,+0零始终为正(情况3,-0)。您可以通过将num2 = num2==0 ? 1 : num2;切换为+0来将其更改为-0。 br />
最后,对于情况1,1,您可能会说它必须始终返回false,因为“某物(某些符号)等于什么(无符号)”始终为false。您可以通过切换除以原始-1且带有切换符号的num2 = num2==0 ? num1 : num2;符号(始终为1)来切换num2 = num2 == 0 ? (num1 == Int32.MinValue ? 1 : num1 * -1) : num2;符号。

请注意,在C#中,两个整数相除会返回整数(四舍五入)。通过强制浮动,结果将浮动。如果没有它,结果将是一个int值,如果num1足够大,则无论它是从负值还是向正值逼近,都将舍入为0。

评论


\ $ \ begingroup \ $
再说一次。不存在零值。对于其余部分,我认为这与乘法答案相似,只是视角不同。对?
\ $ \ endgroup \ $
–约翰·德米特里(John Demetriou)
2015年10月15日13:11

\ $ \ begingroup \ $
@JohnDemetriou:否。除法保留符号的方式与乘法保留符号的方式不同,因为乘法可能会溢出。
\ $ \ endgroup \ $
–埃里克·利珀特
2015年10月15日在16:14

\ $ \ begingroup \ $
@Zukki:您是否将-1乘以-1就不会改变符号的情况正确吗?
\ $ \ endgroup \ $
–埃里克·利珀特
2015年10月16日在1:45

\ $ \ begingroup \ $
@EricLippert是的,在这种情况下,num2的值设置为带有相反符号的num1的值。然后,除法为num1 /(带正号的num1),且始终小于0
\ $ \ endgroup \ $
–祖基
2015年10月16日13:13

\ $ \ begingroup \ $
我想你没有关注我。 MinValue是一个负数,当乘以-1时,仍然是一个负数。您建议的乘以-1的算法是否仍适用于该值?
\ $ \ endgroup \ $
–埃里克·利珀特
2015年10月16日13:38

#8 楼

 bool sameSign(int num1, int num2)
    {
        if (num1 > 0 && num2 < 0)
            return false;
        if (num1 < 0 && num2 > 0)
            return false;
        return true;
    }


第一行(原型)应该是(如果要检查双精度):点号)

第三和第五行是完全错误的(第三行以0num1为负,第五行以0作为num2为负)。 >
bool sameSign(long double num1, long double num2)




if (num1 >= 0 && num2 < 0)


以获得正确的结果。但我建议只返回1:

if (num1 < 0 && num2 >= 0)


,这样该函数就是:
一线:

return num1>=0&&num2>=0||num1<0&&num2<0


或两线:

bool sameSign(long double num1, long double num2)
{
    return num1>=0&&num2>=0||num1<0&&num2<0;
}


#9 楼

我想介绍另一种方法来检查两个数字是否具有相同的符号。

当然选择的方法是
方法代码在这里,下面是一个繁琐的解释。

(仅对于Int32;其他Int类型,应将数字31更改为减1的整数类型的位大小,并在警告Dmitry Rubanovich时添加)

return System.Convert.ToBoolean(~(num0>>31 ^ num1>>31) & 1);


利用以下两个事实,我们可以建立一个完整的按位检查(没有任何比较),以使符号相等。用于存储负数的二进制补码。以二进制形式构成最高有效位的数字
,如果数字为负,则为1;如果数字为正,则为0。我们可以使用以下代码段访问32位整数的最高有效位


num0>>31 


将其向右移动31位,忽略除最左边的一位以外的所有其他位(符号指示符位:-))

如果两个位不相等,则XOR(^)运算符返回1,如果两个位相等,则
返回0。如果在XOR
结果(称为XNOR)上使用“〜”(“ binary
的补码运算符”,它基本上会翻转位),并提取其最低有效位(使用逻辑“和”使用整数1可以解决问题)如果在C#中位相等,则得到1,如果位不相等,则得到0。
我们必须将int显式转换为布尔值,因此,
将两个上面的事实我们得到:


return System.Convert.ToBoolean(~(num0>>31 ^ num1>>31) & 1);


如果至少一个数字为零,按位代码将返回true。

评论


\ $ \ begingroup \ $
这个问题是一个典型的例子:“我们有100个开发人员,所以我们至少有200条意见” :-P,虽然很好
\ $ \ endgroup \ $
–约翰·德米特里(John Demetriou)
15年10月17日在17:40

\ $ \ begingroup \ $
您的回答还不够完美。如果您想要一个无论数字有多大都有效的东西,但确实依赖于2个int大小相同的事实,则返回(num0 ^ num1)> = 0;
\ $ \ endgroup \ $
–德米特里·鲁巴诺维奇(Dmitry Rubanovich)
2015年10月18日在9:21

\ $ \ begingroup \ $
@DmitryRubanovich我不是母语人士,您的意思是“还不够完美”,我个人建议(但不采取行动:-D)使用math.sign或可能的情况下使用库,请使用if来表示冗长,并且除非有必要,否则请不要依赖hack。正如我说的,答案只是对有兴趣的人们的逐字招数的展示。而且我相信您的放置方式通常更安全,因此更有用。仅当数据类型严格为32位并且函数调用非常庞大时,我的方法才有用。
\ $ \ endgroup \ $
– FazeL
15-10-18在10:41

\ $ \ begingroup \ $
@FazelL,使用固定位数的移位强制只考虑32位宽的整数。通过使用XOR而不是乘法,可以消除溢出的可能性。这使您的答案比以前的答案有所改善。但是,您的思想自然就会到​​达完成另一个按位操作的位置。如果改用常规比较,则会得到一个答案,该答案适用于相同位宽的任何两个整数。
\ $ \ endgroup \ $
–德米特里·鲁巴诺维奇(Dmitry Rubanovich)
2015年10月18日在19:05

\ $ \ begingroup \ $
这个答案甚至没有编译。在C#中,您不能将int隐式转换为bool。
\ $ \ endgroup \ $
– MAG
2015年10月19日在8:36

#10 楼

将两个数字相乘。如果结果的符号为正,则符号相同。如果结果的符号为负,则符号不同。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace SignsSameProject
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            bool x;

            x =  SignsAreTheSame(5, 2);
            if (x == true)
                MessageBox.Show("True");
            else
                MessageBox.Show("False");
        }

        private bool SignsAreTheSame(int x, int y)
        {
            long z;

            z = x * y;
            if (z < 0)
                return false;
            else
                return true;

        }
    }
}


评论


\ $ \ begingroup \ $
用这2个正数(2,int.MaxValue)测试您的答案,再用正数和负数(2,int.MinValue)测试您的答案。
\ $ \ endgroup \ $
–里克·戴文(Rick Davin)
2015年10月18日在13:23