Go语言必知必会100问题-19 浮点数溢出问题

news2024/9/30 13:27:27
问题呈现

在Go语言中,有两种浮点数类型(虚数除外):float32和float64. 浮点数是用来解决整数不能表示小数的问题。我们需要知道浮点数算术运算是实数算术运算的近似,下面通过例子说明浮点数运算采用近似值的影响以及如何提高计算精度。

var n float32 = 1.0001
fmt.Println(n * n)

上面的程序,我们的预期结果可能是 1.0001 * 1.0001 = 1.00020001。然而,实际上在大多数的x86处理器上,运行结果为 1.0002。

原因分析

如何解释这种差异呢?我们先来理解浮点数运算规则。

以float64为例,在math.SmallestNonzeroFloat64(float64的最小值)到math.MaxFloat64(float64的最大值)区间内有无穷尽个实数值。但是float64是用64个bit位表示的,将无穷尽的实数一一映射到有限的64个bit上是无法实现的。必须采用近似值的方法,丢失一些精度信息。同理对于float32类型,也是这样。

Go语言中的浮点数遵循IEEE-754标准,用部分bit位表示尾数,另一部分bit位表示指数。尾数用来表示基本值,指数将与尾数进行相乘得到的结果为最终的数值。在单精度浮点类型(float32)中,用8个bit位表示指数,23个bit位表示尾数,还有1个bit位是符号位。在双精度浮点类型(float64)中,分别用11个和52个bit位表示指数和尾数,剩下的1个bit位表示符合。可以用下面的计算公式将浮点数转为十进制数。

sign * 2^exponent * mantissa

下图是数值1.0001(float32)在IEEE-754下的计算机表示。阶码由8个bit位构成:01111111, 而阶码=原码+偏置值, 8位的偏值为:2^(8-1)-1=127. 所以原码的值为0,即exponent为0. mantissa的值为1.000100016593933. 因此它的十进制数为: 1 × 2^0 × 1.000100016593933. 原本1.0001在计算机中的存储的实际值是1.000100016593933,所以缺少精度会影响存储值的准确性。

在这里插入图片描述

解决方法

通过上面的一个具体的例子了解了浮点数在计算机中存储的是近似值。那我们在开发程序的时候需要注意什么呢?第一个需要注意的是比较操作,使用 == 运算符比较两个浮点数可能会导致不准确。我们应该比较它们的差值,看差值是否在一个小的误差内。例如,用于测试的testify(https://github.com/stretchr/testify)库有一个InDelta函数来断言两个值是否在给定的delta范围内。第二个需要注意的是浮点数的结果取决于实际的处理器。大多数处理器都有一个浮点单元(FPU)来处理这种计算,不能保证在一台机器上执行的结果在另一台具有不同FPU的机器上相同。通过比较差值是否在一定的范围内可能是跨不同机器实现有效测试的解决方案。

经验一:用好三种特殊浮点数

Go语言中还有三种特殊的浮点数:正无穷大、负无穷大、NaN(Not-a-Number)。根据IEEE-754标准,NaN是唯一满足 f!=f的浮点数。下面是创建特殊浮点数的示例。

var a float64
positiveInf := 1 / a
negativeInf := -1 / a
nan := a / a
fmt.Println(positiveInf, negativeInf, nan)
+Inf -Inf NaN

我们可以使用math库中的math.IsInf检查浮点数是否为无穷大,以及使用math.IsNaN检查浮点数是否为NaN.

经验二:注意累积放大偏差

十进制数到浮点数的转换可能存在精度下降,这是由于转换导致的错误。此外,还要注意错误可以在一系列浮点运算中累积, 通过下面这个例子进行说明。f1和f2函数以不同的顺序执行相同的操作,在f1函数中,result先被初始化为float64类型的10000, 然后在循环中每次自增1.0001。相反,f2函数先进行自增操作,然后增加10000.

func f1(n int) float64 {
    result := 10_000.
    for i := 0; i < n; i++ {
        result += 1.0001
    }
    return result
}
 
func f2(n int) float64 {
    result := 0.
    for i := 0; i < n; i++ {
        result += 1.0001
    }
    return result + 10_000.
}

在x86处理器上执行上述计算,得到结果如下。可以看到,n越大,不精确性越大。f2的精度比f1要高。

nExact resultf1f2
1010010.000110010.00099999999310010.001
1k11000.111000.09999999929311000.099999999982
1m1.0101e+061.0100999999761417e+061.0100999999766762e+06

如果对浮点数进行乘法和除法运算,结果是什么样的呢?现在假设要执行下面的运算操作:

a * ( b + c )

作所周知,运用数学分配率,上面的结果和下面的是一样的。

a * b + a * c

现在通过程序进行验证以下,看看是否如上面的预期一样。代码如下:

a := 100000.001
b := 1.0001
c := 1.0002
 
fmt.Println(a * (b + c))
fmt.Println(a*b + a*c)

运行上述程序,得到的结果如:

200030.00200030004
200030.0020003

准确的结果应该是200030.002,所以第一种计算方法得到的精度最差。事实上,当执行操作涉及加法、减法、乘法和除法时,先进行乘法和除法运算,能够获得更好的精度。虽然,这可能会影响执行时间(第二种计算方法需要3步操作,第一种方法只需两步操作),但这是执行结果准确度和执行时间之间权衡的选择。

思考总结

Go语言中float32和float64在计算机中是一种近似值表示,因此,我们必须牢记下面的规则:

  • 当比较两个浮点数时,检查它们的差值是否在可接受的范围内,而不是直接 == 进行比较

  • 当执行加法或减法时,为了获得更好的精度,可以根据运算级进行分组

  • 为了提高准确性,如果一系列运算需要加法、减法、乘法或除法,先执行乘法和除法运算

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

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

相关文章

LeetCode:143.重排链表

143. 重排链表 解题过程 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val val; }* ListNode(int val, ListNode next) { this.val val; this.next next; …

python 蓝桥杯之并查集

文章目录 总述合并过程查找过程算法实战实战1 总述 并查集&#xff08;Disjoint-set Union&#xff0c;简称并查集&#xff09;是一种用来管理元素分组情况的数据结构。它主要用于解决集合的合并与查询问题&#xff0c;通常涉及到以下两种操作&#xff1a; 合并&#xff08;Uni…

Redis基础篇:初识Redis(认识NoSQL,单机安装Redis,配置Redis自启动,Redis客户端的基本使用)

目录 1.认识NoSQL2.认识Redis3.安装Redis1.单机安装Redis2.配置redis后台启动3.设置redis开机自启 4.Redis客户端1.Redis命令行客户端2.图形化桌面客户端 1.认识NoSQL NoSQL&#xff08;Not Only SQL&#xff09;数据库是一种非关系型数据库&#xff0c;它不使用传统的关系型数…

Android14 Handle机制

Handle是进程内部, 线程之间的通信机制. handle主要接受子线程发送的数据, 并用此数据配合主线程更新UI handle可以分发Message对象和Runnable对象到主线程中, 每个handle实例, 都会绑定到创建他的线程中, 它有两个作用,: (1) 安排消息在某个主线程中某个地方执行 (2) 安排…

解放生产力,AI加持你也能做这些事!

去年网上流行一个说法叫一人企业或超级IP。一个人就是一家公司&#xff0c;可以更加专注于自身核心技能。既能对工作拥有更大的自主性和控制力&#xff0c;又能舍弃了传统公司管理等繁琐的事务工作&#xff0c;可以全面释放自己的兴趣和潜力。 这个概念给笔者留下了比较深的印…

开源的python 游戏开发库介绍

本文将为您详细讲解开源的 Python 游戏开发库&#xff0c;以及它们的特点、区别和应用场景。Python 社区提供了多种游戏开发库&#xff0c;这些库可以帮助您在 Python 应用程序中实现游戏逻辑、图形渲染、声音处理等功能。 1. Pygame 特点 - 基于 Python 的游戏开发库。…

第3章 数据链路层(1)

3.1数据链路层的功能 加强物理层传输原始比特流的功能,将可能出差错的物理连接改成逻辑上无差错的数据链路[节点的逻辑通道] 3.1.1 为网络提供服务 (1).无确认的无连接服务 适合通信质量好的有线传输链路(实时通信或误码率较低的通信信道)【例如以太网】(2).有确认的无连接服务…

WIN32部分知识介绍

&#x1f308;前言&#xff1a;此篇博客是为下一篇的《贪吃蛇》的做的前戏工作&#xff0c;这篇会讲到贪吃蛇所用到的一些工具以及函数。 首先在讲WIN32的内容时我们想了解一下他的基本概念&#xff1a; Windows 这个多作业系统除了协调应⽤程序的执⾏、分配内存、管理资源之外…

指数移动平均(EMA)

文章目录 前言EMA的定义在深度学习中的应用PyTorch代码实现yolov5中模型的EMA实现 参考 前言 在深度学习中&#xff0c;经常会使用EMA&#xff08;指数移动平均&#xff09;这个方法对模型的参数做平均&#xff0c;以求提高测试指标并增加模型鲁棒。实际上&#xff0c;_EMA可以…

全栈的自我修养 ———— css中常用的布局方法flex和grid

在项目里面有两种常用的主要布局:flex和grid布局&#xff08;b站布局&#xff09;&#xff0c;今天分享给大家这两种的常用的简单方法&#xff01; 一、flex布局1、原图2、中心对齐3、主轴末尾或者开始对其4、互相间隔 二、grid布局1、基本效果2、加间隔3、放大某一个元素 一、…

数据的加密方式及操作方法

目录 一 什么是加密 二 加密方法 对称加密&#xff08;如AES加密&#xff09; 非对称加密&#xff08;如RSA加密&#xff09; 散列&#xff08;如MD5加密&#xff09; 三 加密操作 1 MD5加密&#xff08;散列&#xff09; 2 AES加密&#xff08;对称加密&#xff09; …

HTMK5七天学会基础动画网页10(2)

制作立方体 学完前面的基础内容&#xff0c;制作立方体是个不错的练习方法&#xff0c;先看成品 再分析一下&#xff0c;六个面让每个面旋转平移就可以实现一个立方体&#xff0c;来看代码: <title> 制作立方体</title> <style> *{ margin: 0; padding: 0; …

如何搭建财务数据运营体系:基于财务五力模型的分析

在当今复杂多变的商业环境中,财务数据作为企业决策的重要参考依据,其运营体系的搭建显得尤为关键。一个健全、高效的财务数据运营体系不仅能够为企业提供准确的财务数据支持,还能帮助企业在激烈的市场竞争中保持领先地位。基于财务五力模型的分析,我们可以从收益力、安定力…

基于深度学习YOLOv8+Pyqt5的抽烟吸烟检测识别系统(源码+跑通说明文件)

wx供重浩&#xff1a;创享日记 对话框发送&#xff1a;39抽烟 获取完整源码源文件4000张已标注的数据集配置说明文件 可有偿59yuan一对一远程操作跑通 效果展示 基于深度学YOLOv8PyQt5的抽烟吸烟检测识别系统&#xff08;完整源码跑通说明文件&#xff09; 各文件说明 模型评价…

Mybatis-Plus——07,性能分析插件

性能分析插件 一、导入插件二、SpringBoot中配置环境为dev或test环境三、运行测试————————创作不易&#xff0c;笔记不易&#xff0c;如觉不错&#xff0c;请三连&#xff0c;谢谢~~ MybatisPlus也提供了性能分析插件&#xff0c;如果超过这个时间就停止运行&#xff0…

常见3大web漏洞

常见3大web漏洞 XSS攻击 描述&#xff1a; 跨站脚本&#xff08;cross site script&#xff09;-简称XSS&#xff0c;常出现在web应用中的计算机安全漏桶、web应用中的主流攻击方式。 攻击原理&#xff1a; 攻击者利用网站未对用户提交数据进行转义处理或者过滤不足的缺点。 …

前端文件上传

文件上传方式 前端文件上传有两种方式&#xff0c;第一种通过二进制blob传输&#xff08;formData传输&#xff09;&#xff0c;第二种是通过base64传输 文件相关的对象 file对象其实是blob的子类 blob对象的第一个参数必须是一个数组&#xff0c;你可以把一个file对象放进去…

Oracle SQL优化(读懂执行计划 一)

目录 SQL执行计划的作用示例演示执行计划概念介绍执行计划实例DISPLAY_CURSOR 类型DISPLAY_AWR 类型 指标详解 SQL执行计划的作用 示例演示 执行计划概念介绍 执行计划实例 DISPLAY_CURSOR 类型 DISPLAY_AWR 类型 指标详解

Vivado原语模板

1.原语的概念 原语是一种元件&#xff01; FPGA原语是芯片制造商已经定义好的基本电路元件&#xff0c;是一系列组成逻辑电路的基本单元&#xff0c;FPGA开发者编写逻辑代码时可以调用原语进行底层构建。 原语可分为预定义原语和用户自定义原语。预定义原语为如and/or等门级原语…

【电路笔记】-PNP晶体管

PNP晶体管 文章目录 PNP晶体管1、概述2、PNP晶体管电路示例3、PNP晶体管识别1、概述 PNP 晶体管与我们在上一篇教程中看到的 NPN 晶体管器件完全相反。 在这种类型的 PNP 晶体管结构中,两个互连的二极管相对于之前的 NPN 晶体管是相反的。 这会产生正-负-正类型的配置,箭头…