取模和求余运算

文章目录

  • 背景
  • 探究
  • 总结

被除数 dividend 用 a 表示;
除数 divisor 用 b 表示;
商 quotient 用 q 表示;
余 remainder 用 rem 表示;
模 modulo 用 mod 表示。

背景

最近在一道 Java 习题中,看到这样的一道题:

What is the output when this statement executed:
System.out.printf(-7 % 3);

正整数的取余运算大家都很熟悉,但是对于负数、实数的取余运算,确实给人很新鲜的感觉。于是我对此进行了一些探索。我发现,这里面还是颇有一点可以探索的东西的。

探究

首先,看看自然数的取模运算(定义1):

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

我们计算下 (-7) % 3,这个表达式正常情况下是求余数,计算过程如下图所示:
在这里插入图片描述
按自然数的除法运算规则,得到商值:-3,余数:2,且完全满足上述两个关系表达式:-7 = (-3)*3 + 20 ≤ 2 < 3

那么,各种编程语言和计算器是否是按照这样的规则计算呢?下面列举了几个程序运算的结果:

程序语句输出
C++(G++ 编译)cout << (-7) % 3;-1
Java(1.6)System.out.println((-7) % 3);-1
Python 2.6(-7) % 32
百度计算器(-7) mod 32
Google 计算器(-7) mod 32

可以看到,结果特别有意思。这个问题是百家争鸣的。看来我们不能直接把自然数的法则用在负数上。实际上,在整数范围内,自然数的求余法则并不被很多人所接受,大家大多认可的是下面的这个(定义 2):

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

可以看到,上述定义导致了有负数的求余并不是我们想象的那么简单,根据上述的定义,我们计算下 (-7) % 3,计算过程如下图所示:

在这里插入图片描述
注:按自然数的除法运算规则(定义 1),商和除数的乘积要小于等于被减数才行,但是很多程序并没有遵循定义 1的运算规则,所以依据定义 2的规则,商和除数的乘积可以大于被减数,使用上图所示的技巧计算时,商与除数的乘积必须接近被减数,然后计算得到结果,再验证定义 2的两个表达式是否成立。当然了如果你直接使用定义 2的两个表达式硬套出 pr 的值也可以,不过不推荐。

如上图所示可以得到两组结果:

  1. -3,余 2
  2. -2,余 -1

这两组结果都满足“定义 2”的表达式,也就是说 2 和 -1 都是 (-7) % 3 正确的结果, 所以问题来了到底余数是 2 还是 -1 呢?这个问题最后会给出答案。

我们把 2 和 -1 分别叫做正余数和负余数。通常,整数 a 除以整数 b 时,如果得到正余数为 r1,负余数为 r2,那么存在这样的关系:r1 = r2 + b

看完了 (-7) % 3,下面我们来看一看 7 % (-3) 的情况。根据定义 2,计算过程如下图所示:
在这里插入图片描述

如上图所示,可以得到两组结果:

  1. -2,余 1
  2. -3,余 -2
语言语句输出
C++(G++ 编译)cout << 7 % (-3);1
Java(1.6)System.out.println(7 % (-3));1
Python 2.67 % (-3)-2
百度计算器7 mod (-3)-2
Google 计算器7 mod (-3)-2

从中我们看到几个很有意思的现象:

  1. Java 紧随 C++ 的步伐,而 Python、Google、百度步调一致。难道真是物以类聚?联想一下,Google 一直支持 Python,Python 也颇有 Web 特色的感觉,而且 Google Application Engine 也用的 Python,国内的搜索引擎也不约而同地按照 Google 的定义进行运算。

  2. 可以推断,C++ 和 Java 通常会尽量让商更大一些。比如在 (-7) mod 3中,他们以 -2 为商,余数为 -1。在 Python 和 Google 计算器中,尽量让商更小,所以以 -3 为商。在 7 mod (-3) 中效果相同:C++ 选择了 3 作为商,Python 选择了 2 作为商。但是在正整数运算中,所有语言和计算器都遵循了尽量让商小的原则,因此 7 mod 3 结果为 1 不存在争议,不会有人说它的余数是 -2。

上述的认知其实是错误的,纠正如下:
Java 和 C++ 的 % 是求余运算符,求余遵循让商向 0 靠近的原则(即商向 0 方向舍入取整),也就是说求余是取 q 更趋近 0 时的 r,所以 Java 和 C++ 计算 7 % (-3) 的结果是 1,因为商 -2 更靠近 0;而 Python 的 % 是取模运算符,取模遵循让商向负无穷靠近的原则(商向无穷小方向舍入取整,向负无穷方向舍入取整),也就是说取模是取 q 更趋近无穷小(负无穷)时的 r,所以 Python、百度、谷歌输出的结果是 -2,因为 -3 更靠近负无穷的方向。

如果按照第二点的推断,我测试一下 (-7) % (-3),结果应该是前一组语言(C++,Java)返回 2,后一组返回 -1。(请注意这只是假设)

于是我做了实际测试:

语言语句输出
C++(G++ 编译)cout << -7 % (-3);-1
Java(1.6)System.out.println(-7 % (-3));-1
Python 2.6-7 % (-3)-1
百度计算器-7 mod (-3)-1
Google 计算器-7 mod (-3)-1

结果让人大跌眼镜,所有语言和计算机返回结果完全一致。

这个眼镜也白跌了,上述结果完全符合求余时商向 0 靠近的原则,取模时商向负无穷方向靠近的原则,所以都输出 -1 根本就在预料之内。

我们看下 (-7) % (-3) 的运算过程,如下图所示:
在这里插入图片描述
所以根据求余时商向 0 靠近的原则,取模时商向负无穷方向靠近的原则,求余和取模的商都选择 2,所以余数和模数都是 -1,结果输出的都是 -1

总结

我们由此可以总结出下面两个结论:

  1. 对于任何同号的两个整数,其取余结果没有争议,所有语言的运算原则都是使商尽可能小。
  2. 对于异号的两个整数,C++/Java语言的原则是使商尽可能大,很多新型语言和网页计算器的原则是使商尽可能小。

这个总结也是错误的。

最后总结:除法运算遵循定义 2的规则,求余是取 q 更趋近 0 时的 r,取模是取 q 更趋近负无穷的 r,如果被除数和除数符号相同,因为取相同的商值,所以余数和模数相同,被除数和除数的符号不同,则余数和模数会不同。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/333950.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

图像重建算法_基于深度学习图像重建算法(DLIR)对CT图像质量和剂量优化的研究:体模实验...

编者按&#xff1a;今年Jol Greffier博士等在European Radiology (IF 4.1)上发表了题为《Image quality and dose reduction opportunity of deep learning image reconstruction algorithm for CT: a phantom study》的文章&#xff0c;通过与混合迭代重建算法(IR)对比&#x…

oracle中sp怎么写_校招简历中的实习和项目经历该怎么写?

在2017年和2018年&#xff0c;我针对校招生的简历分别写了两篇文章&#xff0c;一篇是《秋招中的互联网产品、运营岗&#xff0c;该如何准备&#xff1f;》&#xff0c;里面有一部分内容是关于简历的准备&#xff1b;另一篇是《如何用产品思维解决简历问题&#xff1f;七步简历…

自然语言处理领域基本概念笔记

自然语言处理 词向量&#xff1a; 自然语言处理问题要转化为机器学习的问题&#xff0c;首先就要把单词数学化表示&#xff0c;就是用n维实数向量来代表一个单词。 对话系统 对话系统发展历程的三个阶段&#xff1a; 1.基于符号规则和模板的对话系统 2.基于统计机器学习的…

如何理解 inode

一、inode是什么&#xff1f; 理解inode&#xff0c;要从文件储存说起。 文件储存在硬盘上&#xff0c;硬盘的最小存储单位叫做"扇区"&#xff08;Sector&#xff09;。每个扇区储存512字节&#xff08;相当于0.5KB&#xff09;。 操作系统读取硬盘的时候&#xff0c…

java api 开发_Java开发人员应该知道的前20个库和API

java api 开发优秀且经验丰富的Java开发人员的特征之一是对API的广泛了解&#xff0c;包括JDK和第三方库。 我花了很多时间来学习API&#xff0c;尤其是在阅读了Effective Java 3rd Edition之后 &#xff0c;Joshua Bloch建议在Java 3rd Edition中使用现有的API进行开发&#x…

Linux 系统的硬链接和软链接详解

文章目录什么是链接链接用来干什么的硬链接和软链接的区别硬链接和软链接的图示总结我们知道文件都有文件名与数据&#xff0c;这在 Linux 上被分成两个部分&#xff1a;用户数据 (user data) 与元数据 (metadata)。用户数据&#xff0c;即文件数据块 (data block)&#xff0c;…

okta-spring_通过Okta的单点登录保护Spring Boot Web App的安全

okta-spring“我喜欢编写身份验证和授权代码。” 〜从来没有Java开发人员。 厌倦了一次又一次地建立相同的登录屏幕&#xff1f; 尝试使用Okta API进行托管身份验证&#xff0c;授权和多因素身份验证。 您可以使用SpringBoot和Okta在不到20分钟的时间内启动具有完整用户身份和…

apache poi_将HTML转换为Apache POI的RichTextString

apache poi1.概述 在本教程中&#xff0c;我们将构建一个将HTML作为输入的应用程序&#xff0c;并使用提供HTML的RichText表示形式创建Microsoft Excel工作簿。 为了生成Microsoft Excel工作簿&#xff0c;我们将使用Apache POI 。 为了分析HTML&#xff0c;我们将使用Jericho。…

Windows 下有哪些逆天的软件?

文章目录逆天软件系列1&#xff1a;Everything逆天软件系列2&#xff1a;Total Commander逆天软件系列3&#xff1a;Snipaste逆天软件系列4&#xff1a;Microsoft To-Do逆天软件系列5&#xff1a;ScreenToGIF逆天软件系列6&#xff1a;Geek Uninstaller逆天软件系列7&#xff1…

oracle adf_Fn函数来构建Oracle ADF应用程序

oracle adf在我之前的一篇文章中&#xff0c;我描述了如何创建一个Docker容器作为ADF应用程序的构建器。 在这里&#xff0c;我将展示如何将此容器用作 在FN平台的功能 。 首先&#xff0c;让我们更新容器&#xff0c;使其符合功能要求&#xff0c;这意味着可以将其作为接受某…

Sublime Text 4.0 4102 安装插件的问题

文章目录安装包控件&#xff08;Package Control&#xff09;如何安装插件打开 Install Package 面板搜索和安装插件无法打开 Install Package 面板的问题故障排除安装包控件&#xff08;Package Control&#xff09; 要安装插件&#xff0c;必须先安装 Package Control&#…

maven安装教程安装教程_Maven教程之春

maven安装教程安装教程1.简介 在这篇文章中&#xff0c;我们将演示如何针对非常特定的用例对Spring使用Maven依赖项。 我们使用的所有库的最新版本都可以在Maven Central上找到。 对于一个有效的构建周期来说&#xff0c;了解Maven依赖项的工作方式以及如何对其进行管理很重要…

C++核心编程笔记

C核心编程1 内存分区模型1.1 程序运行前1.2 程序运行后1.3 new操作符2 引用2.1 引用的基本使用2.2 引用注意事项2.3 引用做函数参数2.4 引用做函数返回值2.5 引用的本质2.6 常量引用3 函数提高3.1 函数默认参数3.2 函数占位参数3.3 函数重载3.3.1 函数重载概述3.3.2 函数重载注…

Sublime Text 如何设置组合快捷键

Sublime 有个功能叫再次缩进&#xff08;Reindent&#xff09;&#xff0c;我就以这个功能为例讲下如何设置快捷键&#xff0c;这个功能的菜单路径是&#xff1a;Edit ➠ Line ➠ Reindent&#xff0c;有人说这个再次缩进可以格式化代码&#xff0c;扯淡&#xff0c;缩进两下也…

GAN对抗生成网络原始论文理解笔记

文章目录论文&#xff1a;Generative Adversarial Nets符号意义生成器(Generator)判别器(Discriminator)生成器和判别器的关系GAN的训练流程简述论文中的生成模型和判别模型GAN的数学理论最大似然估计转换为最小化KL散度问题定义PGP_GPG​全局最优论文&#xff1a;Generative A…

okta使用_使用Okta的单点登录保护您的Vert.x服务器

okta使用“我喜欢编写身份验证和授权代码。” 〜从来没有Java开发人员。 厌倦了一次又一次地建立相同的登录屏幕&#xff1f; 尝试使用Okta API进行托管身份验证&#xff0c;授权和多因素身份验证。 Vert.x是Spring生态系统中增长最快的元素之一&#xff0c;保护Vert.x服务器可…

java ee打印功能_Java EE 8的前5个新功能

java ee打印功能备受期待的Java Enterprise Edition 8版本具有两个令人兴奋的新API&#xff08;JSON绑定1.0和Java EE Security 1.0&#xff09;&#xff0c;并且对当前API进行了改进&#xff08;JAX-RS 2.1&#xff0c;Bean Validation 2.0&#xff0c;JSF 2.3&#xff0c;CDI…

javafx 打印控件_Java的新视差控件(JavaFX)

javafx 打印控件介绍 视差是一种视觉效果&#xff0c;您可以组合以不同速度移动的两个分层图像以获得深度感。 想想一下&#xff0c;当您在道路上行驶时&#xff0c;您会看到附近的树木在快速移动&#xff0c;而距离较远的树木将沿同一方向移动但速度较慢&#xff0c;结果是您…

编译原理总概述笔记

编译原理编译原理程序设计语言分类翻译编译解释编译的转换过程两阶段的转换三阶段的转换编译程序的工作词法分析语法分析中间代码生成优化目标代码生成表格与表格管理出错处理语句翻译实例过程编写编译程序方式编译原理 是介绍高级程序设计语言变换成计算机硬件所能识别的机器…

apache.camel_Apache Camel 2.21发布–新增功能

apache.camel我们刚刚发布了Apache Camel 2.21&#xff0c;我将在此博客中重点介绍值得注意的更改。 此版本不支持Spring Boot2。对Spring Boot 2的支持将在Camel 2.22中提供&#xff0c; 我们计划在2018年夏季之前发布。 1&#xff09;处理大型JMS消息 我们在JMS组件中添加了…