python浮点型精度损失问题_解决float型数据精度损失问题

问题:浮点型数据存储方式会导致数据精度损失,增大计算误差。

float fval = 0.45;  // 单步调试发现其真实值为:0.449999988

double dval = 0.45; // 单步调试发现其真实值为:0.45000000000000001

当很多个这样的单精度浮点型数据进行运算时,就会有累积误差,使得运算结果达不到理想的结果。尤其是对那种需要判断相等的情况(浮点型数据判断相等会有误差)。

因此我们可以通过把浮点型数据放大1e6倍,把它赋给一个整型变量,把得到的结果再除以1e6,就会使精度损失降到最低。

float a = 0.45;    // 0.449999988double b = 0.45;int c = 1e6 *b;    // 450000double d1 = 2 *a;   // 0.89999997615814209int d = 2 *c;     // 900000double d2 = d / 1e6; // 0.90000000000000002, 推荐方式

double d3 = 2 * 0.45; // 0.90000000000000002

下面是浮点型数据存储方式,转自:https://www.cnblogs.com/wuyuan2011woaini/p/4105765.html

C语言和 C#语言中,对于浮点型的数据采用单精度类型(float)和双精度类型(double)来存储:

float 数据占用 32bit;

double 数据占用 64bit;

我们在声明一个变量 float f = 2.25f 的时候,是如何分配内存的呢?

其实不论是 float 类型还是 double 类型,在存储方式上都是遵从IEEE的规范:

float 遵从的是 IEEE R32.24;

double 遵从的是 IEEE R64.53;

单精度或双精度在存储中,都分为三个部分:

符号位(Sign):0代表正数,1代表为负数;

指数位(Exponent):用于存储科学计数法中的指数数据;

尾数部分(Mantissa):采用移位存储尾数部分;

单精度 float 的存储方式如下:

双精度 double 的存储方式如下:

R32.24 和 R64.53 的存储方式都是用科学计数法来存储数据的,比如:

8.25  用十进制表示为:8.25 * 100

120.5 用十进制表示为:1.205 * 102

而计算机根本不认识十进制的数据,他只认识0和1。所以在计算机存储中,首先要将上面的数更改为二进制的科学计数法表示:

8.25   用二进制表示为:1000.01

118.5 用二进制表示为:1110110.1

而用二进制的科学计数法表示 1000.1,可以表示为1.0001 * 23

而用二进制的科学计数法表示 1110110.1,可以表示为1.1101101 * 26

任何一个数的科学计数法表示都为1. xxx * 2n,尾数部分就可以表示为xxxx,由于第一位都是1嘛,干嘛还要表示呀?所以将小数点前面的1省略。

由此,23bit的尾数部分,可以表示的精度却变成了24bit,道理就是在这里。(float有效位数相应的也会发生变化,而double则不会,因达不到)

那 24bit 能精确到小数点后几位呢?我们知道9的二进制表示为1001,所以 4bit 能精确十进制中的1位小数点,24bit就能使 float 精确到小数点后6位;

而对于指数部分,因为指数可正可负(占1位),所以8位的指数位能表示的指数范围就只能用7位,范围是:-127至128。所以指数部分的存储采用移位存储,存储的数据为元数据 +127。

注意:

元数据+127:大概是指“指数”从00000000开始(表示-127)至11111111(表示+128)

所以,10000000表示指数1 (127 + 1 = 128 --> 10000000 ) ;

指数为 3,则为 127 + 3 = 130,表示为 01111111 + 11 = 10000010 ;

下面就看看8.25 和 118.5 在内存中真正的存储方式:

8.25 用二进制表示为:1000.01

8.25用二进制的科学计数法表示为: 1.0001* 23,按照上面的存储方式:

符号位为:0,表示为正;

指数位为:3+127=130,即 10000011;

尾数部分为:0001;

故8.25的存储方式如下图所示:

而单精度浮点数118.5的存储方式如下图所示:

那么如果给出内存中一段数据,并且告诉你是单精度存储的话,你将如何知道该数据的十进制数值呢?

其实就是对上面运算的反推过程,比如给出如下内存数据:01000010111011010000000000000000,

首先我们现将该数据分段:0  10000101  11011010000000000000000,在内存中的存储就为下图所示:

根据我们的计算方式,可以计算出这样一组数据表示为:

1.1101101*2(133-127=6)=1.1101101 * 26= 1110110.1=118.5

而双精度浮点数的存储和单精度的存储大同小异,不同的是指数部分和尾数部分的位数。所以这里不再详细的介绍双精度的存储方式了,只将118.5的最后存储方式图给出:

下面就这个知识点来解决一个疑惑,请看下面一段程序,注意观察输出结果:

class 浮点数

{

static void Main(string[] args)

{

float f = 2.2f;

double d = (double)f;

Console.WriteLine(d.ToString("0.0000000000000"));

//结果:"2.2000000476837"

f = 2.25f;

d = (double)f;

Console.WriteLine(d.ToString("0.0000000000000"));

//结果:"2.2500000000000"

//2.25 - 2.2 = 0.05 ( 但实际结果不是0.05 )

float f2 = 2.25f - 2.2f;

Console.WriteLine(f2.ToString("0.0000000000000"));

//结果:"0.0499999500000"

}

}

输出的结果可能让大家疑惑不解:

单精度的 2.2 转换为双精度后,精确到小数点后13位之后变为了2.2000000476837

而单精度的 2.25 转换为双精度后,变为了2.2500000000000

为何2.2在转换后的数值更改了,而 2.25却没有更改呢?

其实通过上面关于两种存储结果的介绍,我们大概就能找到答案。

2.25的单精度存储方式表示为:0 10000001 00100000000000000000000

2.25的双精度存储方式表示为:0 10000000 0010010000000000000000000000000000000000000000000000000

这样 2.25 在进行强制转换的时候,数值是不会变的。

而我们再看看2.2,用科学计数法表示应该为:

将十进制的小数转换为二进制的小数的方法是:将小数*2,取整数部分。

0.2×2=0.4,所以二进制小数第一位为0.4的整数部分0;

0.4×2=0.8,第二位为0.8的整数部分0;

0.8×2=1.6,第三位为1;

0.6×2=1.2,第四位为1;

0.2×2=0.4,第五位为0;

...... 这样永远也不可能乘到=1.0,得到的二进制是一个无限循环的排列 00110011001100110011...

对于单精度数据来说,尾数只能表示 24bit 的精度,所以2.2的 float 存储为:

但是这种存储方式,换算成十进制的值,却不会是2.2。

因为在十进制转换为二进制的时候可能会不准确(如:2.2),这样就导致了误差问题!

并且 double 类型的数据也存在同样的问题!

所以在浮点数表示中,都可能会不可避免的产生些许误差!

在单精度转换为双精度的时候,也会存在同样的误差问题。

而对于有些数据(如2.25),在将十进制转换为二进制表示的时候恰好能够计算完毕,所以这个误差就不会存在,也就出现了上面比较奇怪的输出结果。

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

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

相关文章

Linux配置本地yum源(RHEL8)

https://www.cnblogs.com/itwangqiang/p/13391401.html

如何把照片正面变成反面_各国签证照片要求大全 (含模板)

对于不是很熟悉签证的小伙伴来说,面对全球那么多国家的签证而且每张签证照片的规格不同为此我们为您整理了各国签证照片要求大全 东南亚国家的签证照要求基本相同,就以泰国为例,告诉大家签证照的注意事项。“泰国,新加坡&#xff…

TensorFlow实验(3)

模型的保存与恢复 我们来简单实现一下模型的保存与恢复 训练完TensorFlow模型后,可将其保存为文件,以便于预测新数据时直接加载使用。 TensorFlow模型主要包含网络的设计或者图以及已经训练好的网络参数的值。 TensorFlow提供的tf.train.Saver()函数…

ad域 禁用账号_IST-AD域信息同步平台来袭

IST的AD域信息同步系统是能帮助域管理员简化日常的一些管理工作,可以让AD域系统与其他的业务系统进行用户信息同步,实现自动的新旧用户帐户信息的同步修改、组织架构同步调整,并有简单易操作的配置页面系统与操作日志查询等。通过ODBC、Web S…

Linux基础(firewalld防火墙配置管理工具的图形用户界面)

firewall-config的界面如图所示 我们先将当前区域中请求http服务的流量设置为允许,但仅限当前生效。具体配置如图 尝试添加一条防火墙策略规则,使其放行访问8080-8088端口(TCP协议)的流量,并将其设置为永久生效&#x…

ios 请求失败封装_vue_axios请求封装、异常拦截统一处理

1、前端网络请求封装、异常统一处理vue中采用axios处理网络请求,避免请求接口重复代码,以及各种网络情况造成的异常情况的判断,采用axios请求封装和异常拦截操作;axios 请求封装// 引入axios文件包import axios from axios// POST…

Linux基础(使用ssh服务管理远程主机1)

配置网络参数 使用nmtui命令配置网络参数,以及通过nmcli命令查看网络信息并管理网络会话服务。 执行nmtui命令运行网络配置工具 进入主界面 选中编辑连接并按下回车键 选中要编辑的网卡名称,然后按下Edit(编辑)按钮 把网络IPv4 …

联想g510升级方案_联想智慧中国行,聚焦第一古城,助力企业智能升级

12月29日,联想智慧中国行“一起联想 生态绽放”One Lenovo融合品鉴会邢台站盛大启幕,现场近70位河北合作伙伴到场参会,共同探讨智能时代带来的多重挑战和迎战机遇。“联想智慧中国行”是联想致力于推动中国行业智能化升级举办的系列活动&…

软件工程(总体设计②设计原理)

设计原理 模块化 模块是由边界元素限定的相邻程序元素的序列,而且有一个总体标识符代表它。 按照模块的定义,过程、函数、子程序和宏,都可作为模块。 面向对象方法学中的对象是模块,对象内的方法也是模块。模块是构成程序的基…

TensorFlow构建二维数据拟合模型(3)

占位符与数据喂入机制 placeholder是TensorFlow提供的占位符节点,由tf.placeholder()函数创建,其实质上也是一种变量。占位符没有初始值,只会分配必要的内存,其值由会话中用户调用的run()函数传递。 占位符声明的方法如表 函数…

合作开发过程产生的专利_被起诉专利侵权怎么办?专利律师给你出招!

随着国内企业专利申请量的增加及专利保护意识的逐步提升,专利侵权诉讼作为常用的商业竞争手段和策略,其数量也随之呈逐年递增之势。考虑到目前国内专利数量较多,且很多专利技术互有交叉,因此在进行产品研发时即使未借鉴他人产品&a…

idea怎么设置代码提示不区分大小写_IntelliJ IDEA 这样设置动图,棒极了!

转自:IntelliJ-IDEA-Tutorial/Judas.n链接:http://suo.im/6sHdelIntelliJ IDEA 有很多人性化的设置我们必须单独拿出来讲解,也因为这些人性化的设置让我们这些 IntelliJ IDEA 死忠粉更加死心塌地使用它和分享它。推荐设置IntelliJ IDEA 的代码…

linux课堂笔记(1)

一、linux特点 1.源代码开发: *.c *.h 可二次开发 2.安全:可检测安全性 3.稳定:共享内存 内存冲突(Windows,内存冲突,蓝屏死机) 4.网络服务:server(WWW&#xf…

表字段顺序有何影响_「品味保定」炸烹虾段乾隆赞 百年保定柔雅香

【引文】上溯先贤尧帝,保定传承已逾千载。燕赵之地、畿辅之疆、北控三关、南通九省、翅卫京师说的就是古城保定。千百年来,古城保定形成了独具特色的饮食文化,精致气派的直隶官府菜就是这种文化的结晶。品百年保定酒,尝直隶官府菜…

linux课堂笔记(2)

linux安装 1,虚拟机:VMware12 (1)注册:商业软件(集成序列号,注册机(根据加密算法生成序列号),文本文件(记录序列号)) &…

testufo测试刷新率测试_上手体验微星电竞显示器PAG301CR:200Hz高刷新率只是它的小亮点...

电子竞技行业高速发展,逐渐成为体育产业版图中重要的一块,产业链也在不断完善,人们对于电子竞技的认识也是越来越深刻,同时越来越多的人加入了电子竞技行列中,但是电竞游戏除了水平外,设备同样也尤其重要,特…

linux课堂笔记(3)

1,linux厂商版本信息 内核信息:主版本.修正号.发行号.on 硬件平台. 主机名 登录 身份认证 localhost login:root password:隐藏口令 提示上一次登录时间与地点 用户名主机名 当前目录 指令提示符 rootlocalhost ]# 注&…

graphpad做折线图坐标轴数字_pandas做数据可视化具体操作,快来看看吧

来自公众号:大邓和他的Python常见的数据可视化库有:matplotlib 是最常见的2维库,可以算作可视化的必备技能库,由于matplotlib是比较底层的库,api很多,代码学起来不太容易。seaborn 是建构于matplotlib基础上&#xff0…

linux课堂笔记(4)

常用指令:mv 1,隐藏文件 (1)windows通过设置属性隐藏文件 attrib h s 文件 attrib h s d:\net.txt attrib h s d:\net.txt (2)linux系统文件以点开始命名隐藏文件 mv /路径/源文件 /路径/目标…

实现输入提示 layui_ASP.NET Core SignalR :学习消息通讯,实现一个消息通知

什么是 SignalR目前我用业余时间正在做一个博客系统,其中有个功能就是评论通知,就是假如A用户评论B用户的时候,如果B用户首页处于打开状态,那么就会提示B用户有未读消息。暂时用SignalR来实现这个功能。我也是看了两天的资料才明白…