BIT : The Boolean Integer Test
The previous post was about how I got bitten by an implicit type conversion in C, and how the compiler allowed comparing an integer with a boolean value. This made me wonder about other languages, how do they behave? And so, I explored a number of other programming languages, many of which I’ve never even used before. It’s interesting to see how they handle this particular case. For this we use a small program that I call the BIT test; the boolean integer test. In pseudo code:
# BIT : Boolean Integer Test
a = -1
b = true
if (a) {
print "using integer as truth value"
}
if (a == b) {
print "comparing integer and bool : true"
} else {
print "comparing integer and bool : fail"
}
The first if-statement tests whether integer values can be used as
truth value. This assumes that -1
implies TRUE
, which is usually
the case. [Of course, usually non-zero implies true, but that’s not exactly
what we are testing for here].
What also may happen is that the compiler bails out with an error;
an integer is not a boolean.
I’m actually fine with both behaviors of this case (either compile or error
out), I just think it’s interesting when a language is strict enough
to flag an error for using an integer as truth value.
The second if-statement is more subtle. An integer value of -1
is not
equal to true
, therefore some languages will produce fail
.
You can (and may) argue that “fail” should actually read “fine”.
My confusion is complete however, because iff in the first if-statement
a
evaluated as true
, then why does the second if-statement not print
true
…?
In a lot of languages however, the second if-condition gives an error and
does not compile; cannot compare type integer with type bool. I actually
prefer this strict behavior; that’s what strong typing is about.
Following is a list of languages and their BIT test program.
C
/*
bit.c - boolean integer test
*/
#include <stdbool.h>
#include <stdio.h>
int main(void) {
int a = -1;
bool b = true;
if (a) {
printf("using integer as truth value\n");
}
if (a == b) {
printf("comparing integer and bool : true\n");
} else {
printf("comparing integer and bool : fail\n");
}
return 0;
}
The C program compiles and outputs:
using integer as truth value
comparing integer and bool : fail
C++
The C++ program is the same as the C program, except that we include
<cstdio>
. It also behaves the same.
C3
module output; // necessary for compiler explorer
import std::io;
fn void main() {
int a = -1;
bool b = true;
if (a) {
io::printfn("using integer as truth value");
}
if (a == b) {
io::printfn("comparing integer with bool : true");
} else {
io::printfn("comparing integer with bool : fail");
}
}
The second if-statement does not compile.
Go
package main
import "fmt"
func main() {
a := -1
b := true
if a {
fmt.Println("using integer as truth value")
}
if a == b {
fmt.Println("comparing integer with bool : true")
} else {
fmt.Println("comparing integer with bool : fail")
}
}
Both if-statements do not compile.
Swift
import Foundation
let a = -1
let b = true
if (a) {
print("using integer as truth value")
}
if (a == b) {
print("comparing integer with boolean : true")
} else {
print("comparing integer with boolean : fail")
}
Both if-statements do not compile.
Zig
const std = @import("std");
const builtin = @import("builtin");
pub fn main() void {
const a: i32 = -1;
const b: bool = true;
if (a) {
std.debug.print("using integer as truth value");
}
if (a == b) {
std.debug.print("comparing integer with bool : true");
} else {
std.debug.print("comparing integer with bool : fail");
}
}
Both if-statements do not compile.
Odin
package main
import "core:fmt"
main :: proc() {
a := -1
b := true
if a {
fmt.printf("using integer as truth value\n")
}
if a == b {
fmt.printf("comparing integer and bool : true\n")
} else {
fmt.printf("comparing integer and bool : fail\n")
}
}
Both if-statements do not compile.
Rust
fn main() {
let a = -1;
let b = true;
if a {
println!("using integer as truth value");
}
if a == b {
println!("comparing integer with bool : true");
} else {
println!("comparing integer with bool : fail");
}
}
Both if-statements do not compile.
Carbon
package BIT api;
fn Main() -> i32 {
var a: i32 = -1;
var b: bool = true;
if (a) {
Print("using integer as truth value");
}
if (a == b) {
Print("comparing integer to bool : true");
} else {
Print("comparing integer to bool : fail");
}
return 0;
}
Both if-statements do not compile.
C#
using System;
class Program
{
static void Main() {
int a = -1;
bool b = true;
if (a) {
Console.WriteLine("using integer as truth value");
}
if (a == b) {
Console.WriteLine("comparing integer with bool : true");
} else {
Console.WriteLine("comparing integer with bool : fail");
}
}
}
Both if-statements do not compile.
Java
class Test
{
public static void main(String []args)
{
int a = -1;
boolean b = true;
if (a) {
System.out.println("using integer as truth value");
}
if (a == b) {
System.out.println("comparing integer and boolean : true");
} else {
System.out.println("comparing integer and boolean : fail");
}
}
};
Both if-statements do not compile.
Scala
@main def run() =
val a: Int = -1
val b: Boolean = true
if (a) {
println("using integer as truth value")
}
if (a == b) {
println("comparing integer and boolean : true")
} else {
println("comparing integer and boolean : fail")
}
Both if-statements do not compile.
Kotlin
fun main() {
val a = -1
val b = true
if (a) {
println("using integer as truth value")
}
if (a == b) {
println("comparing integer and bool : true")
} else {
println("comparing integer and bool : fail")
}
}
Both if-statements do not compile.
JavaScript
let a = -1
let b = true
if (a) {
console.log("using integer as truth value")
}
if (a == b) {
console.log("comparing integer to bool : true")
} else {
console.log("comparing integer to bool : fail")
}
This outputs:
using integer as truth value
comparing integer to bool : fail
TypeScript
The BIT test program in TypeScript is the same as JavaScript. It also produces the same output when run. TypeScript does report an error for the second if-statement.
Perl
my $a = -1;
my $b = true;
if ($a) {
print("using integer as truth value\n");
}
if ($a == $b) {
print("comparing integer and bool : true\n");
} else {
print("comparing integer and bool : fail\n");
}
This outputs:
using integer as truth value
comparing integer and bool : fail
Ruby
a = -1
b = true
if a then
puts "using integer as truth value"
end
if a == b then
puts "comparing integer and boolean : true"
else
puts "comparing integer and boolean : fail"
end
This outputs:
using integer as truth value
comparing integer and boolean : fail
Lua
a = -1
b = true
if a
then
print("using integer as truth value")
end
if a == b
then
print("comparing integer and bool : true")
else
print("comparing integer and bool : fail")
end
This outputs:
using integer as truth value
comparing integer and bool : fail
Python
if __name__ == '__main__':
a = -1
b = True
if a:
print("using integer as truth value")
if a == b:
print("comparing integer with bool : true")
else:
print("comparing integer with bool : fail")
This outputs:
using integer as truth value
comparing integer with bool : fail
Mojo
def main():
a = -1
b = True
if a:
print("using integer as truth value")
if a == b:
print("comparing integer to boolean : true")
else:
print("comparing integer to boolean : fail")
The second if-statement does not compile.
Nim
let
a = -1
b = true
if a:
echo "integer used as truth value"
if a == b:
echo "comparing integer and bool : true"
else:
echo "comparing integer and bool : fail"
Both if-statements do not compile.
Julia
a = -1
b = true
if a
println("using integer as truth value")
end
if a == b
println("comparing integer with bool : true")
else
println("comparing integer with bool : fail")
end
Julia does not compile the first if-statement. But it does compile the second, and then branches to “fail”.
Pascal
program bit;
var
a : integer = -1;
b : boolean = TRUE;
begin
if a then
writeln('using integer as truth value');
if a == b then
writeln('comparing integer with bool : true');
else
writeln('comparing integer with bool : fail');
end.
Both if-statements do not compile.
Ada
with Ada.Text_IO;
procedure Bit is
a : Integer := -1;
b : Boolean := True;
begin
if a then
Ada.Text_IO.Put_Line("using integer as truth value");
end if;
if a == b then
Ada.Text_IO.Put_Line("comparing integer with bool : true");
else
Ada.Text_IO.Put_Line("comparing integer with bool : fail");
end if;
end Bit;
Both if-statements do not compile.
Fortran 90
PROGRAM bit
INTEGER :: a = -1
LOGICAL :: b = .TRUE.
IF (a) THEN
print *, 'using integer as truth value'
END IF
IF (a == b) THEN
print *, 'comparing integer and bool : true'
ELSE
print *, 'comparing integer and bool : fail'
END IF
END PROGRAM
Both if-statements do not compile.
FORTH
The FORTH language does not feature a boolean type, and thus the BIT test can not be ported to FORTH. Over there, the whole issue is a non-issue; does not apply. It’s fascinating if you think about why this is … FORTH is close to assembly language, and consequently in the design of FORTH there is no such thing as a “bool”.
Strong versus weak
The bool
found in higher-level programming languages is a conceptual
thing, that only exists in the mind of the compiler, so to speak.
But if that is so, then how do compilers compile bool
?
Right, it gets treated as an integer in disguise.
And so it happens that some languages mix up the difference between booleans
(value either true
or false
) and integers (which span a range of whole
numbers, positive or negative, including zero).
Languages that allow comparing integers and booleans are typically dynamically typed scripting languages. It’s rather odd that the statically typed C/C++ exhibit the behavior that they do. Strong typing is nothing new; many older languages that came before C already treated the comparison of integer and bool as an error.
The Julia language is unique in that it allows the comparison, but using an integer as truth value is an error. This is peculiar, and it almost feels like a bug.
Another notable case is Python. Python is dynamically typed, but it features
strong type annotations—which are basically comments, but you can
run a separate type checker. Moreover, the language features a TypeError
exception that is raised when you are using the wrong type (!) but it
does not raise for this case.
C and Python are easily my two favorite programming languages. I like their mild typing; kind of strong, but also weak. But even so, I strongly feel that not signalling the error, is an error. The BIT test shows that not all languages behave in the same way. That’s confusing; just like the laws of physics apply everywhere, arithmetics work the same everywhere, and boolean logic is rock solid no matter where you go. Yet there is no universal rule for comparison between integer and boolean in programming.