À utiliser dans les cas où une bibliothèque standard n'est pas disponible. Supposons que le mois soit donné sous forme d'entier non signé.
Je serais intéressé de voir la plus courte expression arithmétique qui donne la bonne réponse, autorisant ou interdisant les opérateurs et les masques au niveau du bit, mais pas les tables de recherche. Les expressions partielles peuvent être enregistrées dans une variable pour une meilleure lisibilité afin de présenter l'idée utilisée.
4 Réponses :
Réponse de force brute dans le pseudocode pour plus de lisibilité:
monthlength(month,leapyear) := 30 ^ (month==2)<<1 ^ (month==2)&leapyear ^ (month^month>>3)&1
Où month> = 8 peut également être implémenté avec un décalage de bits car il ne s'agit que du quatrième bit dans une représentation non signée, de sorte que le mois impair est ( premier bit) xor (quatrième bit), qui peut être consigné par (mois ^ (mois >> 3)) & 1. De même, soustraire le décalage de février peut être considéré comme un retournement du deuxième bit en février et un retournement du premier bit pendant l'année bissextile février.
une seule ligne sans variables intermédiaires:
monthlength(month,isleapyear) := 30 + ( month + (month >= 8 ? 1 : 0)) % 2 - (month==2 ? (2 - isleapyear) : 0)
Alternativement, celui qui utilise exclusivement l'arithmétique au niveau du bit et les décalages en utilisant les astuces décrites ci-dessus:
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
Utiliser un ternaire est probablement une meilleure idée que de multiplier par un booléen. La plupart des compilateurs créeront du code sans branche dans les deux cas, mais month == 2? 2-leapyear: 0
semble plus clair pour les humains. Vous ne voulez certainement pas qu'un compilateur fasse réellement un booléen 0/1 et le multiplie, en particulier sur un processeur intégré sans multiplication rapide du matériel. (par exemple en mode débogage, ou avec un compilateur de merde fourni par le fournisseur.)
Convenu. Il était là sous la forme de pseudocode principalement en raison de l'astuce du bithift, et a fini par vouloir rendre les choses cohérentes.
J'ai fini par changer la réponse donnée. Rend également l'expression copiable dans plus de langues.
Je doute que la plupart des compilateurs optimisent (mois + (mois> = 8? 1: 0))% 2
en (mois >> 3) et mois
. D'une part, car ce n'est même valable que si l'analyse de plage de valeurs peut prouver qu'il n'y a pas de bits définis au-delà du 4ème. Si vous voulez qu'il soit utilement copiable dans les langages courants de bas niveau, écrivez-le définitivement comme un shift.
J'ai fait un peu plus de golf de code et d'inspection de la sortie d'assemblage. Multiplier par un booléen avec un compilateur C décent compile le même code qu'un ternaire avec zéro. Nous nous sommes retrouvés avec des versions arithmétiques au niveau du bit qui n'utilisent pas du tout l'addition, car tout est de toute façon sur la définition des deux derniers bits.
Voici une approche qui n'utilise que quatre opérations arithmétiques et bit à bit simples et une constante de 26 bits:
int days_in_month2(unsigned m, bool ly) { return 28 + ((0b11101110111110111011011111101110111110111011001100u >> (m + 12*ly) * 2u) & 0b11); }
Si vous souhaitez également gérer les années bissextiles (pas de mention dans la question) , vous pouvez adopter une approche similaire, au prix de quelques opérations supplémentaires et d'une constante de 50 bits:
int days_in_month(unsigned m) { // 121110 9 8 7 6 5 4 3 2 1 0 return 28 + ((0b11101110111110111011001100u >> m * 2u) & 0b11); }
Si vous êtes prêt à passer l'année bissextile dans une autre façon, par exemple, un peu comme mois | 16
pour indiquer une année bissextile, ce serait plus efficace.
Je suppose que vous passez le mois de 1 à 12, pas de 0 à 11.
Tests et asm généré peut être vu sur godbolt .
Excellente approche! Une variation mineure serait: 28 | (0b11111010111110101111111010111110101111010011110000u >> (4 * m | 2 * ly) & 0b11) Qui gère les longueurs de mois avec des années bissextiles dans six instructions d'assemblage, un peu comme dans stackoverflow.com/revisions/62547215/1 , mais en remplaçant une instruction LEA par une instruction movabs selon godbolt
Variante de @BeeOnRope belle réponse.
#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; }
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.
Que voulez-vous dire "autoriser ou interdire les opérateurs et les masques au niveau du bit mais pas les tables de recherche". Alors, les opérations au niveau du bit sont-elles autorisées? Les tables de recherche sont-elles autorisées?
((((M + 9)% 12)% 5) +1)% 2 + 30
gère tout sauf février, mais après avoir vu douceur , pourquoi continuer?Une table de consultation est la bonne solution si vous vous souciez du code rapide et lisible. Si vous avez besoin d'un code lent et illisible, eh bien ... amusez-vous avec ça. Cette question sent le golf de code limite.
saolof, @Lundin comment est juste. Pourquoi ne pas utiliser une table de consultation?
Le contexte original de ma question était dans le code d'assemblage, où les tables de recherche sont carrément douloureuses.
Et bien sûr, étant donné le format de la question, l'utilisation d'une recherche est la solution triviale
Les petites tables de recherche ne sont pas pénibles dans de nombreuses formes de langage d'assemblage, que ce soit pour maintenir ou pour les performances. Les ISA embarqués particulièrement courants comme ARM où la différence entre la vitesse de la mémoire et la vitesse du processeur n'est pas aussi énorme que le x86 moderne. (Sur les x86-64 modernes, implémenter une table de recherche 2 bits en décalant un immédiat est une bonne astuce, cependant.)
@saolof vous n'a toujours pas répondu à ma question sur ce que vous entendez par "autoriser ou refuser".
@BeeOnRope Les deux réponses avec ou sans sont utiles. L'utilisation de l'arithmétique au niveau du bit est bien meilleure pour les plates-formes qui la prennent en charge. Les solutions qui n'utilisent pas l'arithmétique au niveau du bit seront moins performantes mais sans doute plus portables.
@saolof - peut-être que nos définitions ne sont pas les mêmes, mais des éléments de base au niveau du bit comme et, ou sont pris en charge par n'importe quelle plate-forme, même les petits microcontrôleurs. Je suppose que des choses comme le décalage variable varient, mais je m'attends à ce que ce soit au moins aussi courant que la multiplication.