• 负数如何取模?深度理解取模:取余运算



    在了解取模和取余运算之前,我们必须先了解 取整,因为余数是先确定好整数商之后的附带产物,而取模和取余的概念又非常相似(没错,取模和取余是不同的)

    关于”取整“,你必须得知道的事儿

    C语言采取的取整规则

    #include 
    int main() {
    
        int a = 2.9;
        int b = -2.9;
        printf("%d\n", a);
        printf("%d\n", b);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    image-20220831204038140

    结果是2和-2,大家应该是知道的,但是大家了解原理吗?看下面的图

    image-20220831211055018

    C语言采取的就是上图所述的向0取整

    取整函数trunc

    取整函数trunc采用的也是向0取整规则

    #include 
    #include 
    int main() {
        printf("%.1f\n", trunc(-2.1));
        printf("%.1f\n", trunc(-2.9));
        printf("%.1f\n", trunc(2.1));
        printf("%.1f\n", trunc(2.9));
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    image-20220831204656669

    取整函数ceil

    #include 
    #include 
    int main() {
        printf("%.1f\n", ceil(-2.1));
        printf("%.1f\n", ceil(-2.9));
        printf("%.1f\n", ceil(2.1));
        printf("%.1f\n", ceil(2.9));
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    image-20220831204931618

    结果好像都变大了

    image-20220831211125147

    那是因为ceil函数取整采用的规则是向+∞取整

    取整函数floor

    #include 
    #include 
    int main() {
        printf("%.1f\n", floor(-2.1));
        printf("%.1f\n", floor(-2.9));
        printf("%.1f\n", floor(2.1));
        printf("%.1f\n", floor(2.9));
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    image-20220831205335393

    结果又全都变小了

    image-20220831211137483

    因为floor函数取整采取的是向-∞取整

    取整函数round

    #include 
    #include 
    int main() {
        printf("%.1f\n", round(-2.1));
        printf("%.1f\n", round(-2.9));
        printf("%.1f\n", round(2.1));
        printf("%.1f\n", round(2.9));
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    image-20220831205157901

    这结果有点像四舍五入

    image-20220831211148859

    没错,round函数取整采取的是四舍五入取整

    结论

    浮点数(整数/整数),其实是有很多种取整方式的(四种),而C语言采取的是向0取整

    是时候该聊聊取模和取余了

    先看一下取模的定义:

    如果a和d是两个自然数,d非零,可以证明存在两个唯一的整数q和r,满足a = q * d + r 且0 <= r < d。其中q被称为商,r被称为余数。

    看下面代码:

    #include 
    int main()
    {
        int a = 10;
        int b = 3;
        printf("%d\n", a / b);
        printf("%d\n", a % b);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    image-20220831215516403

    可以看到结果分别是3和1,此时还没有任何的问题。

    #include 
    int main()
    {
        int a = -10;
        int b = 3;
        printf("%d\n", a / b);
        printf("%d\n", a % b);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    image-20220831215714181

    再看结果,分别是-3和-1,这里有一点不对劲儿了,-3还好理解,但是-1好像跟定义不太符合,再继续往下看

    image-20220831220949016

    在python中,正数的“取模”是1非常正确,但是负数的“取模”竟然是2,这好像太不对劲儿了吧,但是这个2是符合定义的。

    从这里可以得出一点结论:关于取模的定义,并不能满足语言上的取模运算

    因为在C语言中,余数出现了负数,这显然是不符合定义的,所以现在这里有了一个修订版的定义:

    如果a和d是两个自然数,d非零,可以证明存在两个唯一的整数q和r,满足a = q * d + r 且0 <= |r| < |d|。其中q被称为商,r被称为余数。

    有了这个新的定义,那么C中和python中的“取模”,就都能解释了。

    解释C:-10 = (-3) * 3 + (-1)

    解释python:-10 = (-4) * 3 + 2

    那么是什么造成了C和python这种现象的呢?

    我们一定要知道一点,余数一定是根据商来求得的,余数的变化随商的变化而变化。

    那么上面C和python余数的不同肯定是由于商的不同而造成的,上图中也可以看出,C中负数的商是-3而python负数的商却是-4,那么这是为什么呢?

    因为C和python遵守的取整规则不同,C遵守的是向0取整,而python遵守的是向-∞取整

    取余和取模一样吗?

    取余和取模并不能严格等价(虽然大部分情况差不多)

    先看取整

    取余:尽可能让商进行向0取整

    取模:尽可能让商进行向-∞取整

    所以

    C中%,本质是取余

    python中%,本质才是取模

    理解链:

    对任何一个大于0的数,对其进行向0取整或者-∞取整,取整方向是一致的。故取模等价于取余

    对任何一个小于0的数,对其进行向0取整或者-∞取整,取整方向是相反的。故取模不等价于取余

    而同符号数据得到的商一定是正数,所以此时取模也等价于取余。

    image-20220831225229898

    image-20220831225440361

    通过对比实验,更加验证了,参与取余的两个数据,如果同符号,取模等价于取余。

    如果参与运算的数据,不同符号呢?

    #include 
    int main()
    {
        printf("%d\n", -10 / 3);
        printf("%d\n", 10 / -3);
        printf("%d\n", -10 % 3);
        printf("%d\n", 10 % -3);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    image-20220906221750106

    通过结论可以看到:如果不同符号,参考之前的定义,余数的符号是和被除数的符号相同的。

    然而真的是这样吗?

    image-20220906221908699

    ?????

    这是怎么回事?

    上面讲过了C语言中的求整规则是向0取整,而python中的取整规则是向负∞取整。

    其实导致上图的二者之间的余数的符号的区别,也是由于C语言和python采取的取整规则不同。

    -10 // 3 = -4 ,而-4*3=-12,所以余数是-10 - (-12) = 2,

    10 // -3 = -4 ,而-4*-3=12,所以余数是10 - 12 = -2

    结论:如果参与取余的两个数的符号不同,在采取向0取整的语言中(C语言、C++、Java),余数符号和被除数相同;而在采取向负∞取整的语言中(python),余数符号相对是无规则的,具体符号可根据公式倒退出来。

    总结

    • 浮点数(或者整数相除),是有很多的取整方式的。
    • 如果a和d是两个自然数,d非零,可以证明存在两个唯一的整数q和r,满足a = q * d + r,q为整数,且0 <= |r| < |d|。其中,q被称为商,r被称为余数。
    • 在不同语言中,同一个计算表达式,“取模”结果是不同的。
    • 具体余数r的大小,本质是取决于商q的。而商,又取决于除法计算的时候的取整规则。
    • 取余vs取模:取余尽可能让商进行向0取整。取模尽可能让商进行向-∞取整。
    • 参与取余的两个数据,如果同符号,取模等价于取余。
    • 如果参与取余的两个数的符号不同,在采取向0取整的语言中(C语言、C++、Java),余数符号和被除数相同;而在采取向负∞取整的语言中(python),余数符号相对是无规则的,具体符号可根据公式倒退出来。
  • 相关阅读:
    洛谷题单 【入门3】循环结构
    如何在el-tree懒加载并且包含下级的情况下进行数据回显-01
    Java 网络编程之TCP(五):分析服务端注册OP_WRITE写数据的各种场景(一)
    理解 React 中的 useEffect、useMemo 与 useCallback
    Visual Studio 2022平台的使用
    得了糖尿病,千万不能这么做
    element ui框架(vuex3使用)
    Apache DolphinScheduler使用学习文档
    物体分类__pytorch
    C#调用C++ dll 返回数组
  • 原文地址:https://blog.csdn.net/qq_67569905/article/details/126735516