【知识点随笔分享 | 第一篇】避不开的浮点误差

news2024/9/20 15:02:53

  引入:

各位在大一初入C语言的时候,老师肯定说过浮点数之间的比较要用做差法,当二者的差值特别小甚至于接近0的时候,这两个数就相等,不知道各位是否会有疑惑?为什么浮点数不可以直接进行比较呢?

#include<stdio.h>
int main()
{
	double a = 0.1;
	double b = 0.2;
	

	if ((a + b) == 0.3)
	{
		printf("true");
	}
	else
	{
		printf("false");
	}
}

这段代码非常简单,就是判断用浮点数存储的0.1+0.2是否等于0.3。

运行结果竟然是不等于。但是实际知识告诉我们0.1+0.2确实等于0.3,那么为什么比较不相等呢?
我们单独查看一下 浮点数0.1+0.2的值:
我们发现:原来浮点数0.1+0.2并不完全等于0.3,怎么尾部还有这么多数字?
其实这就是我们今天要介绍的内容:
浮点误差

浮点误差:

想要探寻误差的起源,我们就要介绍一下IEEE-754是什么东西,它详细的规定了浮点数字在计算机中的二进制存储规则。
 

IEEE-754是一种国际通用的二进制浮点数标准,它规定了浮点数在计算机系统内部的表达方式和相应的计算规则,目的是为了提高不同计算机系统之间浮点数的互操作性和可移植性。

IEEE-754标准定义了两种浮点数格式,分别是单精度浮点数和双精度浮点数。其中,单精度浮点数包含32位,双精度浮点数包含64位。这两种格式都被广泛使用,尤其是在科学计算和工程领域中。

IEEE-754标准还规定了浮点数的符号位、指数位和尾数位的分布,以及浮点数的四种运算方式(加、减、乘、除)和异常处理方式。其中,浮点数的指数采用移码表示,可以表示正数、负数和零,浮点数的精度和有效数字也会随着指数的变化而变化。

在实际应用中,IEEE-754标准的浮点数格式和计算规则被广泛应用于各种编程语言和计算机系统中,如C语言、Java、Python等,也为高性能计算和大规模科学计算提供了重要的技术支持。

EEE-754标准规定了两种浮点数格式,分别是单精度浮点数双精度浮点数,具体如下:

  1. 单精度浮点数:32位二进制,有1位符号位、8位指数位和23位尾数位。最高位为符号位,0表示正数,1表示负数。接下来的8位指数位采用移码表示,可以表示正数、负数和零。23位尾数位包括整数部分和小数部分,用于保存有效数字和精度。

  2. 双精度浮点数:64位二进制,有1位符号位、11位指数位和52位尾数位。最高位为符号位,0表示正数,1表示负数。接下来的11位指数位采用移码表示,可以表示正数、负数和零。52位尾数位、包括整数部分和小数部分,用于保存有效数字和精度。

这两种浮点数格式使用指数和尾数的组合方式,可以表达一定范围内的实数。浮点数格式的设计中,采用了阶码和尾数的分离表示方法,从而可以根据指数高低位的变化来动态调整浮点数的精度和范围,提高了浮点数计算的灵活性和准确性。

误差的起源:

我们可以用这张图简单表示一下二进制的存储

我们可看到:由于是二进制的原因,小数存储0.5,0.25非常方便,但是没有办法存储像0.1这样的数字,我们只能是无限逼近0.1,对应的二进制就是一个不断循环的无限数字:0.00011001100110011001100110011........ 

但是通过刚才对IEEE-754的介绍,大家应该已经清楚:我们能够存储的位数是有限的,如果是单精度浮点型,就只有23位存储空间,如果是双精度,就只有52位存储空间,因此像这种无限循环下去的二进制数字,在实际存储的时候就会被截断。而这也就是误差的来源。

虽然这种误差很小,但是在某些方面也会有很大的损失,例如银行的存储,如果银行每一份钱都要多出这么一点的话,那么对于银行来讲将会是巨大的损失。

因此我们也一直都在探寻如何避免浮点误差

如何避免浮点误差:

手动实现:

1. 不要直接比较浮点数
直接比较浮点数时,可能会因为浮点误差而得到错误的结果。而应该通过一个很小的误差范围来比较浮点数,例如相差小于某个很小数值 eps,则认为两个浮点数相等。

2. 尽可能使用整数运算
在一些情况下,可以预先将浮点数转换为整数,进行乘以若干倍数的运算,然后再除以对应的倍数,这样可以避免浮点误差。

3. 使用高精度算法
如果精度要求很高,可以使用高精度算法,例如可以使用自带高精度计算功能的编程语言,或者第三方高精度计算库等。

4. 设置适当的浮点数精度
有时可以使用四舍五入、向零取整等方式设置适当的精度来避免浮点误差。例如round函数,可以向最近的整数取整,也可以设置精度取小数点后几位

5. 避免迭代计算
迭代计算往往会引入严重的误差,因此应该尽可能避免使用。可以使用单次计算得到精确结果的方法来避免迭代计算。

最后需要说明的是,避免浮点误差的方法是应该根据具体问题来决定的,不同的问题可能需要不同的方法。在实际问题中,需要对精度和效率进行综合考虑,并选择适当的方法来避免浮点误差。

此外,Python还为我们提供了一个特殊的数据类型:deciaml,用来存储特定精度的小数。

ecimal是一个Python库,用于高精度浮点数计算。它允许开发者在不丧失精度的前提下进行浮点数运算。

ecimal库的主要特点如下:

1. 高精度
ecimal使用了基数为10的定点算术来执行浮点数运算,这使得它能够提供高达28位有效数字的计算。这就意味着,在进行涉及到小数点后多位数字的计算时,能够保持非常高的精度。

2. 可重现
与IEEE-754标准中指定的浮点数计算不同,ecimal的计算结果具有可重现性。这意味着无论使用何种计算机架构或CPU,则在每个机器上都将获得相同的结果。

3. 容易上手
由于模块的设计,使用ecimal库进行编码非常便捷。它可以很好地与Python的内置数据类型和运算符配合使用。而且,经过一些特殊的带符号的算术功能使得开发者可以控制精度。

        实际上,ecimal已经成为Python编程中十分重要的一个库,特别是在与商业、财务相关的计算中,例如货币兑换、税收计算等。在这些场景中,有必要保持高精度和可重现的浮点数计算,而ecimal正是提供这些功能的最佳解决方案之一。

 

回到刚开头的那个错误:既然不能直接比较,那么我们怎么办呢?

第一种方法是直接做差,当两个数的差很小的时候,我们就认为这两个浮点数相等。

自动实现:

第二种方法是直接调用各大语言里面的库函数,各语言在设计的时候就已经注意到了这个问题,因此已经设计好了库函数来供我们使用。

各编程语言提供的判断浮点数是否相等的函数如下:

1. Python:math.isclose()
在Python中,math.isclose()函数用来比较两个浮点数是否接近。该函数有五个参数,分别为a、b(需要进行比较的两个数)、rel_tol(相对误差阈值,默认为1e-9)、abs_tol(绝对误差阈值,默认为0)、两个布尔型参数,分别表示是否应考虑符号和两个值是否为NaN。当a和b之间的差异小于所提供的误差阈值时,该函数返回True,反之返回False。

2. C++:std::abs()
在C++中,可以使用std::abs()函数来判断两个浮点数是否相等。根据该函数返回的差值是否小于给定的epsilon,从而判断两个浮点数之间的差异是否小于阈值。例如:

   bool isEqual(double x, double y) {
       const double epsilon = 1e-9;
       if (std::abs(x - y) <= epsilon * std::abs(x)) {
           return true;
       }
       return false;
   }

3. Java:Math.abs()
在Java中,可以使用Math.abs()函数来判断两个浮点数是否相等。使用该函数返回的差值是否小于所给的epsilon值,从而判断两个浮点数之间的差异是否小于阈值。例如:

   public static boolean isEqual(double x, double y) {
       final double epsilon = 1e-9;
       return Math.abs(x - y) <= epsilon * Math.abs(x);
   }

4. MATLAB:eps()
在MATLAB中,可以使用eps()函数来判断两个浮点数是否相等。该函数返回与指定浮点数具有相同精度的浮点数。一般情况下,可以将浮点数进行相减并通过eps()函数计算差异,然后将结果与所给的epsilon值比较。例如:

   function bool = isEqual(x, y)
       epsilon = 1e-9;
       bool = abs(x - y) <= epsilon * abs(x);
   end

总之,使用这些函数可以有效地避免由于浮点舍入误差而导致不正确的计算结果。

 

 总结:

虽然浮点数会存在浮点误差的状态,但是我们不要害怕使用浮点数,只要在逻辑上牢记浮点数会出现浮点误差,就难出现错误。

如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,大家的支持就是我坚持下去的动力!

 

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

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

相关文章

Nacos-手写配置中心基本原理

本文已收录于专栏 《中间件合集》 目录 概念说明Nacos配置中心Naocs配置项Naocs配置集Naocs配置快照 需求分析核心功能代码实现AService模块BService模块NacosService模块NacosSDK模块 注意事项总结提升 概念说明 Nacos注册中心&#xff1a;https://blog.csdn.net/weixin_4549…

vs中运行时库简要说明

vs中右键单击工程 -->属性–>c/c->代码生成&#xff0c;进入如下菜单中&#xff1a; 可以看出有如下几个选项&#xff1a; 多线程(/MT)&#xff1a;链接目标库为libcmt.lib 多线程调试(/MTd)&#xff1a;链接目标库为libcmtd.lib 多线程DLL(/MD)&#xff1a;链接目标…

02.GLM-130B

文章目录 前言泛读相关知识GPTBERTT5小结 背景介绍主要贡献和创新点GLM 6B 精读自定义Mask模型量化1TB 的中英双语指令微调RLHFPEFT训练策略 实验分析与讨论模型参数六个指标其他测评结果 代码复现&#xff08;6B&#xff09;环境准备运行调用代码调用网页服务命令行调用 模型微…

在 Python 中生成随机 4 位数字

文章目录 在 Python 中生成随机数使用 random 模块在 Python 中生成随机数使用 random.randint() 方法使用 random.randrange() 方法 使用替代方法在 Python 中生成随机数总结 Python 是一种高级解释型编程语言&#xff0c;全球大多数程序员都在使用它。 它在面向对象编程 (OOP…

SpringCloud Alibaba入门5之Hystrix的使用

我们继续在前一章的基础上进行学习。 SpringCloud Alibaba入门5之使用OpenFegin调用服务_qinxun2008081的博客-CSDN博客 上一节我们已经使用OpenFeign完成了服务间的调用&#xff0c;如果现在存在大量的服务&#xff0c;每个服务有若干个节点&#xff0c;其中一个节点发生故障…

Word技巧之【文档自动保存】

打工人的噩梦—电脑突然坏掉&#xff0c;文档还没保存&#xff01;你是否遇到这种情况&#xff1f; 如果Word文档设置了自动保存&#xff0c;就不用太过担心了&#xff0c;只需要几个简单的操作就能设置好。还不知道的小伙伴&#xff0c;跟着小编一起看看吧。 设置Word文档自动…

一起来学R编程把—do.call 函数的应用

R语言由一个个基础函数组成&#xff0c;熟练灵活应用这些基础函数&#xff0c;有助于我们更好的学习R包及编程,这个专栏可能很多人不感兴趣&#xff0c;但是对提升自己很有帮助&#xff0c;感兴趣的朋友一起来学习。今天我们来介绍一下do.call函数的用法. do.call函数是一个非…

ChatGLM-6B阿里云部署

机器配置 重点关注指标&#xff1a;CPU、内存、GPU、GPU驱动 类型CPU内存GPU机器配置16核125GNVIDIA A100 80G # 查看显卡安装状态 nvidia-smi 安装必要的软件 git sudo apt-get update sudo apt-get install git git-lfs(大文件管理) sudo apt-get install git-lfs py…

从零开始手搓一个STM32与机智云的小项目——GPIO模拟时序控制外设1

文章目录 前言WS2812B1.模块简介2.时序介绍3.硬件介绍4.传输速率&#xff0c;以及帧数要求 代码1.初始化2.模拟时序1.复位函数2.发送0、1码3.封装发送函数 总结 前言 上一篇文章中介绍了整个板子的最基本功能模块——使用GPIO的通用输入输出实现简单的按键输入以及推挽输出控制…

基于Java+Swing+Mysql实现图书管理系统V2.0

基于JavaSwingMysql实现图书管理系统V2.0 一、系统介绍二、功能展示1.项目内容2.项目骨架3.数据库表4.主界面5.添加6、修改7、查询8、删除 四、其它1.其他系统实现五.获取源码 一、系统介绍 本系统主要有对图书信息的增删改查操作功能。 项目类型&#xff1a;Java SE项目&…

到底还有谁学不会 MySQL 中的视图?

文章目录 MySQL中的视图视图的概念视图的用法简化查询操作提高查询效率保护数据的安全性 视图的代码示例总结 MySQL中的视图 在MySQL中&#xff0c;视图是一种虚拟表&#xff0c;它是由一个或多个基本表的行或列组成的。视图并不实际存储数据&#xff0c;而是根据定义的查询语…

6-端午练习

目录标题 6_221. 进程和线程2. 数据7>>1 6_231. 用户相关指令2. 创建用户(1. 查看id(2. 查看当前用户(3. 创建用户(4. 给新用户添加sudo权限>1 修改sudoers文件 2. 删除用户3. 修改用户名2. 磁盘1. 保证ubuntu链接上U盘(1. VM弹窗&#xff0c;选择链接到虚拟机(2. 虚拟…

DELL的交换机PowerSwitch学习手册-ONIE篇

下面是最近学习DELL的网络交换机PowerSwitch的一些笔记&#xff0c;供朋友们参考。如果还有问题&#xff0c;可以add wechat at StorageExpert。 在具体学习产品之前&#xff0c;先来了解下DELL的ONIE&#xff0c;什么是ONIE&#xff1f;和如何使用ONIE&#xff1f; ONIE是 O…

关于Nginx网站服务

目录 一、首先搭建Nginx服务 二、授权的访问控制 第一步 安装依赖包 第二步 生成用户密码认证文件 第三步 修改文件属性和权限 第四步 修改配置文件 第五步 用浏览器测试网站 三、基于IP地址进行限制 第一步 修改配置文件 第二步 用两台设备进行访问测试 四、基于域…

Linux - 内存、swap、内存回收机制

参考 2023年6月22日 https://zhuanlan.zhihu.com/p/107350459 —— 讨论的swap基于Linux4.4内核代码 内存深度科普: 从堆内存到虚拟内存管理 2023年6月22日 qbittorrent swap 问题 https://github.com/qbittorrent/qBittorrent/issues/12947 massif valgrind --toolmassif qb…

taro实现小程序地图打点

使用taro的map标签&#xff0c;往markers里放入点位&#xff1a; <map v-if"mapLoading" id"mapId":longitude"userPosition.x":latitude"userPosition.y":show-location"false":markers"markerList":scale&q…

Spring Boot 如何使用 JUL 进行日志记录

Spring Boot 如何使用 JUL 进行日志记录 在 Spring Boot 中&#xff0c;我们可以使用多种日志框架进行日志记录。其中&#xff0c;JUL (Java Util Logging) 是 Java 平台自带的日志框架&#xff0c;它提供了简单的 API 和配置&#xff0c;可以轻松地进行日志记录。本文将介绍如…

RocketMQ 常见面试题(一)

RocketMQ Broker 中的消息被消费后会立即删除吗&#xff1f; 不会&#xff0c;每条消息都会持久化到 CommitLog 中&#xff0c;每个 Consumer 连接到 Broker 后会维持消费进度信息&#xff0c;当有消息消费后只是当前Consumer 的消费进度&#xff08;CommitLog 的 offset&…

【计算机视觉】在计算机视觉里,传统卷积已经彻底输给Transformer了吗?

文章目录 一、传统卷积 & Transformer1.1 传统卷积1.2 Transformer 二、知乎高赞回答2.1 作者&#xff1a;知乎用户2.2 作者&#xff1a;王云鹤2.3 作者&#xff1a;知乎用户 一、传统卷积 & Transformer 1.1 传统卷积 传统卷积&#xff08;Traditional Convolution&…

【初识C语言(3)】选择语句+循环语句+函数+数组

文章目录 1. 选择语句2. 循环语句3. 函数4. 数组 C语言是一门结构化的程序设计语言 顺序结构&#xff1b; 选择结构&#xff1b; 循环结构。 1. 选择语句 生活中处处面临着选择&#xff0c;如果你好好学习&#xff0c;校招时拿一个好offer&#xff0c;走上人生巅峰。如果你不学…