【??BUG??】在MYSQL源碼里面有一段,算每年的天數。其中用到了兩個很有意思的
1)(year & 3) == 0
2)(year % 400 == 0 && year),為什么要 &&year呢?
>>>>>mysql-server/mysys/my_time.cc 的128~137行/**Calc days in one year.@note Works with both two and four digit years.@return number of days in that year
*/
uint calc_days_in_year(uint year) {return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)) ? 366: 365);
}
一般的算閏年的寫法很簡單
一、閏年的基本定義與規則
閏年是為了彌補因公歷紀年中 “平年”(365 天)與地球實際公轉周期(約 365.2422 天)的時間差而設立的。具體判斷規則如下:
1. 普通閏年判斷規則
滿足以下條件之一的年份為閏年:
- 能被 4 整除且不能被 100 整除。
例:2004 年(2004÷4=501,2004÷100=20.04)是閏年;
1900 年(1900÷4=475,但 1900÷100=19)不是閏年。
2. 世紀閏年判斷規則
- 能被 400 整除的整百年份為閏年。
例:2000 年(2000÷400=5)是閏年;
1800 年(1800÷400=4.5)不是閏年。
二、規則拆解與邏輯說明
可用邏輯表達式表示為:
(年份 % 4 == 0 且 年份 % 100 != 0) 或 年份 % 400 == 0
-
為什么除以 4?
地球公轉約 365.2422 天,每 4 年積累約 0.9688 天,接近 1 天,故每 4 年設 1 個閏年(加 1 天)。 -
為什么除以 100 的例外?
每 4 年加 1 天會導致誤差:400 年累計多算約 3 天。因此規定 “能被 100 整除但不能被 400 整除的年份” 不算閏年(如 1900 年、2100 年),以修正誤差。 -
為什么除以 400 的例外?
進一步修正誤差:每 400 年中,原本應排除 3 個整百年(如 1700、1800、1900),但保留能被 400 整除的年份(如 2000 年),使 400 年內閏年數量為 97 個,更接近實際公轉周期。
C語言寫個簡單的函數,首先要判斷不能<=0,再做判斷。
int isLeapYear(int year) {if (year <= 0) return 0;return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}
=====================================================================
但是前面那段mysql的程序是什么意思呢?
在閏年判斷函數中,(year & 3) == 0
?是一種高效的位運算技巧,用于替代?year % 4 == 0
?判斷一個年份是否能被 4 整除。
一、位運算原理:為什么?(year & 3) == 0
?等價于?year % 4 == 0
?
1. 4 的倍數的二進制特征
一個整數能被 4 整除,當且僅當其二進制表示的最后兩位是 0。
例如:
- 4 的二進制:
100
(最后兩位是 00) - 8 的二進制:
1000
(最后兩位是 00) - 12 的二進制:
1100
(最后兩位是 00) - 15 的二進制:
1111
(最后兩位是 11,不能被 4 整除)
2. 位與運算(&)的作用
3
?的二進制是?0011
(假設是 32 位整數,則為?000...0011
)。當一個數?year
?與?3
?進行位與運算時:
year & 3 等價于:只保留 year 的二進制表示的最后兩位,其余位清零
因此:
- 若?
year
?能被 4 整除(最后兩位是 00),則?year & 3
?的結果為?00
(十進制 0)。 - 若?
year
?不能被 4 整除(最后兩位不為 00),則?year & 3
?的結果為?01
、10
?或?11
(十進制 1、2 或 3)。
二、為什么用位運算替代取模(%)?
1. 性能優勢
- 取模運算(%):在 CPU 中通常需要除法指令,涉及多次減法或移位操作,計算開銷較大。
- 位與運算(&):直接對二進制位進行操作,僅需一次邏輯運算,速度遠快于取模。
2. 編譯器優化的局限性
雖然現代編譯器可能會將?year % 4
?優化為?year & 3
(當除數是 2 的冪時),但:
- 不同編譯器的優化能力不同,手動使用位運算能確保在所有環境下都高效。
- 代碼可讀性不受影響,因為?
(year & 3) == 0
?的含義很直觀(“判斷是否為 4 的倍數”)。
三、原代碼的閏年判斷邏輯拆解
原代碼為:
return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)) ? 366 : 365);
1. 邏輯表達式的結構
整體邏輯等價于:
if ((year & 3) == 0 && (year % 100 != 0 || year % 400 == 0)) {return 366; // 閏年
} else {return 365; // 平年
}
2. 核心判斷步驟
- 第一步:判斷是否為 4 的倍數:
(year & 3) == 0
?替代?year % 4 == 0
,高效檢查年份是否能被 4 整除。 - 第二步:判斷是否為 100 的倍數:
year % 100 != 0
(原代碼中?year % 100
?為真等價于?year % 100 != 0
)。 - 第三步:判斷是否為 400 的倍數:
year % 400 == 0
。
3. 特殊情況處理:&& year
?的作用
原代碼中?(year % 400 == 0 && year)
?中的?&& year
?是冗余的,因為:
- 年份?
year
?是?uint
(無符號整數),其值 ≥ 0。 - 當?
year = 0
?時,year % 400 == 0
?為真,但?&& year
?為假,結果為平年(符合實際,因為公元 0 年不存在)。 - 當?
year > 0
?時,&& year
?恒為真,不影響判斷。