前言
实验课上,老师布置了这样一个任务
万年历 :由用户输入一个日期(包含y年m月d日信息),请编写一个万年历程序能计算出这一天是星期几?
计算天数由三部分构成:
(1)y-1年包含的天数。 days1=(y-1)* 365+闰年的数量;因为闰年有366天。
(2)m-1月包含的天数。 days2=1月天数+2月天数+…+m-1月天数。
(3)days3=d天。
因此, 总的天数=days1+day2+days3,然后将求得的总天数进行模7运算,即得以7为周期的余数。得到的值与星期几之间有如下对应关系: 0-星期日;1-星期一;2-星期二;3-星期三;4-星期四;5-星期五;6-星期六。
(2)和(3)很好解决,只需要按照给定的公式写一个函数再调用就好了
可以我在计算(1)的时候遇到了一些问题
理论上,我们只需要这样写
// (1) 计算days1
long long days1 = (y - 1) * 365
+ (y - 1) / 4
- (y - 1) / 100
+ (y - 1) / 400;之后把days全部加起来再使用%运算符就可以算出7的余数,也就对应实际的星期了
// (4) 总天数
long long days = days1 + days2 + days3;
// (5) 求余数
int weekday = days % 7;但是之后老师又提出了一个问题
如果days值很大,以至于系统的long也无法处理,该怎么办?
虽然C语言中的long long最大值为9223372036854775807,也就意味着除非我们输入年份大于25269512429739111才会出现这种情况,但是问题都提出来了你不能不解决对吧
当然,我们要解决这个问题也不难,我们不用真的求出 days,只要对每个年份求模即可,所以我将其封装出一个函数
long long calculateDays1(int y) {
long long days1 = ((y - 1) * 365LL % 7
+ ((y - 1) / 4) % 7
- ((y - 1) / 100) % 7
+ ((y - 1) / 400) % 7
) % 7;
return days1;
}真的是非常简单的解决了这个问题呢,直到我在调试的时候不经意输入了2017这个年份,这个时候,days1变成了days1 = -1
这是什么鬼?
取模和取余的区别
搜索资料得知,C语言中的%运算事实上是取余运算而不是取模运算,而我之前一直以为这两种算法没有任何区别
那么什么是取模运算呢?
我们先从小学除法的定义出发:
两个整数相除:
被除数 = 除数 × 商 + 余数
举个例子:
被除数 = 除数 × 商 + 余数
17 = 5 × 3 + 2
这里的 2 就是 余数。
那么负数呢?
假设我们计算-17 ÷ 5
这时候问题就来了:
商可以取不同的整数,余数也会变。
在数学上有两种常见的处理方式:
取模操作也是整数除法后得到的余数,但取模的结果总是非负的。在数学中,取模通常表示为 a mod b,其中 a 是被除数,b 是除数。取模的结果与除数的符号相同。
这就意味着
取模的结果总是非负的。
在处理负数时,取模的结果可能是除数的绝对值减去取余的结果。
在我写的这个程序中,因为星期是循环的:
0 → 星期日
1 → 星期一
…
6 → 星期六
所以我们希望“结果”始终落在 0 ~ 6 范围内,
这就要求必须是数学意义上的模(modulus)。
那怎么办?
很简单,只需要在得出的结果加上一个周期(对于这里是一周的天数7),这样无论 x 是正数还是负数,结果都保证落在 0~6
对应到原来的程序中就是
long long days1 = ((y - 1) * 365LL % 7
+ ((y - 1) / 4) % 7 //每 4 年的闰年多一天
- ((y - 1) / 100) % 7 //每 100 年去掉一个闰年的天数
+ ((y - 1) / 400) % 7 //每 400 年加回闰年
+ 7) % 7; //强制先加一个正的 7,保证结果非负,因为 C++ 的%运算是取余而不是取模又是一个引入新功能造出bug的实例
其他的编程语言
% 是取余运算(结果符号与被除数相同)的语言:
C / C++
Java
JavaScript
Ruby
Go
Swift
Perl
PHP
Rust
……
% 是取模运算(结果符号与除数相同)的语言:
Python
评论