## Java取模(%)运算

> [上篇文章](https://yebukong.com/article/1101070795486109697.html "上篇文章") 提到了java取模会出现负数的问题,发现自己对这块理解有偏差,这里记录一下。

### 介绍

取模运算,应该都用过,我之前的简单理解就是小学学过的取余运算,在参考了一些资料后,发现理解有偏差。这里放一段维基百科的介绍:

> 模除(又称模数、取模操作、取模运算等,英语:modulo 有时也称作 modulus)得到的是一个数除以另一个数的余数。

>给定两个正整数:被除数 a 和除数 n,a modulo n (缩写为 a mod n)得到的是使用欧几里德除法时 a/n 的余数。 举个例子:计算表达式 "5 mod 2" 得到 1,因为 5÷2=2...1(5 除以 2 商 2 余 1);而 "9 mod 3" 得到 0,因为 9÷3=3...0;注意:如果使用计算器做除法,不能整除时,你不会得到商,而是会得到一个小数,如:5÷2=2.5。

>虽然通常情况下 a 和 n 都是整数,但许多计算系统允许其他类型的数字操作,如:**对浮点数取模**。一个整数对 n 取模的结果范围为: 0 到 n − 1(a mod 1 恒等于 0;a mod 0 则是未定义的,在编程语言里可能会导致除零错误)。**当 a 和 n 均为负数时,通常的定义就不适用了,不同的编程语言对结果有不同的处理。**

可以看到编程语言中的取模操作和小学学过的取余并不是完全一样的,它对数学中的取余(我小学记忆中的)做了扩展,推广到了浮点数以及负数,所以**取模操作结果是会出现负数的**,上面还提到了在不同语言有不同处理,就导致不同语言下取模结果的不一致。

### 取整方式

在理解取模运算过程前,需要先看看取整的不同。当余数不为 0 的时候,取整(/)有几种不同处理方式:

- **向上取整**

向 +∞ 方向取最接近精确值的整数,也就是取比实际结果稍大的最小整数,也叫 Ceiling 取整,例如:`17 / 10 = 2`,`5 / 2 = 3`, `-9 / 4 = -2`;

- **向下取整**

向 -∞ 方向取最接近精确值的整数,也就是取比实际结果稍小的最大整数,也叫 Floor 取整,例如:`17 / 10 = 1`,`5 / 2 = 2`,`-9 / 4 = -3`;

- **向零取整**

向 0 方向取最接近精确值的整数,换言之就是舍去小数部分,因此又称截断取整(Truncate),例如:`17 / 10 = 1`,`5 / 2 = 2`, `-9 / 4 = -2`;

### 负数取模验证

不同编程语言中,对取模操作有truncate(截断除法)和floored(取底除法)等。truncate 除法用到了上面的向零取整,而floor除法用到了上面的向下取整。JAVA中使用的是truncate,而Python使用的是floor。下面我们验证一下:

- JAVA

```java

System.out.println( 5 % 3); // 2

System.out.println( 3 % 5); // 3

System.out.println(-5 % 3); //-2

System.out.println( 5 % -3); // 2

```

分析过程

`5 % 3 = 5 - trunc(5/3)*3 = 5 - 1*3 = 5-3 = 2`

`3 % 5 = 3 - trunc(3/5)*5 = 3 - 0*5 = 3-0 = 3`

`-5 % 3 = -5 - trunc(-5/3)*3 = -5 - (-1)*3 = -5-(-3) = -2`

`5 % -3 = 5 - trunc(5/-3)*(-3) = 5 - 1*(-3) = 5-(-3) = 2`

- Python

```python

print 5 % 3 ; # 2

print 3 % 5 ; # 3

print -5 % 3 ; # 1

print 5 % -3 ; #-1

```

分析过程

`5 % 3 = 5 - floor(5/3)*3 = 5 - 1*3 = 5-3 = 2`

`3 % 5 = 3 - floor(3/5)*5 = 3 - 0*5 = 3-0 = 3`

`-5 % 3 = -5 - floor(-5/3)*3 = -5 - (-2)*3 = -5-(-6) = 1`

`5 % -3 = 5 - floor(5/-3)*(-3) = 5 - (-2)*(-3) = 5-6 = -1`

### 浮点数取模

浮点数取模和整数计算方式是一样的,但是在Java中却有另一个问题:**精度**

```java

System.out.println(5.3 % 3); // 2.3

System.out.println(3 % 5.3); // 3.0

System.out.println(5.3 % 3.2); // 2.0999999999999996

```

分析

`5.3 % 3 = 5.3 - trunc(5.3/3)*3 = 5.3 - 1*3 = 5.3-3 = 2.3`

`3 % 5.3 = 3 - trunc(3/5.3)*5.3 = 3 - 0*5.3 = 3-0 = 3`

`5.3 % 3.2 = 5.3 - trunc(5.3/3.2)*3 = 5.3 - 1*3.2 = 5.3-3.2 = 2.1`

可以看到某种情况下的浮点数取模时,出现了精度问题,这里推荐使用`java.math.BigDecimal`类操作,且构造方法选择 `public BigDecimal(String val) `,具体取模使用`public BigDecimal[] divideAndRemainder(BigDecimal divisor)`方法,返回值为数组,[0]位置存放商,[1]存放余数;

```java

BigDecimal x = BigDecimal.valueOf(5.3);

BigDecimal y = BigDecimal.valueOf(3.2);

BigDecimal[] z = x.divideAndRemainder(y);

System.out.println(z[0]); // 1

System.out.println(z[1]); // 2.1

```

至于精度为何出现丢失,则又是一个坑了。

### 参考链接

> https://zh.wikipedia.org/wiki/%E6%A8%A1%E9%99%A4

> https://www.jianshu.com/p/452c1a5acd31

> https://blog.csdn.net/zhige_me/article/details/80980566

:bowtie:

------------

> 本文由 [叶不空](https://yebukong.com "叶不空") 创作,采用 [知识共享署名 4.0 国际许可协议](https://creativecommons.org/licenses/by/4.0/ "知识共享署名 4.0 国际许可协议")进行许可,转载请附上链接!

> 本文链接: [https://yebukong.com/article/1101707715186593793.html](https://yebukong.com/article/1101707715186593793.html "JAVA中取模的问题")

java取模负数_JAVA中取模的问题相关推荐

  1. java 二进制 表示负数_java中的负数表示

    下面这行代码的输出是什么? 1 public static void main(String[] args) { 2 System.out.println(0xffffffff); 3 } 下面两行代 ...

  2. java 从一列对象中取其中某一列字段

    java 从一列对象中取其中某一列字段 Class Obj{int id;string name;public Obj(id,name){} }List<Obj> list = Array ...

  3. java主键后四位顺序号_JAVA中取顺序号 (转)

    JAVA中取顺序号 (转)[@more@] 在写系统的时候,经常会遇到用一个唯一的键值去票识一个对象,如数据库中一条记录的主键,文件的文件名 等.常用的做法将这个键值从1,2,3--这样一值递加下去, ...

  4. java取邮箱前缀_java抓取网页或文件中的邮箱号码

    java抓取网页或文件中的邮箱号码 发布时间:2020-10-18 08:58:32 来源:脚本之家 阅读:69 作者:java大渣渣 本文实例为大家分享了java抓取邮箱号码的具体代码,供大家参考, ...

  5. java取网页数据_Java抓取网页数据(原来的页面+Javascript返回数据)

    转载请注明出处. 有时候因为种种原因.我们须要採集某个站点的数据.但因为不同站点对数据的显示方式略有不同! 本文就用Java给大家演示怎样抓取站点的数据:(1)抓取原网页数据.(2)抓取网页Javas ...

  6. java构建xml参数_Java中使用XML创建EMAIL模板

    邮件模板 让我们来看看邮件模板的格式.模板是XML文件,它包含一个根元素和一系列根的子元素.根元素是.必要的子元素是, , 和 .可选的子元素是 , , 和 .如果你使用过邮件系统,那么你可以推导出这 ...

  7. java 共享锁 独占锁_java中的公平锁、非公平锁、可重入锁、递归锁、自旋锁、独占锁和共享锁...

    一.公平锁与非公平锁 1.1 概述 公平锁:是指多个线程按照申请锁的顺序来获取锁. 非公平锁:是指在多线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取到锁,在高并发的情 ...

  8. python字典取值_python 字典中取值的两种方法小结

    python 字典中取值的两种方法小结 如下所示: a={'name':'tony','sex':'male'} 获得name的值的方式有两种 print a['name'],type(a['name ...

  9. java int 大小吗_java中int和Integer比较大小

    Integer是int的封装对象,两个对象==比较的是栈的值 Integer a = new Integer(1); Integer b = new Integer(1); a与b存的是Integer ...

最新文章

  1. easyexcel生成excel_阿里JAVA解析Excel工具easyexcel
  2. php乱码解决方案,php中文乱码问题的4种解决方案
  3. 如何将freemarker文件转化为html文件
  4. 有向图强连通分量tarjan算法
  5. 小程序内嵌h5页面分享_微信小程序webview内页面分享
  6. webService(一)开篇
  7. 2020年天津市二级分类土地利用数据(矢量)
  8. 多实例学习PCNN在关系抽取中的应用
  9. WPF实现DoEvents
  10. 云编程和软件环境 、物联网的关键技术
  11. 基于C++实现考试报名系统
  12. 对车辆路试数据集mtcars进一步分析_【案例】图解电磁阀及其故障诊断分析
  13. 【开发日志-已归档】2020-09
  14. 记事本html写代码运行挠脚心,tk挠脚心
  15. 既然Talk is cheap, 那么就用代码教你如何进行正交设计
  16. linux centos7 镜像下载
  17. Docker多容器搭建LNMP
  18. 风控基础指标之决策树的特征选择
  19. linux 卸载apt包,apt - 如何删除Ubuntu中损坏的软件包 - Ubuntu问答
  20. 二阶RC锂电池模型simulink模型

热门文章

  1. Pocsuite3渗透测试框架编写POC和EXP脚本
  2. 推进产学研创新发展,网易云信与杭州电子科技大学计算机学院达成合作
  3. 关于MySQL中文乱码显示???解决办法
  4. 陶瓷电容的ESR-谐振频率去哪儿查?
  5. 基于BlenderGIS插件制作智慧城市三维场景
  6. 985计算机报录比高的学校,985/211高报录比院校
  7. 2022年保育员理论知识(高级)考试单选题专项训练及答案
  8. java微信公众号开发教程_微信公众平台开发教程(java版本含代码) 中文PDF版 3.13MB...
  9. 结构计算之多目视觉(三)
  10. 交易中 你的加仓策略是怎样的?背后的逻辑是什么?