不同语言的负数取余问题

问题的出现

偶然在leetcode上看到一道这样的题:

Given a 32-bit signed integer, reverse digits of an integer.

翻译成中文就是:

给定一个32位有符号整数,将整数由低位到高位反向输出,例:

输入:1230

返回:321

题目很简单,有很多种实现方式,大概十分钟左右就在脑海中想到了一个自认为最好的解法,之后用电脑实验:

int reverse_num(int x,int res)

{

if(!x) return res;

return reverse_num(x/10,(res*10+x%10));

}

int main()

{

int val=0;

val=reverse_num(-9870,val);

cout<

}

输出结果:

-789

解决!!其实用循环也可以高效地实现,为什么要用递归?因为递归总是能写出简洁优美的代码(其实是为了装X...)。

作为习惯,我再用python实现一遍,同样的代码结构:

def reverse(x,res):

if x==0:

return res

return reverse(x/10,(res*10+x%10))

def main():

num=-9870

res=0

val=reverse(num,res)

print val

输出结果:

RuntimeError: maximum recursion depth exceeded

What the ****!!??

我抬起我颤抖的小手移动着屏幕一行一行地检查代码,发现并没有错。

本以为大局已定,结果被极限反杀,当然不能就这么算了,于是开启debug模式

程序毕竟简单,很快就发现了问题:

>>> print -987/10

-99

>>> print -99/10

-10

>>> print -10/10

-1

>>> print -1/10

-1

>>> print -1/10

-1

>>> print -1/10

-1

到这里,各位观众老爷们应该也看出问题来了,从上述运行结果来看,-987/10的结果居然是-99而不是-98,-1/10的结果是-1,再次执行-1/10的结果当然又是是-1,而递归的退出条件为x=0,这导致递归无限执行,所以栈溢出,对于-1/10为什么等于-1这个结果,当然是既震惊又好奇,接下来便是一探究竟!!

问题的解决

根据资料显示,目前使用的主流除法处理有两种:Truncated Division和Floored Division,这两种除法在对正数除法的处理上是一致的,而在对负数处理上则略有不同。

首先,需要了解的是,对于整数的除法,都满足这个公式:

m=q*n+r

m为被除数

n为除数

r为余

q为商

m,n,q,r都为整数

即:

14=3*4+2

14除以4得3余2

这不是很标准了么,那为什么还会有分歧呢?

正整数的除法当然是没有问题的,但是如果遇到负数的除法,比如

-8/5

结果可以是两种,即:

-8 = 5*(-1)+(-3)

在这种情况下,商为-1,余数为-3

或者:

-8 = 5*(-2)+2

而在这种情况下,商为-2,余数为2

这两种除法的分歧导致了上述的不同语言的不同标准。

官方标准

根据官方资料显示,在C89和C98的标准中,对此并没有做规定,把实现留给了编译器来决定,这会导致什么结果呢?就是我们常说的实践出真知在这种情况下可能会得到一个错误的结果!

不管你的编译器用的是C/C++类标准还是python类标准,你得出的结论就是单一标准,写出来的代码在另一个编译器下并不具有移植性。

思考

由此引发博主的一个思考:有时候在研究这类计算机问题时,我们不能单单以某个平台上的实验结果作为标准答案,这是有所偏颇的,编译工具链(脚本解释器)常常有多个版本,而单一平台却无法覆盖所有编译器(脚本解释器)版本,可能仅仅是选择了最通用的版本或者是从多个分歧版本中选其一。

统一标准

在C99的标准中,明确规定了"truncation toward zero",即向0取整。在这个模式下,负整数除法中,商为负数时,小数部分是往靠近0的方向进行取整,即舍弃小数部分,C++和Java则沿用了C的方式,还是那个例子:

-8/5=-1.6

商为-1.6,但是因为是整数除法,小数部分向0取整,商为-1,所以余数为-3,即:

-8 = 5*(-1)+(-3)

而在python中,应用的则是小数部分向进1的方向取整,举个例子:

-8/5=-1.6

商为-1.6,但是因为是整数除法,小数部分向进1取整,商为-2,所以余数为2,即:

-8 = 5*(-2)+2

对于两种不同语言的除法实现,我们已经有了基本的了解,但是事情就这样结束了吗?并没有!!

上述讨论的仅仅是

正整数/正整数

负整数/正整数

还有两种情况怎么能漏呢?

负整数/负整数

正整数/负整数

对于负整数/负整数的除法,这两种除法有没有区别呢?

既然在C99之后对于C带符号整数除法的标准中已经统一,我们还是可以选择用上机运行代码的方式来检验

正整数/负整数

C++代码片段:

int div = 8/-5;

int mod = 8%-5;

int div1= 5/-8;

int mod1= 5%-8;

cout<

cout<

运行结果:

8/-5=-1

8%-5=3

5/-8=0

5%-8=5

python代码片段:

print "%s %d" %("8/-5=",8/-5)

print "%s %d" %("8%-5=",8%-5)

print "%s %d" %("5/-8=",5/-8)

print "%s %d" %("5%-8=",5%-8)

运行结果:

8/-5=2

8%-5=-2

5/-8=-1

5%-8=-3

在正整数/负整数的示例中,C/C++和python的标准如上面所述,商为负数时,商的小数部分一个舍一个入,导致的结果也不一样。

负整数/负整数

C/C++代码片段:

int div = -8/-5;

int mod = -8%-5;

int div1= -5/-8;

int mod1=-5%-8;

cout<

cout<

运行结果:

-8/-5=1

-8%-5=-3

-5/-8=0

-5%-8=-5

python代码片段:

print "%s %d" %("-8/-5=",-8/-5)

print "%s %d" %("-8%-5=",-8%-5)

print "%s %d" %("-5/-8=",-5/-8)

print "%s %d" %("-5%-8=",-5%-8)

运行结果:

-8/-5=1

-8%-5=-3

-5/-8=0

-5%-8=-5

两种语言的输出结果是一样的,很多盆友就开始有点懵了,不是说标准不一样,小数部分取整的方向不一样吗?

如果你仔细看上面的例子,就会发现一个前提条件,商为负数时,取整有差异,而这里商为正数,如-5/-8=0.625,则按照正整数的除法的统一规则(别忘了!正整数的处理两种语言是一致的):

商取0,则:

-5 = 0*(-8)+(-5)

余数为-5

简单总结

python和C/C++/JAVA在商为负数的除法处理上有两种标准,在python中,商的小数部分进位,而在C/C++/JAVA中,商的小数部分被舍弃。(网上资料显示Ruby的处理和python一个标准,博主未进行测试,有兴趣的小伙伴可以尝试一下)

好了,关于不同语言的有符号整数除法的讨论就到此为止啦,如果朋友们对于这个有什么疑问或者发现有文章中有什么错误,欢迎留言

原创博客,转载请注明出处!

祝各位早日实现项目丛中过,bug不沾身.

负数对2取余_不同语言的负数取余问题相关推荐

  1. 2字节取值范围_C语言整数的取值范围以及数值溢出

    short.int.long 是C语言中常用的三种整数类型,分别称为短整型.整型.长整型.在现代操作系统中,short.int.long 的长度分别是 2.4.4 或者 8,它们只能存储有限的数值,当 ...

  2. c语言 int的取值范围,C语言int的取值范围 ?

    C语言int的取值范围在32/64位系统中都是32位,范围为-2147483648~+2147483647,无符号情况下表示为0~4294967295. C/C++编程语言中,int表示整型变量,是一 ...

  3. php用wordanalysis抓取姓名_利用vba查询/抓取 外部数据

    考虑这么一个excel文件,路径为:"E:dataEdata.xlsx",样式如封面图片所示 想要在其他excel文件中,通过代码直接抓取Edata.xlsx中想要的数据,做法如下 ...

  4. java分页抓取数据_网页分页数据抓取的几种方式

    相信所有个人网站的站长都有抓取别人数据的经历吧,目前抓取别人网站数据的方式无非两种方式: 一.使用第三方工具,其中最著名的是火车头采集器,在此不做介绍. 二.自己写程序抓取,这种方式要求站长自己写程序 ...

  5. 回归分析什么时候取对数_冬蜜什么时候取,冬天取蜂蜜的方法

    大家好,我现在分享的是,在冬天是在什么时候取蜜! 冬天在我们南方,取蜜时间是十一月到十二月的时候, 只要温度达到15度以上,蜂蜜封盖了就可以取蜜了, 并且在冬天我们只能取一次,最晚取蜜的时间在十二月的 ...

  6. 抓取 虾米_【虾米音乐抓取】巧用iTools抓取虾米歌曲_玛雅作文网

    作文「巧用iTools抓取虾米歌曲」共有 397 个字,其中有 293 个汉字,62 个英文,3 个数字,39 个标点符号.作者佚名,请您欣赏.玛雅作文网荟萃众多优秀学生作文,如果想要浏览更多相关作文 ...

  7. python爬虫爬取微信_如何使用 Python 爬取微信公众号文章

    我比较喜欢看公众号,有时遇到一个感兴趣的公众号时,都会感觉相逢恨晚,想一口气看完所有历史文章.但是微信的阅读体验挺不好的,看历史文章得一页页的往后翻,下一次再看时还得重复操作,很是麻烦. 于是便想着能 ...

  8. 如何用python爬取数据_如何使用python爬取知乎数据并做简单分析

    原标题:如何使用python爬取知乎数据并做简单分析 一.使用的技术栈: 爬虫:python27 +requests+json+bs4+time 分析工具: ELK套件 开发工具:pycharm 数据 ...

  9. python爬虫爬取豆瓣_爬虫,从爬取豆瓣开始

    1 爬虫概述 当初第一次接触python,听的最多的就是爬虫爬虫,搞得我一脸蒙蔽,因为我从来都没听过这么新颖的词,而且我还天真的以为是不是python长得像一条小虫子,所以才叫爬虫. 直到后来经过不断 ...

最新文章

  1. 前后端分离项目_七个开源的 Spring Boot 前后端分离项目,一定要收藏
  2. 计算机专业的书普遍都这么贵,Github上的计算机电子书很多~~~
  3. Android --- Add Google Maven repository and sync project
  4. 全球及中国数字出版产业投资产值与运营模式咨询报告2022版
  5. armadillo 使用注意 越界不报错
  6. Python小数据池
  7. 用react和electron实现文件树组件(上)
  8. sql删除主键_产品经理的第一节SQL课——ID到底是干什么的?!
  9. 手机端网页开发的两个重要设置
  10. 升降压斩波电路matlab,升降压斩波电路的MATLAB 仿真.pptx
  11. ubuntu packege下载网址
  12. oracle 修改字段类型方法
  13. 从三级界面直接跳回一级调用方法
  14. 几种常见的 Kafka 集群监控工具
  15. 使用slickedit代替sourceinsight
  16. opensuse13.1 安装 SqliteMan
  17. S1200和博途软件通讯测试,S7-1200博途和组态王软件通讯(高端培训).pdf
  18. 用计算机证明的定理,定理机器证明
  19. Euler formula
  20. 玛格丽特·米德2019下半年EI会议与人格理论初探

热门文章

  1. 携程二季度净利润降55% 促销被指“玩烟幕弹”
  2. 不同浏览器对URL最大长度的限制
  3. Android Gallery组件详解(转)
  4. Kubernetes Service 对象的使用
  5. Android应用开发—TextView的动态创建
  6. Angular 2 Output
  7. 致广州达到信息技术有限公司
  8. 【记录】ASP.NET IIS ISAPI_Rewrite
  9. 表格排序——tablesorter.js使用(支持中文排序)
  10. Memcached进程挂掉自动重启脚本