开发者社区> 技术小能手> 正文

从一道招聘考题谈起

简介:
+关注继续查看

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

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

本文作者:蒋步星

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

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
阿里云ECS云服务器初始化设置教程方法
阿里云ECS云服务器初始化是指将云服务器系统恢复到最初状态的过程,阿里云的服务器初始化是通过更换系统盘来实现的,是免费的,阿里云百科网分享服务器初始化教程: 服务器初始化教程方法 本文的服务器初始化是指将ECS云服务器系统恢复到最初状态,服务器中的数据也会被清空,所以初始化之前一定要先备份好。
14607 0
阿里云服务器安全组设置内网互通的方法
虽然0.0.0.0/0使用非常方便,但是发现很多同学使用它来做内网互通,这是有安全风险的,实例有可能会在经典网络被内网IP访问到。下面介绍一下四种安全的内网互联设置方法。 购买前请先:领取阿里云幸运券,有很多优惠,可到下文中领取。
18536 0
阿里云服务器怎么设置密码?怎么停机?怎么重启服务器?
如果在创建实例时没有设置密码,或者密码丢失,您可以在控制台上重新设置实例的登录密码。本文仅描述如何在 ECS 管理控制台上修改实例登录密码。
19595 0
使用OpenApi弹性释放和设置云服务器ECS释放
云服务器ECS的一个重要特性就是按需创建资源。您可以在业务高峰期按需弹性的自定义规则进行资源创建,在完成业务计算的时候释放资源。本篇将提供几个Tips帮助您更加容易和自动化的完成云服务器的释放和弹性设置。
18627 0
使用SSH远程登录阿里云ECS服务器
远程连接服务器以及配置环境
12397 0
如何设置阿里云服务器安全组?阿里云安全组规则详细解说
阿里云安全组设置详细图文教程(收藏起来) 阿里云服务器安全组设置规则分享,阿里云服务器安全组如何放行端口设置教程。阿里云会要求客户设置安全组,如果不设置,阿里云会指定默认的安全组。那么,这个安全组是什么呢?顾名思义,就是为了服务器安全设置的。安全组其实就是一个虚拟的防火墙,可以让用户从端口、IP的维度来筛选对应服务器的访问者,从而形成一个云上的安全域。
16971 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,阿里云优惠总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系.
24733 0
+关注
技术小能手
云栖运营小编~
7208
文章
9
问答
文章排行榜
最热
最新
相关电子书
更多
OceanBase 入门到实战教程
立即下载
阿里云图数据库GDB,加速开启“图智”未来.ppt
立即下载
实时数仓Hologres技术实战一本通2.0版(下)
立即下载