热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Float的基本介绍

关于浮点数,为什么它生来就可能存在误差?带着好奇查阅了一些介绍,然后做了简单汇总。这只是一段知识的开始,后续还会继续完善。(很难过,这里的MarkDown不支持编辑公式。请移驾本人

关于浮点数,为什么它生来就可能存在误差?带着好奇查阅了一些介绍,然后做了简单汇总。这只是一段知识的开始,后续还会继续完善。(很难过,这里的MarkDown不支持编辑公式。请移驾本人的博客地址:
http://neojos.com )

—— 荡荡上帝,下民之辟。疾威上帝,其命多辟。天生烝民,其命匪谌。靡不有初,鲜克有终。

Floating-point represent

浮点数在计算机中是如何表示的?因为它有小数点,那么小数点后面的数,该如何用二进制来表示呢?我们都知道,浮点数本身就存在误差,在工作中,你所使用的float都是一个近似数。这又是什么原因导致的呢?

1. Fixed-point

fixed-point 是将bit位拆分成固定的两部分:小数点前的部分和小数点后的部分。拿32 bitfixed-point表示举例,可以将其中的24 bit用于表示整数部分,剩余的8 bit表示小数部分。

假如要表示1.625,我们可以将小数点后面的第一个bit表示$\frac12$,第二个bit表示1/4,第三个1/8一直到最后一个1/256。最后的表示就是00000000 00000000 00000001 10100000。这样其实也好理解,因为小数点前是从$2^0$开始向左成倍递增,小数点后从$2^{-1}$开始向右递减。

因为小数点后面的部分始终小于1,上面这种表达方式能表达的最大数是255/256。再比这个数小,这种结构就无法表示了。

Floating-point basics

根据上面的思路,我们用二进制表达一下5.5这个十进制数,转化后是$101.1_{(2)}$。继续转换成二进制科学计数法的形式:$1.011_{(2)} * 2^2$。在转换的二进制科学计数法过程中,我们将小数点向左移了2位。就跟转换十进制的效果一样:$101.1_{(10)}$ 的科学计数形式为$1.011 * 10^2$

对于二进制科学计数法表达的5.5,我们将其拆分成2部分,1.011是一部分,我们称为mantissa。指数2是另一部分,称为exponent。下面我们要将$1.011_{(2)} * 2^2$ 映射到计算机存储的8 bit结构上。

我们用第一个bit来表示正负符号,1表示负数,0表示正数。紧接着的4 bit用来表示exponent + 7后的值。 4 bit最大可以表示到15,这也就意味着当前的exponent不能超过8,不能低于-7。最后的3 bit用于存储mantissa的小数部分。你可能有疑问,它的整数部分怎么办呢?这里我们约定整数部分都调整成1,这样就可以节省1 bit了。举个例子,如果要表示的十进制数是0.5,那么最后的二进制数不是$0.1_{(2)}$,而是$1.0 * 2^{-1}$。最后表示的结果就是:0 1001 011

《Float的基本介绍》

再来一个decode的例子,即将0 0101 100还原回原始值。根据之前的描述0101表示的十进制是5,所以expOnent= -2,表示回二进制科学计数法的结果:$1.100 * 2^{-2} = 0.011_{(2)}$。我们继续转换成真实精度的数:0.375

最后可以看在,如果mantissa的长度超过3 bit表示的范围,那么数据的存储就会丢失精度,结果就是一个近似值了。

1. Representable numbers

继续按照上面的思路,现在8 bit的浮点表示能表示的数值区间更大。

要表示最小正数的话,sign置为0,接下来的4 bits置为0000,最后的mantissa也置为000。那么最终的表示结果就是:$1.000_{(2)} * 2^{-7} = 2^{-7} ≈ 0.0079_{(10)}$

表示最大正数的话,sign置为0,其他位也都置为1。最终表示的结果:$1.111_{(2)} * 2^{8} = 111100000_{(2)} = 480_{(10)}$。所以8 bits浮点表示的正数范围(0.0079, 480]。而8 bits 二进制表示的范围是[1, 127]。范围确实大了很多。

但是必须注意:浮点数无法准确表示该区间内的所有数。拿十进制51来说,用二进制表示是110011。转化为8 bits的浮点数表示:$110011_{(2)} = 1.10011_{(2)}*2^{5}$。当我们试着去存储的时候,发现3 bitsmantissa放不下现在的10011。我们不得不做近似取值,将结果修改为$1.101_{(2)} * 2^{5} = 110100_{(2)} = 52_{(10)}$。所以,在我们8 bits 表达的浮点数中51 = 52。这样的处理有时候让我们很无奈,但这也是为了让8 bits表示更大范围的数所必须付出的代价。

从上面的过程中,我们还可以理解在计算中round upround down的策略。当小数点后的数超过3 bit时,就是展现这个策略的时候。拿19举例,表示成二进制科学计数法:$1.0011 * 2^4$。如果执行round up,最终的结果就是$1.010_{(2)} * 2^4 = 20_{(10)}$。如果执行round down,结果便是$1.001_{(2)} * 2^4 = 18$

如果我们要提高浮点数表达的精度,mantissa区间就需要更多的bit来表示。拿float32来举例,它是1 bitsign8 bitsexponent以及23 bits表示的mantissa

IEEE standard

该标准定义了更长的bit来提高表达的精度和范围。

1. IEEE formats

它定义了上面描述的signexponentmantissa以及excess(就是8 bits表示过程中用到的7)。

signexponentmantissaexponentsignificant
formatbitbitsbitsexcessdigits
Our 8-bit14371
Our 16-bit169313
IEEE 32-bit18231276
IEEE 64-bit111521,02315
IEEE 128-bit11511216,38334

2. 非数值

顾名思义:not a number,程序中偶尔会看到的NaN。比如0/0∞ + −∞等。这类数值在表示中exponent都是1。

3. 运算

讨论 x + (y + z)(x + y) +z的结果是否相同,拿上面8 bits的浮点数表示来说明。其中x=1y=52z= -52。我们注意到y+z = 0,所以第一个计算结果是1。但(x+y)的结果仍然是52,这主要是因为mantissa无法表示,导致最终结果取近似值还是52,最终结果是0。

另外一个例子:1/6 + 1/6 + 1/6 + 1/6 + 1/6 + 1/6 = 1等式也是不存在的。在8 bits的表示中无法准确的表示1/6,所以最终的结果要比1小。

在程序开发过程中,我们必须意识到这类问题产生的影响。

float to float

Round返回最近的整数,但返回值是一个float64类型。返回值是四舍五入后的结果。

a := math.Round(12.3456)
//output: 12

相对应的函数,还有FloorCeil

// Floor returns the greatest integer value less than or equal to x.
// output: 12
a := math.Floor(12.3456)
// Ceil returns the least integer value greater than or equal to x.
// output: 13
a := math.Ceil(12.3456)

match/big

关于浮点数的比较:

// change these value and play around
float1 := 123.4568
float2 := 123.45678
// convert float to type math/big.Float
var bigFloat1 = big.NewFloat(float1)
var bigFloat2 = big.NewFloat(float2)
// compare bigFloat1 to bigFloat2
result := bigFloat1.Cmp(bigFloat2)

参考文章:

  1. Golang : Compare floating-point numbers
  2. Floating-point representation
  3. Floating Point Numbers

推荐阅读
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • EPICS Archiver Appliance存储waveform记录的尝试及资源需求分析
    本文介绍了EPICS Archiver Appliance存储waveform记录的尝试过程,并分析了其所需的资源容量。通过解决错误提示和调整内存大小,成功存储了波形数据。然后,讨论了储存环逐束团信号的意义,以及通过记录多圈的束团信号进行参数分析的可能性。波形数据的存储需求巨大,每天需要近250G,一年需要90T。然而,储存环逐束团信号具有重要意义,可以揭示出每个束团的纵向振荡频率和模式。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 本文介绍了Linux系统中正则表达式的基础知识,包括正则表达式的简介、字符分类、普通字符和元字符的区别,以及在学习过程中需要注意的事项。同时提醒读者要注意正则表达式与通配符的区别,并给出了使用正则表达式时的一些建议。本文适合初学者了解Linux系统中的正则表达式,并提供了学习的参考资料。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 本文介绍了如何使用PHP向系统日历中添加事件的方法,通过使用PHP技术可以实现自动添加事件的功能,从而实现全局通知系统和迅速记录工具的自动化。同时还提到了系统exchange自带的日历具有同步感的特点,以及使用web技术实现自动添加事件的优势。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • Oracle Database 10g许可授予信息及高级功能详解
    本文介绍了Oracle Database 10g许可授予信息及其中的高级功能,包括数据库优化数据包、SQL访问指导、SQL优化指导、SQL优化集和重组对象。同时提供了详细说明,指导用户在Oracle Database 10g中如何使用这些功能。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
author-avatar
JJ关键词
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有