Bit Puzzles

During my work on the computer organization topics, I explored a series of bit-level manipulation problems that tested my understanding of how data is represented and manipulated at the hardware level. Each function pushed me to think critically about constraints, efficiency, and correctness. Below, I document my solutions, explaining my thought process, why I implemented them as I did, and what could be improved. All code written in C.
1. is_tcomp_min
Objective:
Determine if x
is the smallest two’s complement integer.
My Code:
int is_tcomp_min(int x) {
int temp = ~0; // Mask with all 1s
int isEqual = (x ^ temp) + 1; // Check if x is the inverse of all 1s
int result = !(isEqual ^ x); // Confirm equality
result = !(result ^ (!!x)); // Exclude x = 0
return result;
}
Why I Did It This Way:
I used bitwise operations to ensure compliance with the constraints. By leveraging XOR, I identified whether x
matched the bit pattern of the two’s complement minimum (0x80000000
). Adding a step to exclude 0
as a false positive was essential.
What I Could Have Done Better:
The logic is efficient, but I could have used simpler checks (e.g., comparing x
directly to 0x80000000
) to make it more intuitive for future readers.
2. can_tcomp
Objective:
Check if x
can be represented using n
bits in two's complement.
My Code:
int can_tcomp(int x, int n) {
int shift = 32 + (~n + 1); // Calculate how much to shift
int x_after_shift = (x << shift) >> shift; // Shift and restore
return !(x ^ x_after_shift); // Compare with original
}
Why I Did It This Way:
I chose to leverage shifts because they allow me to simulate truncating the higher-order bits while checking if the value still fits. This method ensures correctness with minimal operations.
What I Could Have Done Better:
The code is compact and effective, but I could have added comments explaining why (32 + (~n + 1))
is used to calculate the shift value.
3. is_add_ok
Objective:
Determine if adding two integers results in overflow.
My Code:
int is_add_ok(int x, int y) {
int sum = x + y; // Compute the sum
int overflow = ((x ^ sum) & (y ^ sum)) >> 31; // Check for sign mismatches
return !overflow;
}
Why I Did It This Way:
Overflow occurs when the signs of x
and y
are the same, but the sign of the result differs. By isolating these mismatches using XOR and masking, I efficiently detected overflow cases.
What I Could Have Done Better:
While the solution is concise, I could enhance readability by breaking down the overflow condition into smaller, well-named expressions.
4. is_less_or_equal
Objective:
Check if x <= y
.
My Code:
int is_less_or_equal(int x, int y) {
int diff = x + (~y + 1); // Calculate x - y
int sign_x = (x >> 31) & 1;
int sign_y = (y >> 31) & 1;
int sign_diff = (diff >> 31) & 1;
return (sign_x & !sign_y) | (!sign_x & !sign_y & sign_diff) | !(x ^ y);
}
Why I Did It This Way:
The key was to handle cases where x
and y
had different signs (e.g., x
negative, y
positive) separately from cases where they had the same sign. This structured approach ensured correctness in all scenarios.
What I Could Have Done Better:
Some intermediate variables, like sign_diff
, could have been better named for clarity. Adding inline comments for each logical case would improve maintainability.
5. sat_add
Objective:
Perform addition with saturation to handle overflows gracefully.
My Code:
int sat_add(int x, int y) {
int sum = x + y;
int x_sign = x >> 31;
int y_sign = y >> 31;
int sum_sign = sum >> 31;
int pos_overflow = ~(1 << 31); // Max int
int neg_overflow = 1 << 31; // Min int
int overflow = ((~x_sign & ~y_sign & sum_sign) | (x_sign & y_sign & ~sum_sign));
return (~overflow & sum) | (overflow & ((x_sign & neg_overflow) | (~x_sign & pos_overflow)));
}
Why I Did It This Way:
I identified overflow conditions by examining the relationship between the sign bits of the operands and the result. If overflow occurred, the result was clamped to the appropriate extreme value.
What I Could Have Done Better:
This solution is efficient and meets the constraints, but explaining the purpose of each logical condition with comments would make it more readable.
6. float_neg
Objective:
Return the bit-level negation of a floating-point value.
My Code:
unsigned float_neg(unsigned uf) {
unsigned sign = uf & 0x80000000;
unsigned frac_exp = uf & 0x7FFFFFFF;
if (frac_exp > 0x7F800000) return uf; // NaN
return uf ^ 0x80000000; // Flip sign bit
}
Why I Did It This Way:
Floating-point negation is as simple as flipping the sign bit, except for NaN values, which must remain unchanged. By isolating the sign and fraction-exponent fields, I handled these cases explicitly.
What I Could Have Done Better:
Add comments to explain why NaN detection uses frac_exp > 0x7F800000
.
7. float_to_int
Objective:
Convert a floating-point value to an integer, handling out-of-range cases.
My Code:
int float_to_int(unsigned uf) {
unsigned sign = uf >> 31;
unsigned exp = (uf >> 23) & 0xFF;
unsigned frac = uf & 0x7FFFFF;
int bias = 127;
int e = exp - bias;
if (e < 0) return 0;
if (e >= 31) return 0x80000000u;
frac = frac | 0x800000; // Add implicit 1
int result = (e < 23) ? (frac >> (23 - e)) : (frac << (e - 23));
return sign ? -result : result;
}
Why I Did It This Way:
This implementation carefully handles the floating-point representation by normalizing the fraction and adjusting for the exponent. Special cases like NaN and out-of-range values are handled early.
What I Could Have Done Better:
The function is correct, but breaking down the logic into helper functions (e.g., for normalization or range checking) could improve readability.
Final Thoughts:
Working on these puzzles deepened my understanding of bit-level operations and how hardware handles data. Each solution reflects my focus on adhering to constraints, ensuring correctness, and writing efficient code. While the solutions are functional, some could benefit from improved clarity and comments for future readers. This project was both challenging and rewarding, showcasing my ability to think critically and optimize under constraints.