The code is in Go. It could be easily ported to other languages.
Did you know that you can bitshift a number to the left or right by multiplying a number by 2 or dividing(integer division) a number by 2 respectively?
Anyway, let’s start by getting the least significant bit of a number:
// SPDX-FileCopyrightText: 2024 iketsj <iketsj@gmail.com>
//
// SPDX-License-Identifier: MIT
func getLeastSignificantBit(operand uint32) uint32 {
var operandLeastSignificantBit uint32
//can also use modulo ( % 2)
//shift the least significant bit to the left through multiplication
//and ignore the consequent zeroes
if operand * uint32(math.Pow(2, float64(31))) != 0 {
operandLeastSignificantBit = 1
} else {
operandLeastSignificantBit = 0
}
return operandLeastSignificantBit
}
Then let’s move on how to perform bitwise AND.
AND Truth Table:
A | B| C
0 | 0| 0
0 | 1| 0
1 | 0| 0
1 | 1| 1
// SPDX-FileCopyrightText: 2024 iketsj <iketsj@gmail.com>
//
// SPDX-License-Identifier: MIT
//AND is performed using multiplication
func BitwiseAnd(operand1 uint32, operand2 uint32) uint32 {
var answer uint32
if operand1 == 0 || operand2 == 0 {
//if one of the operand is already 0,
//anything multiplied(and) by 0 is automatically 0
answer = 0
} else {
for i := 0; i <= 31; i++ {
//get the least significant bit of both numbers
operand1LeastSignificantBit := getLeastSignificantBit(operand1)
operand2LeastSignificantBit := getLeastSignificantBit(operand2)
//AND is performed using multiplication
//then shift it to the right through multiplication
//then add it to the already processed answer
if operand1LeastSignificantBit * operand2LeastSignificantBit != 0 {
answer += 1 * uint32(math.Pow(2, float64(i)))
}
//perform shift to the right by 1 using division by 2
operand1 /= 2
operand2 /= 2
//if one of the operand is already 0, stop already
if operand1 == 0 || operand2 == 0 {
break
}
}
}
return answer
}
Then how to perform OR.
OR Truth Table:
A | B| C
0 | 0| 0
0 | 1| 1
1 | 0| 1
1 | 1| 1
// SPDX-FileCopyrightText: 2024 iketsj <iketsj@gmail.com>
//
// SPDX-License-Identifier: MIT
//OR is performed using addition but take care of carry 1 as 1 + 1 = 10 bin
func BitwiseOr(operand1 uint32, operand2 uint32) uint32 {
var answer uint32
if operand1 == 0 || operand2 == 0 {
//if one of the operand is already 0, just add them
answer = operand1 + operand2
} else {
for i := 0; i <= 31; i++ {
//get the least significant bit of both numbers
operand1LeastSignificantBit := getLeastSignificantBit(operand1)
operand2LeastSignificantBit := getLeastSignificantBit(operand2)
//perform OR operation by adding
//this also takes care of the carry 1
if operand1LeastSignificantBit + operand2LeastSignificantBit != 0 {
answer += 1 * uint32(math.Pow(2, float64(i)))
}
//perform shift to the right by 1 using division by 2
operand1 /= 2
operand2 /= 2
//if one of the operands is already 0,
//just add(or) the remaining bits to their proper place
if operand1 == 0 {
answer += operand2 * uint32(math.Pow(2, float64(i + 1)))
break
} else if operand2 == 0 {
answer += operand1 * uint32(math.Pow(2, float64(i + 1)))
break
}
}
}
return answer
}
Then how to perform XOR.
XOR Truth Table:
A | B| C
0 | 0| 0
0 | 1| 1
1 | 0| 1
1 | 1| 0
// SPDX-FileCopyrightText: 2024 iketsj <iketsj@gmail.com>
//
// SPDX-License-Identifier: MIT
//XOR, perform OR if bits are not the same. if bits are the same, answer is 1.
func BitwiseXor(operand1 uint32, operand2 uint32) uint32 {
var answer uint32
if operand1 == operand2 {
//if both operands are equal, return 0
answer = 0
} else if operand1 == 0 || operand2 == 0 {
//if one operand is 0, just add the other
answer = operand1 + operand2
} else {
for i := 0; i <= 31; i++ {
//get the least significant bit of both numbers
operand1LeastSignificantBit := getLeastSignificantBit(operand1)
operand2LeastSignificantBit := getLeastSignificantBit(operand2)
//perform XOR operation
//if bits are the same, bit answer is 0
//if bits are not the same, bit answer is 1
if operand1LeastSignificantBit != operand2LeastSignificantBit {
answer += 1 * uint32(math.Pow(2, float64(i)))
}
//perform shift to the right by 1 using division by 2
operand1 /= 2
operand2 /= 2
//if one of the operands is already 0
//just add(OR) the remaining bits to their proper place
//yes, OR
if operand1 == 0 {
answer += operand2 * uint32(math.Pow(2, float64(i + 1)))
break
} else if operand2 == 0 {
answer += operand1 * uint32(math.Pow(2, float64(i + 1)))
break
}
}
}
return answer
}
Then how to perform NOT.
NOT Truth Table:
A | C
0 | 1
1 | 0
// SPDX-FileCopyrightText: 2024 iketsj <iketsj@gmail.com>
//
// SPDX-License-Identifier: MIT
//NOT, just invert bits
func BitwiseNot(operand uint32) uint32 {
var answer uint32
if operand == 0 {
//if operand is already 0
//return the inverted bits which is 0xFFFFFFFF
answer = 0xFFFFFFFF
} else if operand == 0xFFFFFFFF {
//if operand already has all the bits set
//return the inverted bits which is 0x00000000
answer = 0x00000000
} else {
for i := 0; i <= 31; i++ {
//get the least significant bit of the number
operandLeastSignificantBit := getLeastSignificantBit(operand)
//perform NOT
if operandLeastSignificantBit == 0 {
answer += 1 * uint32(math.Pow(2, float64(i)))
}
//perform shift to the right by 1 using division by 2
operand /= 2
//if remaining bits are 0
//trim 0xFFFFFFFF by shifting it to the right
// by how many bits are already processed through division
//shift it to the left by
// how many bits are already processed through multiplication
// then add it to the processed answer
if operand == 0 {
answer += (0xFFFFFFFF / uint32(math.Pow(2, float64(i + 1)))) * uint32(math.Pow(2, float64(i + 1)))
break
}
}
}
return answer
}
With NAND, XNOR, NOR. We can just chain together function calls.
BitwiseNot(BitwiseAnd(1, 1)) //4294967294 in unsigned 32bit
BitwiseNot(BitwiseXor(0, 0)) //4294967295 in unsigned 32bit
BitwiseNot(BitwiseOr(4, 4)) //4294967291 in unsigned 32bit
Note:
There might be some more optimized way.