Reputation: 1661
For use in cases where a standard library is not available. Assume that the month is given as an unsigned integer.
I'd be interested in seeing the shortest arithmetic expression that gives the correct answer, allowing or disallowing bitwise operators & masks but not lookup tables. Partial expressions can be saved into a variable for readability to showcase the idea used.
Upvotes: 1
Views: 157
Reputation: 382
unsigned int m, leapyr, y ;
//m = month range is 1 to 12
//y = year range is 00 to 99
leapyr = ( ( y & 0x03 ) && 1 ); //0 means leap year and 1 means Non leap year
m = 30 + ( ( m & 1 ) ^ ( 1 && ( m & 8 ) ) ) - ( ( !( m & 13 ) ) ) - ( ( !( m & 13 ) ) & leapyr );
My answer is considering year. If you don't want to use assign leapyr variable 0 or 1 as you wish.
Upvotes: 0
Reputation: 1661
Brute force answer in pseudocode for readability:
monthlength(month,is_leapyear) :=
oddmonth = ( month + (month >= 8) ? 1 : 0) % 2 // Or (month ^ (month >> 3))&1
feb_days_offset = (month == 2) ? 2 - is_leapyear : 0
return 30 + oddmonth - feb_days_offset
Where month >= 8 can also be implemented with a bitshift since it's just the fourth bit in an unsigned representation, so that oddmonth is (first bit) xor (fourth bit), which can be consisely written as (month ^ (month >> 3))&1 . Similarly, subtracting the feb offset can be thought of as flipping the second bit on febuary, and flipping the first bit during leap year february.
single line with no intermediate variables:
monthlength(month,isleapyear) := 30 + ( month + (month >= 8 ? 1 : 0)) % 2 - (month==2 ? (2 - isleapyear) : 0)
Alternatively, one that uses exclusively bitwise arithmetic and shifts using the tricks discussed above:
monthlength(month,leapyear) := 30 ^ (month==2)<<1 ^ (month==2)&leapyear ^ (month^month>>3)&1
Upvotes: 0
Reputation: 65046
Here's an approach that uses only four simple arithmetic and bitwise ops and a 26-bit constant:
int days_in_month(unsigned m) {
// 121110 9 8 7 6 5 4 3 2 1 0
return 28 + ((0b11101110111110111011001100u >> m * 2u) & 0b11);
}
If you also want to handle leap year (no mention of it in the question), you can take a similar approach, at the cost of a few more operations and a 50-bit constant:
int days_in_month2(unsigned m, bool ly) {
return 28 + ((0b11101110111110111011011111101110111110111011001100u >> (m + 12*ly) * 2u) & 0b11);
}
If you are willing the pass the leap year in a different way, e.g., setting a bit like month | 16
to indicate leap year, it would be more efficient.
I assume you pass the month as 1 to 12, not 0 to 11.
Tests and generated asm can be seen on godbolt.
Upvotes: 7
Reputation: 154169
Variation om @BeeOnRope nice answer.
#include <stdbool.h>
int DaysPerMonth(int Month, bool IsLeapYear) {
assert(Month >= 1 && Month <= 12);
// 0b11101110111110111011001100u
// 3 B B E E C C
return (((0x3BBEECCu | (IsLeapYear << 2*2)) >> Month*2) & 3) + 28;
}
#include <stdio.h>
int main() {
for (int ly = 0; ly <= 1; ly++) {
for (int m = 1; m <= 12; m++) {
printf("(%2d %2d), ", m, DaysPerMonth(m,ly));
}
puts("");
}
return 0;
}
Upvotes: 2