从一道招聘考题谈起

简介:

润乾研发部在招聘时有一个笔试题:

1/2, 1/5, 1/20, 1/64, 1/125都可以写成有限小数,而1/3, 1/7, 1/15, 1/24则必须写成无限循环小数。请指出能写成有限小数的分数具有什么样的特征?在什么情况下1/5也会被写成无限循环小数?


坦白地说,这个题的通过率并不高,不到一半吧。

仔细分析题目中的分母,我们会发现,这些能写成有限小数的分母,分解质因数之后就都只有2和5这两种质因子。而那些不能写成有限小数的分母分解因数后则含有不是2和5的质因子。也就是说,只有当分母可以写成2^n*5^m这种形式时才可能写成有限小数。

这是为什么呢?

其实很简单,因为我们把这些分数写成小数时使用的是10进制。而10=2*5,对于任何一个形如2^n*5^m的分线,设k=max(n,m),则把这个数乘以10^k,即2^k*5^k,就一定会变成一个整数了,也就是说,这个数的小数部分最多只有k位,这当然是有限的。而如果分母中含有其它质因子,则不可能有个k使得让这个数乘以10^k后变成整数,也就只能是无限小数了。

那么,什么时候1/5会写成无限小数呢?

如果我们采用的数制不是5的倍数,就会发现这种情况了,比如计算机普遍采用的2进制,这时候1/5会写成一个无限循环小数。

但是,我们的机器都是有限位数的,不可能真地表示一个无限位的数,只能舍弃后面的位。也就是说,1/5在计算机中是不能被精确表示的!

这个现象会影响到我们的程序设计。

比如我们写一段这样的代码:

double x = 0;

for ( int i=0; i<=1000; i++ )

x+=0.001;

我们现在想当然地认为x会等于1,然而并不是!在我的机器上的Java环境中跑出来x=1.0000000000000007,一个奇怪的结果。

为什么要强调的是我的机器上的Java环境呢?因为浮点数的表示和CPU以及编译器都有关系,换一台机器或编译器就可能会跑出不同的结果。

而且,结果也不总是变大。如果我们改成反复加1万次(即用i<=10000),得到的x=9.999999999999897,没啥规律可言。

计算结果和预期值的误差其实非常小,会有什么后果吗?

如果是用于后续再计算(比如再加减乘除等),这个误差确实不重要,可以不去理它。但有时候我们可能会把它用于比较,再根据比较结果做下面的动作。比如我们预期这个x应当等于1,如果后续是这样的代码:

if ( x==1.0 ) {…} else {…}

那就会执行出错误的结果了,这个bug还很难被发现,代码逻辑上看完全没有问题。而且由于前面所说的该现象出现的随机性,也不是对任何数都一定会产生这个结果,很可能在测试时没碰到而被放过了。

那么,怎么避免这个错误呢?

在涉及浮点数相等比较时,一般不要直接使用精确地相等去判断,而要看差的绝对值是否小于某个很小的数。代码写成:

if ( abs(x-1.0) < 1E-10) ….

就不会错了。

如果目标比较值是整数,那还可以将计算结果转换成整数,整数在2进制下都可以精确表示,可以放心地用==去判断,但注意要做四舍五入,即

if ( int(x+0.5) == 1 ) ….

如果直接用int(x)取整,在计算结果因舍位误差小于预期结果时,也会出错。

比较值不是整数,但能保证一定位数的精度,可以先用乘法再转换成整数:

if ( int(x*1000+0.5) == 1000 ) …

还有的办法就是避免使用浮点数。

我们知道,现代数据库都提供有decimal数据类型,其实就是这么个思路。decimal可以称为定点数,其小数部分也是按位数存储的,计算时能够精确表示,不会有上述的误差。但是,decimal不是现代CPU直接支持的数据类型,需要数据库软件来自行实现其计算逻辑,性能就会差出很多。所以,在不需要这种精度时(比如只是计算总数或平均值等),我们还是把它转换成浮点数来计算更好一点。集算器在从数据库取数时提供了@d选项就是为了自动把decimal转成浮点数获得高性能,但需要冒不精确的风险,所以做成选项由程序员自行根据场景决定。

实际业务中,需要精确比较的浮点数常常是金额。大多数国家的货币都是两位小数的,这样我们可以将数值先乘以100转换成整数再存储,而整数的运算和比较都是精确的,不会出现这种问题,但是在显示时需要再转换回来变成用户习惯的两位小数写法。CPU计算和处理整数的性能也非常高,64位的CPU能够表示的整数范围在±2^63,即使除以100也还有16位整数部分,大约是1千万亿,这对于相当多的场景都够用了。这样就即有精确度又有高性能。


原文发布时间为:2018-08-28

本文作者:蒋步星

本文来自云栖社区合作伙伴“数据蒋堂”,了解相关信息可以关注“数据蒋堂”。

相关文章
|
5月前
|
存储 JSON NoSQL
绝密!快速解决秋招简历项目难题的技术
绝密!快速解决秋招简历项目难题的技术
|
小程序 测试技术
通关小技巧!有了软件测试题库的加持,面试都有底气
面试,是决定求职者是否能进入到自己心仪岗位的敲门砖,而对于很多第一次参加软件测试面试的求职来说,想要通过面试就得经过hr的初面以及技术官技术面,前者还好说,可以根据情况临场发挥,而后者,就是实打实的专业性问题。
136 0
|
算法 网络协议 架构师
程序员投简历到底是“精投”好,还是“广撒网”好?
一、简历 如何准备简历? 按照我的模板,大概是这样划分的。
程序员投简历到底是“精投”好,还是“广撒网”好?
|
SQL 缓存 NoSQL
冲刺金三银四,这份豪礼【面试锦囊】真舍不得给你们
大家好,我是小羽马上就是金三银四啦,最近有很多粉丝跟我交流关于面试方面的经验以及分享,也有部分是在准备今年的金三银四的春招。小羽也一直没出关于面试方面的文章,这篇就结合自己之前的面试经历以及...
150 0
冲刺金三银四,这份豪礼【面试锦囊】真舍不得给你们
|
缓存 NoSQL 前端开发
揭密!程序员面试不得不知道的潜规则
规则1:面试的本质不是考试,而是告诉面试官你会做什么 很多刚入行的小伙伴特别容易犯的一个错误,不清楚面试官到底想问什么,其实整个面试中面试官并没有想难道你的意思,只是想通过提问的方式来知道你会什么
揭密!程序员面试不得不知道的潜规则
|
安全 测试技术 数据安全/隐私保护
6 大测试用例设计题详细整理— 助攻高薪求职之路!
我发现无论是刚入职场的测试新人还是在具备几年测试经验的职场老人,对于测试用例设计这块,倘若不是自己接触过测试过的软件产品,被问到如何测试基本回答不上来,原因归根结底还是测试思维积累不够。
6 大测试用例设计题详细整理— 助攻高薪求职之路!
|
前端开发 JavaScript 算法
从文科生到前端专家 - 在转行时我想过的问题
我是繁易,一名从文科转行的前端工程师,希望能为和我一样,对编程感兴趣/犹豫转行/正在转行的同学们提供一点经验分享。
252 0
从文科生到前端专家 - 在转行时我想过的问题
|
机器学习/深度学习 人工智能 自然语言处理
15篇面试通关经验+10大热招岗位,给你足够底气斩获offer!|开发者必读(164期)
人生有许多种可能,只要勇敢一次,你会发现收获的东西比想象中多很多,譬如说,我们正在招人,你勇敢投简历了吗?如果你还没有准备好,没关系,我们给你足够底气斩获offer!10大热招岗位需求+15篇师兄师姐面试经验分享,手把手教你面试通关!
|
弹性计算 算法 开发者
阿里巴巴招聘最全集合帖:宣讲会+岗位+30篇面试宝典来啦
又到了金三银四的季节…… 特别为你送上阿里巴巴招聘各类信息汇总,还有30篇面试宝典+必备资料,供你学!
20013 0
阿里巴巴招聘最全集合帖:宣讲会+岗位+30篇面试宝典来啦
|
前端开发 程序员
招聘本科程序员好,还是专科有经验的程序员?网友答案很现实!
大家可能已经发现,最近几年来的各种招聘,很多岗位都要求本科以上学历,小型的创业互联网公司艰苦的工作岗位,才有部分专科岗位 。这对于专科生来说,看下面这位HR的经历着实郁闷,怎么破? 网友评论: 也...
1198 0