Why is 1 && 2 in C# false?
Asked Answered
D

4

14

I got frustated with my other question. So i wrote up this example.

In C the below is true. See demo

int main()
{
printf("%d", 1 && 2);
return 0;
}

Output:

1

In C#. It is FALSE. WHY is this false? Also i dont understand why i needed to create the bool operator in this example but not the one in my other question but no matter. Why is the below false? it makes no sense to me.

BTW the logic making the below false is described here

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            MyInt a=1, b=2;
            bool res=a && b;
            Console.WriteLine("result is {0}", res);
        }

        class MyInt
        {
            public int val;
            public static bool operator true(MyInt t) { return t.val != 0; }
            public static bool operator false(MyInt t) { return t.val == 0; }
            public static MyInt operator &(MyInt l, MyInt r) { return l.val & r.val; }
            public static MyInt operator |(MyInt l, MyInt r) { return l.val | r.val; }
            public static implicit operator MyInt(int v) { return new MyInt() { val = v }; }
            public static implicit operator bool(MyInt t) { return t.val != 0; }
        }
    }
}
Donovan answered 5/3, 2011 at 12:20 Comment(8)
If you want to code in C, code in C. If you want to code in C++, code in C++. If you want to code in C# code in C#. But you can't assume that these languages are interchangeable. You will have the most success if your code according to the rules of the language you are writing rather than coding to the rules of some other language.Rayborn
common convention should always trump every other aspect - if 1 has always been true, and 0 always been false, a language that does things differently should be criticised for it. Us poor humans only remember so much and we make mistakes, so changing conventions just makes mistakes more likely.Menado
@gbjbaanb: Exactly and if its going to change at least tell us the reasoning behind it so we can take advantage of why it changed. Which is what i was trying to ask (three times. Almost no one got it). This (the why reason) is why i liked CodeInChaos answer in another question #5203998Donovan
@gbjbaanb: 0 remains false and 1 remains true for the MyInt class above if the & and | operators are implemented correctly, as I specify below. The language doesn't "do things differently," at least not in the way you seem to imply. It just uses & and | as non-short-circuiting Boolean operators when the operands aren't integers.Haberdashery
@gbjbaanb: 1 has not always been true in all languages... sometimes -1 has been, e.g. in VB (all bits set to 1 in an int).Crinkumcrankum
@acidzombie24: When you pick up a new language, suggest you read through the language specification first. I'm not trying to be negative... this is a serious suggestion. Languages evolve over time and it's important to understand the difference between what you already know and what you are learning.Crinkumcrankum
@Eric J.: I read generics, getters, setters, this[T t], how explicit/implicit cast work since it was different from C++ operator T(). However, i did not look at how for loops work or how '|' works since every language has them and they all work the same. However, i did not think || would be different nor seen it different up until now. And its done a good job making me believe its the same. I dont suppose you suggest i should read up on how function call work in every language? (but i do know named parameters but that was new since the version i started with) Or the assignment operator?Donovan
I looked at their C++ to C# guide before any of that. This was NOT one of the things in that document. I dont know if you take that suggest back now but w/e. One funny thing about the assignment operator being the same in every language. They works differently in my language. It obvious though.Donovan
B
22

In C there is no bool. Convention is that 0 is false and != 0 is true. if statement treated conditional expression result exactly that way.

In C++ bool was introduced. But it was compatible with old rules, 0 treated as false and false as 0, and there was implicit conversion between int and bool.

In C# it is not the same way: there is bool and int and they are not convertible to eachother. That is what C# Standard says. Period.

So when you tried to reimplement bool and int compatibility you made a mistake. You use && which is logical operator, but in C# you can't override it and only &, which is implemented as bitwise. 1 & 2 == 0 == false! here it is!

You even should not overload bitwise ones, to maintain compatibility you just have to leave operator true and false.

This code works as you expect:

class Programx
{
    static void Main(string[] args)
    {
        MyInt a = 1, b = 2;
        bool res = a && b;
        Console.WriteLine("result is {0}", res);
    }

    class MyInt
    {
        public int val;
        public static bool operator true(MyInt t)
        {
            return t.val != 0;
        }
        public static bool operator false(MyInt t)
        {
            return t.val == 0;
        }
        public static implicit operator MyInt(int v)
        {
            return new MyInt() { val = v };
        }
        public static implicit operator bool(MyInt t)
        {
            return t.val != 0;
        }
    }
}

result is True

Brimmer answered 5/3, 2011 at 12:24 Comment(14)
I always assumed int was not implicitly cast to bool to avoid accidental if/while/for problems. If i wrote this the result is true var b1 = Convert.ToBoolean(a.val); var b2 = Convert.ToBoolean(b.val); Console.WriteLine("result is {0}", b1&&b2);. Why doesnt C# do boolean AND the way C does. Why is the boolean and logic the way it is?Donovan
In C there was no bool until 1999.Zanthoxylum
Ok accepted as the answer. But... WTF, now i cant use bitwise ands. And if i do add bitwise ands logical ands break... WTF. I still dont understand why C# uses && as a short circuit &.Donovan
using logical operators on integers doesn't really make sense @acid. It's just a work around for c's weak type system.Fruitcake
@acidzombie24 let me try to explain in short words. C# compiler sees x && y, and neither of them are bools, your case. It checks is operator false defined. If it is there it is called. If it returns false expression value is false (short circuiting) if it returns true (your case) then operator & is called. But it returns bitwise, not logical result. This is the contradiction.Brimmer
@acidzombie24 root problem is that you mixed too much sorts of operations in one type. another thing is that C is weakly typed about bools and you are trying to loose strict C# typing to weak.Brimmer
@Mike the bool type in C is in the language called _Bool, not bool. The latte is merely a library macro.Protean
@Andrey: For completeness i'll add in this answer which puts things in perspective. #5203998Donovan
@acidzombie24 that answer is greatBrimmer
@CodeInChaos: The behavior c uses for integers in Boolean context is perfectly reasonable in context. A great many (most, I believe) processors of that era had conditional branches like "jump-if-zero" and "jump-if-not-zero", and K&R were intimately familiar with assembly programming in that environment. Imposing a strongly differentiated bool type would have been silly for a systems programming language targeting those processors.Gastro
Even today processors work with these instructions. The abstraction of having a bool different from int doesn't cost any performance, but makes the language cleaner. Just like there is no difference between a pointer and an integer as far as the processor is concerned. But it still makes sense to separate those in a language. It's the usual case that if you are too familiar with something you are blind to alternatives, even if the alternative is better. Unfortunately that happens to all of us more often than we'd like.Fruitcake
@Andrey: You hit the nail on the head saying that the attempt to implement "bool and int compatibility" was a mistake, but I disagree with your followup. Specifically, when you assert that the & operator, when overridden on a class type, is "implemented as bitwise." It's "implemented as" a non-short-circuiting logical AND operator on class types that are advertising the fact that they want to be treated like Boolean types (by participating in logical & and && expressions). "1 & 2 == 0 == false" is a non-sequitor, because the zero and the false have no relationship to one another in C#.Haberdashery
@CodeInChaos: In 1972 asking a programmer to give up cycles to preserve an abstraction was dumb. All the more so in a language that was designed to implement much of the core functionality of a OS running on a simple (for that era!) machine. Simple compilers, machine memories measured in kilowords and speeds in thousands of operations per second: machine time cost more than programmer time which is very different from the world we live in.Gastro
You're not giving up any cycles at runtime. And the pascal compilers of the same era had that exact featureFruitcake
H
9

Your implementations of operator& and operator| are wrong. These binary operators have bitwise meanings when applied to integral types, and when applied to either Boolean types or classes that have their own & and | operators, they have logical AND and OR semantics (being the non-short-circuiting cousins of && and ||). Correct implementations would look as follows:

operator &(MyInt l, MyInt r) {return l.val != 0 && r.val != 0);}
operator |(MyInt l, MyInt r) {return l.val != 0 || r.val != 0);}
Haberdashery answered 5/3, 2011 at 12:26 Comment(8)
WTF!?! So... & is either logical or short circuited logical? not Boolean logical? (maybe i am saying this wrong but i think you know what i mean?)Donovan
And the bottom line is you simply cannot make an integer type in C# that supports bitwise operations to that integer with the & and | operator and at the same time overload true/false to provide a logical truth test.Evacuee
Why on earth would it be like this!?!?!Donovan
@acidzombie24: I'm afraid I don't get what you mean by the distinction. & and |, when supported as user-defined operators on a class, do double duty supporting both the non-short-circuiting logical (i.e., Boolean) & and | operations and the short-circuiting logical (i.e., Boolean) && and || operations. They are not intended to support bitwise (i.e., binary mathematical) operations.Haberdashery
it is plainly incorrect, because you are mixing bitwise and logical operatorsBrimmer
@Andrey: Exactly. I always thought && was boolean/logical and & was bitwise. But now i find out that && is a short circuit bitwise. What sense does that make!?!Donovan
@acid && isn't a short circuit bitwise on any reasonable type. The core of your problem is the misunderstanding that & means bitwise and in C#. It doesn't. It means non short-circuiting and.Fruitcake
And the problem with your type isn't the incorrect implementation of & it is the incorrect implementation of true and false. Those operators should not exist on a non logical type. See my answer to your old question on why these operators are designed like this in C#.Fruitcake
M
2

I'll try and make this simple, since I think people are overcomplicating this.

var x = 1 & 2;
// behind the scenes: 0001 AND 0010 = 0000
Console.Write(x); // 0, as shown above

Integers can NOT be used as booleans in C#. The result of:

if (1 && 2) // compile error
var x = 1 && 2; // compile error

There is no point to asking why an Integer can not be used as a boolean in C#, it just can't. The type system does not allow it. If one were to implement their own Integer class, they could provide implicit conversions from their type to bool, but int does not do this. You also have to make a choice when overloading; do you want bitwise behaviour, or logical behaviour. You can not have both.

Some languages allow 0, "", [] as 'falsey' values. C# does not. Get over it, and use a bool if you're doing boolean logic. If all else fails, Convert.ToBoolean on an int will return true for all non-zero values.

Magdeburg answered 5/3, 2011 at 12:55 Comment(0)
D
1
public static MyInt operator &(MyInt l, MyInt r) { return l.val & r.val; }

If I read the linked article correctly, res = a && b will be "expanded" to:

MyInt.false(a) ? a : MyInt.&(a, b)

MyInt.false(a) is false, so evaluates to:

MyInt.&(a, b)

which "expands" to:

a.val & b.val

which is (1 & 2) == 0, and thus false.

Disciplinant answered 5/3, 2011 at 12:32 Comment(12)
Yes i knew that since the beginning thus why i asked my other question. My question is WHY ON EARTH is it doing this!?!Donovan
What part is annoying you? (1&2) == 0?Disciplinant
@acidzombie24 Why not? Imagine you come to C/C++ from Pascal do you start with: #define Begin { #define End } ? Or you'll read language reference and use new rules?Subreption
@Mat: The fact that bool 1 is true and bool 2 is true but 1&&2 is not.Donovan
@Nick: Well, if && is logical and not boolean then why even bother to short circuit it? and if its suppose to be like C (since much of the syntax is similar) why even have the && or make it not boolean like C does. I dont understand its use if its suppose to do a logical and.Donovan
It's very useful, both for efficiency (not evaluating the right side if you don't need to) and to avoid NullPointerExceptions eg if (o!=null && o.Property) avoids horrible nested ifs and prevents an exception if it were just if (o.Property) when o was nullIconoclasm
If you want the C semantics so much, use C :-) C doesn't have operator overloading, so that simplifies the problem a lot from a language perspective. If you go with C++ for the expressivity and user-defined types, you'll be able to overload operator&&, but you'll loose short-circuiting in the process and that has some surprising effects if you're not really really careful. It's always a matter of compromise. At least the behavior in C# preserves short-circuiting and is well documented.Disciplinant
@acidzombie24 I don't know what to add to other good explanations here. I'm really wonder why you with C++ background ask that :( I come to C# with ASM/C/C++ background and just keep in mind that Minimal / Short-circuit evaluation - en.wikipedia.org/wiki/Minimal_evaluation here. Have you read this article? Have you read #1799572 ?Subreption
@Nick: None of these answers are great IMO. This answer put things into perspective. #5203998 I come from asm as well and to me & means if left and right are set, the result is set. So i assume if left and right as a bool is true it would be true. I had no idea && wasnt a logical/boolean op in C#. This answer linked told me that and why it uses &.Donovan
@acidzombie24 C# is new language and it not C/C++. Yes, it utilizes some (or you can say "much") of С/С++ syntax and even semantics for easier migration for us. Yes, sometimes it can really confusing with different background, one of this is finalizer syntax (~) and have really confusing meaning in С++ (destructor) and C# (finalizer). Yes, there are many other language features, sometimes similar to other (C++, JAVA or even Delphi), sometimes differ.Subreption
@acidzombie24 When I have to use new language (F#, Python, Haskell, Erlang or other, such as LISP, Prolog) in my work or just for studying I try do not implicate my previous language knowledge and start from reference. There are maybe academic interest why language designed in such way but not in another but when you start to use it you use it as it is, you can't really change language. You can stay in language of your choice without moving or construct another language and try to make it popular.Subreption
@Nick: Really, the confusion was if it worked that way why bother having both && and &? If its data you'll want to always apply & and if its bool maybe you can specialize it and only short circuit there. CodingInChaos mention thats how pascal does it. Anyways my frustration was NO ONE said WHY it worked this way (except one) and told me i was 'doing it wrong' and told me to do it some other way with no reasoning why that other way works except that it 'works'.Donovan

© 2022 - 2024 — McMap. All rights reserved.