C# cast bool to int

Warning: This is a semi-rant about the WTF Microsoft backend code.

I’ve been working on a semi-large scale server backend for a game. While working in C#, my favorite language choice along with my favorite library .NET, I had to convert bool to int. Now, while I’ve done this a million times before, for some reason I was annoyed at it this time (I like when bools are equivalent to int). So I cracked open the .NET library to see if MS’ Convert.ToXYZ was using some sort of backend voodoo to cast bool to int. The short answer: no…Long answer: In fact, they don’t even cast. They just give new data back.

public static uint ToUInt32(bool value) {
    return value? (uint)Boolean.True: (uint)Boolean.False;
}
 
public static int ToInt32(bool value) {
    return value? Boolean.True: Boolean.False;
}

What the f**k Microsoft. Are you serious? Boolean.True/False are just constants, which means this is exactly the same as just doing this…

public static uint ToUInt32(bool value) {
    return value ? 1U: 0U;
}
 
public static int ToInt32(bool value) {
    return value ? 1: 0;
}
public struct Boolean
{
    internal const int True = 1;
    internal const int False = 0;
}

Why would they over-engineer such a simple solution? I’ll never understand it, so all I can say is, what the f**k Microsoft. Even you hate your own C# bool/int design flaw in my opinion.

——-
I did some testing with alternatives (results below), like an actual bool-int cast, but the speed could not match unsurprisingly. I assume by now that there are some major backend speed improvements, hence my lack of surprise.

	class Program
	{
		[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Explicit)]
		public struct BoolIntUnion
		{
			[System.Runtime.InteropServices.FieldOffset(0)]
			public int IntValue;
			[System.Runtime.InteropServices.FieldOffset(0)]
			public bool BoolValue;
		}
 
		internal static BoolIntUnion b2u = default(BoolIntUnion);
 
		public static int BoolToInt(bool value)
		{
			b2u.BoolValue = value;
			return b2u.IntValue;
		}
 
		public unsafe static int BoolToInt2(bool value)
		{
			return *((int*)&value);
		}
 
		static void Main(string[] args)
		{
			int val = 0;
			var sw = new System.Diagnostics.Stopwatch();
			sw.Start();
			for (int i = 0; i < 10000000; i++)
				val = Convert.ToInt32(i % 2 == 1);
			sw.Stop();
			Console.WriteLine("~B2I: " + sw.Elapsed);
 
			sw.Restart();
			for (int i = 0; i < 10000000; i++)
				val = BoolToInt(i % 2 == 1);
			sw.Stop();
			Console.WriteLine("+B2I: " + sw.Elapsed);
 
			sw.Restart();
			for (int i = 0; i < 10000000; i++)
				val = i % 2 == 1 ? 1 : 0;
			sw.Stop();
			Console.WriteLine("~B2I: " + sw.Elapsed);
 
			sw.Restart();
			for (int i = 0; i < 10000000; i++)
				val = BoolToInt(i % 2 == 1);
			sw.Stop();
			Console.WriteLine("+B2I: " + sw.Elapsed);
 
			sw.Restart();
			for (int i = 0; i < 10000000; i++)
				val = BoolToInt2(i % 2 == 1);
			sw.Stop();
			Console.WriteLine("~B2I: " + sw.Elapsed);
 
			sw.Restart();
			for (int i = 0; i < 10000000; i++)
			{
				b2u.BoolValue = i % 2 == 1;
				val = b2u.IntValue;
			}
			sw.Stop();
			Console.WriteLine("+B2I: " + sw.Elapsed);
 
			bool b;
			sw.Restart();
			for (int i = 0; i < 10000000; i++)
			{
				b = i % 2 == 1;
				unsafe
				{
					val = *((int*)&b);
				}
			}
			sw.Stop();
			Console.WriteLine("~B2I: " + sw.Elapsed);
 
			// Confirmation of it working
			Console.WriteLine(BoolToInt(true).ToString());
			Console.WriteLine(BoolToInt(false).ToString());
			Console.WriteLine(BoolToInt2(true).ToString());
			Console.WriteLine(BoolToInt2(false).ToString());
		}
	}

At 10,000,000 iterations
Convert.ToInt32 (aka if-else 1/0) : 84.8522ms
Bool Cast (BoolIntUnion): 176.6304ms
Avoiding call stack manipulation, if-else 1/0: 78.896ms
Bool Cast (BoolIntUnion confirmation): 178.0686ms
Bool Cast (pointers [unsafe]): 153.3262ms
Avoiding call stack manipulation, Bool Cast (BoolIntUnion): 96.2205
Avoiding call stack manipulation, Bool Cast (pointers [unsafe]): 84.6614ms

With this I conclude that a [ternary] if/else 1/0 on the spot will theoretically give the best performance. Realistically, it is highly unlikely to make a real world difference.

My goal was really to just understand what the deal is with C# bool and int handling and quite frankly, I still don’t get it. I guess I’m not big nonsensical language design choices like making bool and int different. I mean, in x86 assembly (and all assembly languages for that matter), it’s not like bool and int are inherently different, they are in fact only one thing, a byte or a few bytes.

mov eax, 1

Published by Goodlookinguy

Owner of NRGsoft, programmer, 日本語を話す人間, and UI aesthetics perfectionist.