如何提高浮点类型计算的精度
把下面这篇文章的表达方式改成像正常的人类作者写的,而不是AI写的。
——————
如何提高浮点类型计算的精度
在后端开发中,浮点数的计算一直一个常见难题,特别是在需要与GPU协作进行复杂计算时,浮点精度的偏差可能带来预期之外的结果。例如,在图形渲染、科学计算和物理模拟等场景中,GPU通常用于加速复杂的数学运算,而这些运算依赖于浮点数的精度。如果浮点精度不足,可能导致图像渲染出现瑕疵、模拟结果失真等问题,从而影响最终系统的表现。本文将围绕如何提高浮点类型的计算精度展开,结合代码示例,帮助大家理解并应对这一常见问题。
浮点数的存储与精度问题
在计算机中,浮点数的存储方式是通过IEEE 754标准来定义的,这种标准于1985年被引入,用于统一浮点数的表示方法,解决不同计算机系统之间浮点运算结果不一致的问题。这种存储方式的核心是将数值分为符号位、指数位和尾数位。对于32位浮点数(即float类型),其存储结构如下:
- 符号位(1位):表示正负号
- 指数位(8位):用于表示数值的范围
- 尾数位(23位):用于存储有效数值
这种表示方式使得浮点数可以表示非常大的范围,但也引入了精度上的限制。当数值位数超出尾数位能够表示的范围时,就会产生舍入误差,这就是很多工程师在进行浮点数运算时遇到的精度问题的根源。
浮点数计算误差示例
在浮点数计算中,误差的来源通常是由于有限位数存储导致的小数舍入误差。例如,考虑以下代码示例:
c
float a = 2.f; // 初始值 a
float t = 0.0000025f; // 增量 t
float b = a + t; // 将 a 与 t 相加,结果存储在 b 中
float c = b - a; // 计算 b 与 a 之间的差值,结果存储在 c 中
printf(“b = %.8f\n”, b); // 输出 b 的值,精确到小数点后 8 位
printf(“c = %.8f\n”, c); // 输出 c 的值,精确到小数点后 8 位
输出结果为:
b = 2.00000238
c = 0.00000238
我们可以看到,期望的结果应该是b = 2.0000025和c = 0.0000025,但是由于浮点数的精度问题,结果产生了偏差。要解决这一问题,我们需要深入理解并应用一些方法来尽量减少浮点数计算中的误差。
提高浮点计算精度的方法
1. 使用更高精度的数据类型
如果计算允许,可以使用更高精度的数据类型来替代float。例如,可以使用double类型,其提供了64位的存储,能够表示更高的精度。然而,使用double也伴随着一定的性能和内存开销。在现代硬件上,double的计算速度通常比float慢,尤其是在GPU上,因为许多GPU的硬件对double的支持有限。此外,double类型占用的内存是float的两倍,这意味着在大规模数据处理中,内存消耗和带宽压力也会增加。因此,在决定是否使用double时,需要权衡精度需求与性能和内存的开销。在您的场景中,由于要将数据传输到GPU,而OptiX库仅接受float作为顶点坐标类型,因此无法直接使用double。
尽管如此,在数据传输到GPU之前,可以考虑在CPU端使用double进行计算,减少中间过程中的误差,然后在最后将结果转换为float。这种方式虽然不能完全避免精度损失,但能够将累积的误差降到最低。
2. 使用Kahan求和算法
Kahan求和算法是一种减少浮点数累积误差的方法,尤其在进行多次累加操作时非常有效。它通过在每次加法操作中对舍入误差进行补偿,从而使最终的结果更加精确。
以下是一个简单的Kahan求和算法实现:
c
float kahan_sum(float* values, int length) {
float sum = 0.0f;
float compensation = 0.0f;
for (int i = 0; i < length; i++) {
float y = values[i] - compensation;
float t = sum + y;
compensation = (t - sum) - y;
sum = t;
}
return sum;
}
Kahan求和算法在每次加法操作中,通过计算上次舍入误差的补偿值,将其应用于下一次运算,从而显著降低了浮点数累加时的误差。
3. 调整计算顺序
在浮点数运算中,计算顺序的不同可能会带来不同的精度结果。一般来说,先进行相对较大的数之间的运算,再进行小数值的加减操作,可以减少误差累积。例如,考虑以下代码:
c
float a = 1000000.0f;
float b = 0.0001f;
float result1 = (a + b) - a; // 先加后减
float result2 = b + (a - a); // 先减后加
printf("result1 = %.8f
", result1);
printf("result2 = %.8f
", result2);
在这种情况下,result1的值会因为先进行了大数相加而丢失掉部分精度,而result2的结果则为预期的0.0001,因为先进行了大数相减,得到了零,避免了精度损失。
在原始代码中,a + t的操作是将一个较大的数与一个非常小的数相加,这就很容易产生精度损失。考虑重新安排计算顺序,或者将计算拆分为多个步骤,以尽量减少精度损失。
4. 使用定点数替代浮点数
在某些场景下,可以使用定点数来替代浮点数。定点数将数值放大为整数进行运算,从而避免了浮点数精度的问题。比如,如果对0.0000025的精度要求很高,可以将其放大为2500,然后在计算结束后再除以相应的倍数。
以下是一个使用定点数的示例:
c
int a = 2000000; // 将2.0放大为2000000
int t = 25; // 将0.0000025放大为25
int b = a + t; // 进行整数运算,结果为2000025
float result = b / 1000000.0f; // 将结果缩小回原来的比例
printf("Result: %.8f
", result); // 输出结果,精确到小数点后 8 位
在这个示例中,我们将浮点数放大为整数来进行运算,从而避免了浮点数在小数位上的舍入误差。定点数的优点是可以消除浮点运算中的精度问题,尤其适用于对小数部分精度要求高的计算。其缺点是可能会导致整数溢出,尤其是在处理非常大的数值时。此外,代码的可读性和复杂性也会有所增加,需要额外的逻辑来进行缩放和还原。
定点数的使用虽然可以消除浮点运算的精度问题,但需要格外注意可能出现的溢出问题,并且需要在代码中增加额外的处理逻辑。
5. GPU计算中的浮点精度优化
由于您提到要在GPU中使用浮点数进行计算,因此可以考虑一些GPU计算的优化方法:
- 混合精度计算:在一些现代GPU中,支持混合精度计算,即在保持计算速度的同时,通过混合使用float和double来提高精度。在OptiX中,可以将一些关键的计算操作暂时使用double处理,然后将结果转换为float以满足API要求。
- 精度模式控制:某些GPU编程平台(如CUDA)提供了对精度模式的控制,可以设置更高的计算精度模式来减少误差。不过,这可能会以一定的性能开销为代价。以下是一个在CUDA中设置精度模式的代码示例:
cpp
#include <cuda_fp16.h>
global void compute_kernel() {
// 设置CUDA的浮点数精度模式为高精度
asm volatile (“fma.rn.f32 %0, %1, %2, %3;” : : “f”(a), “f”(b), "f"©);
}
int main() {
// 在CUDA内核中调用计算函数
compute_kernel<<<1, 1>>>();
cudaDeviceSynchronize();
return 0;
}
在这个示例中,我们通过内嵌汇编的方式手动设置了浮点数的精度模式为舍入最近的方式(rn),这样可以确保在关键的计算中使用更高精度的模式来减少误差。
6. 分块计算与减少误差累积
在涉及大量数据的计算中,可以采用分块计算的方法,将整个计算过程分为多个较小的部分来逐步完成。这样做的好处是可以减少误差在整个计算过程中的累积。例如,在对大量浮点数进行求和时,可以将数据分块,每块数据使用Kahan求和算法来计算,然后再合并各个块的结果。一个实际的应用场景是大规模数据分析中的数据聚合操作,例如在计算海量传感器数据的总和时,可以将数据按时间段或地理区域分块,每个块在单独的线程或GPU内核中并行计算,然后再将所有块的结果进行最终汇总。这样既能充分利用并行计算的能力,又能有效减少累积误差。
这种方法特别适合于需要在GPU上并行处理的场景,因为GPU擅长并行计算,分块的计算可以充分利用GPU的计算资源,提高计算的精度和效率。
7. 使用多重精度库
在某些情况下,可以使用专门设计的多重精度库来解决浮点精度问题。例如,MPFR和Arb等开源多重精度库可以提供比标准float和double更高的精度。在需要极高精度的场景中,例如科学研究和工程计算,多重精度库可以显著减少误差累积并提高计算的可靠性。
这些库通过支持任意精度的小数运算,为开发人员提供了更大的灵活性,可以根据需求选择精度等级。这种方法通常应用于那些要求极高精度但对性能要求不那么苛刻的领域。
浮点数精度问题的可视化
为了更好地理解浮点数精度损失,我们可以通过可视化手段来分析。以下是一个简单的Python脚本,用于绘制浮点数在累加过程中出现的误差情况:
python
import matplotlib.pyplot as plt
import numpy as np
定义累加次数
n = 10000
定义每次累加的浮点数
increment = 0.0000025
使用普通累加与Kahan求和算法进行累加
normal_sum = 0.0
kahan_sum = 0.0
compensation = 0.0
normal_sums = []
kahan_sums = []
for i in range(n):
# 普通累加
normal_sum += increment
normal_sums.append(normal_sum)
# Kahan求和算法累加
y = increment - compensation
t = kahan_sum + y
compensation = (t - kahan_sum) - y
kahan_sum = t
kahan_sums.append(kahan_sum)
绘制结果
plt.plot(range(n), normal_sums, label=‘Normal Sum’)
plt.plot(range(n), kahan_sums, label=‘Kahan Sum’, linestyle=‘–’)
plt.xlabel(‘Iteration’)
plt.ylabel(‘Sum Value’)
plt.title(‘Floating Point Accumulation Error’)
plt.legend()
plt.show()
上面的代码使用matplotlib绘制了普通累加与Kahan求和算法在累加过程中的误差变化情况。可以看到,普通累加随着迭代次数的增加,误差逐渐累积,而Kahan求和算法则有效地降低了这一误差。
实际应用中的浮点精度优化案例
在实际的工程项目中,浮点精度问题可能会影响到各个方面,例如金融计算、物理模拟、科学计算和图像处理等。以下是一些实际应用中的浮点精度优化案例:
-
金融系统中的精度问题
在金融系统中,金额的计算需要极高的精度。使用浮点数可能会导致金额计算结果出现细微误差,从而引发严重的问题。因此,在金融系统中,通常会采用定点数或者BigDecimal(在Java中)来确保计算的精度。 -
物理模拟中的精度优化
在物理模拟中,例如流体动力学或者刚体动力学,精度问题会影响模拟结果的稳定性和真实性。为了解决这一问题,通常会在计算的关键步骤中使用双精度浮点数,或者采用混合精度的方法来平衡性能和精度。 -
图像处理中的累积误差
在图像处理和计算机视觉中,像素值的累积计算可能会导致误差的逐渐放大,影响最终的图像质量。在这种情况下,使用Kahan求和算法或者调整计算顺序,确保对误差进行补偿,能够显著提升图像处理的效果。 -
机器学习中的精度控制
在机器学习的训练过程中,特别是深度学习中,浮点精度问题可能会影响模型的收敛速度和最终的性能。在某些情况下,可以使用混合精度训练,即部分使用float16进行权重更新,部分使用float32来保持模型的精度,从而在保证训练效果的同时提高运算速度。
总结
浮点数的精度问题在计算机科学中是一个普遍存在的问题,特别是在涉及到GPU计算和科学计算的场景中更为突出。随着计算硬件和软件的发展,越来越多的研究致力于改进浮点数的精度。例如,某些前沿研究正在探索新的浮点数表示方法,如Posit数,这种方法有望在保持存储效率的同时提高计算精度。此外,量子计算和高精度数学库的进步也可能在未来帮助减轻浮点精度问题。为了尽可能提高浮点数计算的精度,我们可以考虑以下几种方法:
- 使用更高精度的数据类型(如double),在计算完成后再转换为float。
- 使用Kahan求和算法减少累加过程中的舍入误差。
- 调整计算顺序,减少误差的累积。
- 使用定点数代替浮点数进行计算。
- 在GPU计算中使用混合精度或精度模式控制等优化手段。
- 采用分块计算的方法来减少误差的累积。
- 使用多重精度库来应对极高精度要求的场景。
通过合理地选择和组合这些方法,可以有效地降低浮点数计算中的精度损失,得到更加精确的计算结果。希望能够帮助各位开发人员在实际项目中更好地应对浮点精度问题,从而提高系统的稳定性和可靠性。
相关文章:
如何提高浮点类型计算的精度
把下面这篇文章的表达方式改成像正常的人类作者写的,而不是AI写的。 —————— 如何提高浮点类型计算的精度 在后端开发中,浮点数的计算一直一个常见难题,特别是在需要与GPU协作进行复杂计算时,浮点精度的偏差可能带来预期之…...
RabbitMQ简介及安装类
RabbitMQ概述-MQ介绍 RabbitMQ是一个开源的消息代理和队列服务器,它支持多种消息协议,并且可以轻松地与多种编程语言和框架集成。RabbitMQ是使用Erlang语言编写的,因此它具有高并发和高可用性的特点。以下是RabbitMQ的一些关键特性和概念 消息…...
游戏服务器防御策略:防止玩家因DDoS攻击而掉线
在网络游戏环境中,玩家体验至关重要。然而,DDoS(分布式拒绝服务)攻击是导致玩家在游戏中频繁掉线的一个重要原因。本文将探讨如何通过一系列技术和策略来减轻DDoS攻击的影响,保障玩家的游戏体验。 一、引言 DDoS攻击是…...
Django学习笔记二:数据库操作详解
Django框架提供了一个功能强大的ORM(对象关系映射)系统,使得开发者可以使用Python代码来操作数据库,而无需编写复杂的SQL语句。以下是Django数据库操作的一些基本概念和方法: 模型定义 在Django中,模型是…...
Spring Boot 应用开发案例:在线书籍管理系统
Spring Boot 应用开发案例,涵盖从项目搭建到具体实现,包括常用的数据库操作、Restful API 开发、Spring Security 安全认证以及前后端分离架构。这个案例将以开发一个简单的“在线书籍管理系统”为例,介绍如何从头开始搭建项目、实现增删改查功能,并通过 JWT 进行用户认证。…...
阿里140滑块-滑块验证码逆向分析思路学习
一、声明! 原创文章,请勿转载! 本文内容仅限于安全研究,不公开具体源码。维护网络安全,人人有责。 文章中所有内容仅供学习交流使用,不用于其他任何目的,均已做脱敏处…...
使用 Linux 搭建并配置一个 NFS 服务器
目录 1. 安装相关软件包2. 启动并启用相关服务3. NFS 配置文件3.1 /etc/exports 文件3.2 共享目录3.3 客户端列表3.4 参数选项3.4.1 访问权限选项3.4.2 数据写入硬盘模式3.4.3 root 用户权限3.4.4 子树检查3.4.5 匿名用户3.4.6 安全性(特权端口)3.4.7 网络传输3.4.x 更多 (More…...
python发包
Python 中我们经常会用到第三方的包,默认情况下,用到的第三方工具包基本都是从 Pypi.org 里面下载。这些第三方的包都是开发者们发布的自己的库。我们有自己的想法,或者有一些常用的方法想要分享出去,就可以发布自己的库ÿ…...
农行1面:说说 final,finally,finalize的区别
你好,我是猿java。 在 Java中,“final”、“finally”和“finalize”是三个不同的关键字或方法,尽管它们的名字相似,但在功能和用途上却有显著的区别,这篇文章我们继续分析一篇农行1面的题目:说说 final&a…...
ChatGPT实时语音将于本周向免费用户推出:OpenAI DevDay 2024详细解读
大家好,我是木易,一个持续关注AI领域的互联网技术产品经理,国内Top2本科,美国Top10 CS研究生,MBA。我坚信AI是普通人变强的“外挂”,专注于分享AI全维度知识,包括但不限于AI科普,AI工…...
一个月学会Java 第7天 字符串与键盘输入
Day7 字符串与键盘输入 字符串作为所有程序都很重要的东西,这个东西必须好好的学习,在Java中String会比较好学习,比起C和C里面会更加的简单, 在Java之中也是很好理解的,因为底层已经封装好了char数组,也就是…...
Java后端面试题(day16)
目录 java常见的引用类型java中深拷贝和浅拷贝如何设计一个秒杀系统?谈一下对高并发的理解,平时怎么处理高并发问题?Comparable和Comparator区别?解决hash冲突有哪些方法?Synchronized锁的升级过程 java常见的引用类型 java的引用类型一般分…...
Auto-Animate:是一款零配置、即插即用的动画工具,可以为您的 Web 应用添加流畅的过渡效果
嗨,大家好,我是小华同学,关注我们获得“最新、最全、最优质”开源项目和高效工作学习方法 用户体验成为了检验产品成功与否的关键因素。而动画效果,作为提升用户体验的重要手段,在网页和应用开发中扮演着举足轻重的角色…...
k8s之ingress-nginx-controller安装
作者:程序那点事儿 日期:2024/01/30 01:25 要在master节点上安装 helm repo add ingress-nginx Welcome - Ingress-Nginx Controller helm search repo ingress-nginx helm pull ingress-nginx/ingress-nginx --version 4.4.2 mv ingress-nginx-4.4.…...
力扣SQL仅数据库(1098~1132)
1098 小众书籍 需求 编写解决方案,筛选出过去一年中订单总量 少于 10 本 的 书籍,并且 不考虑 上架距今销售 不满一个月 的书籍 。假设今天是 2019-06-23 。 返回结果表 无顺序要求 。 数据准备 Create table If Not Exists Books (book_id int, nam…...
优惠点餐api接口对接的具体步骤是什么?
优惠点餐API接口对接的具体步骤通常包括以下几个阶段: 需求分析:明确对接的目标和需求,例如实现在线点餐、订单管理、支付集成等 。选择API服务提供商:根据业务需求选择合适的点餐API服务提供商 。注册和获取API密钥:…...
【韩顺平Java笔记】第8章:面向对象编程(中级部分)【297-313】
文章目录 297. super基本语法297.1 基本介绍297.2 基本语法 298. super使用细节1299. super使用细节2300. super使用细节3301. 方法重写介绍302. 方法重写细节303. 重写课堂练习1304. 重写课堂练习2输出结果: 姓名:田所浩二 年龄:24305. 养宠物引出多态3…...
快递批量查询物流追踪只揽收无物流信息的单号
在电子商务和物流领域,快递单号的追踪是确保货物顺利送达的关键环节。然而,在实际操作中,经常会遇到一些只显示揽收信息而没有后续物流更新的单号,这给商家和买家都带来了不小的困扰。本文将介绍如何通过快递批量查询物流的方法&a…...
【动态网站资源保存下载】
文章目录 概要解决思路技术细节小结 概要 我们在网上浏览网站时,经常有这样的需求:将浏览的网页保存下来,即使无网的情况下也可以继续浏览。比如一些教育类网站的PPT,内容为HTML格式的,无法作为PPT格式下载下来&#…...
Selenium自动化测试中如何处理数据驱动?
在自动化测试中,数据驱动(Data-Driven Testing)是指通过外部数据源(如Excel、CSV、数据库等)来控制测试用例的执行,而不是直接在代码中硬编码数据。这种方式可以提高测试的灵活性和可维护性,使得…...
Prompt Tuning、P-Tuning、Prefix Tuning的区别
一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...
C++:std::is_convertible
C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...
高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...
【配置 YOLOX 用于按目录分类的图片数据集】
现在的图标点选越来越多,如何一步解决,采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集(每个目录代表一个类别,目录下是该类别的所有图片),你需要进行以下配置步骤&#x…...
第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词
Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid,其中有多少个 3 3 的 “幻方” 子矩阵&am…...
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...
【Linux】Linux安装并配置RabbitMQ
目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的,需要先安…...
数据结构:泰勒展开式:霍纳法则(Horner‘s Rule)
目录 🔍 若用递归计算每一项,会发生什么? Horners Rule(霍纳法则) 第一步:我们从最原始的泰勒公式出发 第二步:从形式上重新观察展开式 🌟 第三步:引出霍纳法则&…...
FTXUI::Dom 模块
DOM 模块定义了分层的 FTXUI::Element 树,可用于构建复杂的终端界面,支持响应终端尺寸变化。 namespace ftxui {...// 定义文档 定义布局盒子 Element document vbox({// 设置文本 设置加粗 设置文本颜色text("The window") | bold | color(…...
CSS 工具对比:UnoCSS vs Tailwind CSS,谁是你的菜?
在现代前端开发中,Utility-First (功能优先) CSS 框架已经成为主流。其中,Tailwind CSS 无疑是市场的领导者和标杆。然而,一个名为 UnoCSS 的新星正以其惊人的性能和极致的灵活性迅速崛起。 这篇文章将深入探讨这两款工具的核心理念、技术差…...
