首页 > 其他分享 >单精度浮点数误差与消除方法

单精度浮点数误差与消除方法

时间:2024-04-10 16:55:42浏览次数:21  
标签:tmp 误差 单精度 浮点数 np array sum float32

技术背景

一个比较容易理解的概念,我们在做计算的过程中,很多时候都要做截断。不同精度的混合计算之间也会有截断,就比如一个float32单精度浮点数,符号占1位,指数占8位,尾数占23位。而一个float64双精度浮点数,符号占1位,指数占11位,尾数占52位。通常情况下,float32的有效数字约7位(按照\(2^{23+1}\)大小的范围计算),float64的有效数字为16位(按照\(2^{52+1}\)大小的范围计算)。那么问题就出现了,如果把一个双精度的浮点数转换成一个单精度的浮点数,就相当于舍弃了9位的有效数字,这就是做了一个截断。在一些特定的计算场景中,这种截断误差有可能会被累积,最终导致结果的错误。

累积误差

我们尝试用Python+Numpy来实现一个这样的示例:

import numpy as np
np.random.seed(1)
sum_1 = np.array([0.], np.float64)
sum_2 = np.array([0.], np.float32)
for _ in range(100000):
    x = np.random.random(1000)
    sum_1 += x.sum()
    sum_2 += x.astype(np.float32).sum()
print (sum_1)
print (sum_2)

输出结果为:

[50003352.04503618]
[50003708.]

虽然同样的都是做一个简单的累加操作,但是最终得到的结果是不同的。至于这个误差能否被接受,其实更多的取决于应用场景,有很多对精度要求比较敏感的计算场景会直接导致结果错误。

大数吃小数

大数吃小数说的是,在两个数字之间运算的时候,如果是两个数字差异比较大,那么其中的小数的有效数字有可能在运算中被截断。同样的我们用Python+Numpy演示一下这个场景:

import numpy as np
x = np.array([1000000.], np.float32)
y = np.array([0.01], np.float32)
print (x+y)

这个程序的执行输出为:

[1000000.]

我们发现y的贡献在这里就完全不体现,但其实如果是使用双精度浮点数进行计算的话:

import numpy as np
x = np.array([1000000.], np.float64)
y = np.array([0.01], np.float64)
print (x+y)

得到的结果为:

[1000000.01]

可以想到,如果在一个大数的基础上不断的去迭代一些小的数字,那么最终的结果也会有较大的误差,甚至有可能结果就是错的。

Kahan求和公式

最简单的来说,要解决这个问题,只要把计算精度改用双精度浮点数就可以了。但是使用双精度浮点数就意味着内存占用的翻倍,计算也会更加的耗时,而且有一些硬件可能根本就不支持使用双精度浮点数。这里还有一个方法,那就是Kahan求和公式。简单来说就是,每一步计算的误差可以归结为:(a+b)-a-b,那么就保存这个误差的计算结果,然后到下一步计算的时候再加回来就可以了。接下来把前面的两个案例都分别实现一下,首先是累加误差问题:

import numpy as np
np.random.seed(1)
sum_1 = np.array([0.], np.float64)
sum_2 = np.array([0.], np.float32)
sum_3 = np.array([0.], np.float32)
tmp_1 = np.array([0.], np.float32)
for _ in range(100000):
    x = np.random.random(1000)
    sum_1 += x.sum()
    sum_2 += x.astype(np.float32).sum()
    tmp_2 = x.astype(np.float32).sum() - tmp_1
    tmp_3 = sum_3 + tmp_2
    tmp_1 = (tmp_3 - sum_3) - tmp_2
    sum_3 = tmp_3
print (sum_1)
print (sum_2)
print (sum_3)

该程序输出结果为:

[50003352.04503618]
[50003708.]
[50003352.]

可以看到,在使用了Kahan求和公式之后,虽然还是使用的float32单精度浮点数,但其实结果精度已经比普通的单精度计算高了两个量级。另外再测试一下大数加小数的问题,这里我们也使用累加的形式测试,结果展示会更加明显一些:

import numpy as np
np.random.seed(1)
sum_1 = np.array([1000000.], np.float64)
sum_2 = np.array([1000000.], np.float32)
sum_3 = np.array([1000000.], np.float32)
tmp_1 = np.array([0.], np.float32)
for _ in range(100000):
    x = np.random.random(1000) * 1e-05
    sum_1 += x.sum()
    sum_2 += x.astype(np.float32).sum()
    tmp_2 = x.astype(np.float32).sum() - tmp_1
    tmp_3 = sum_3 + tmp_2
    tmp_1 = (tmp_3 - sum_3) - tmp_2
    sum_3 = tmp_3
print (sum_1)
print (sum_2)
print (sum_3)

输出结果为:

[1000500.03352045]
[1000000.]
[1000500.06]

这里我们看到一点是,如果不使用Kahan求和公式,这个小数即使被迭代100000次,也还是被忽略。而使用了Kahan求和公式之后,虽然还是略有误差,但是误差位已经超过了float32单精度浮点数第7位有效数字的范围,因此Kahan求和公式的精度还是非常高的。

总结概要

在使用浮点数计算时,尤其是在使用AI框架的过程中,我们往往使用的是float32单精度浮点数,这也跟GPU的硬件架构有关系。但是使用单精度浮点数的过程中,务必要考虑到累加误差和大数吃小数的问题,这两个问题在长时间的迭代过程中,有可能会直接导致计算结果就是错误的。而如果在计算的过程中使用Kahan求和公式,则可以避免这种大数吃小数的问题。Kahan求和公式的本质,就是把大数和小数分开进行计算,这样可以一定程度上达到接近于float64双精度浮点数的运算精度。

版权声明

本文首发链接为:https://www.cnblogs.com/dechinphy/p/float32_error.html

作者ID:DechinPhy

更多原著文章:https://www.cnblogs.com/dechinphy/

请博主喝咖啡:https://www.cnblogs.com/dechinphy/gallery/image/379634.html

标签:tmp,误差,单精度,浮点数,np,array,sum,float32
From: https://www.cnblogs.com/dechinphy/p/18126246/float32_error

相关文章

  • 关于浮点数转整数不准确问题
    #include<stdio.h>#include<string.h>#include<dirent.h>#include<sys/types.h>#include<sys/stat.h>#include<stdlib.h>#include<fcntl.h>#include<unistd.h>#include<iostream>usingnamespace......
  • 浮点数比较,但是不用 eps
    License:CCBY-SA4.0乘法把所有数扩大\(10^n\)倍然后当成整数做,可能要用到__int128.(最后输出答案别忘了除回来)缺点就是除法可能还是会爆精度,并且不支持开根之类的操作。模法突发奇想想到的一种方法.大概思想就是同时记录浮点数和它对某个数取模后的值.classNumber{......
  • 浮点数转定点数(自编函数)
    方法:1、计算b=a*2^F,其中F是变量的分数长度,b是用十进制表示的。2、将所得b的值四舍五入到最接近的整数值。3、将(2)所得的b从十进制转换为二进制表示,并命名为新变量c。4、现在,假设c,需要n位来表示二进制中b的值。另一方面,通过模拟得到了W和F的值。所以W的值应该是等于或大于n。如果为......
  • 整型之韵,数之舞:大小端与浮点数的内存之旅
    ✨✨欢迎......
  • 定点数和浮点数
    定点数和浮点数定点数定点数:表示数据时小数点的位置固定不变。定点整数:纯整数,小数点在最低有效数值位之后。定点小数:纯小数,小数点在最高有效数值位之前。浮点数浮点数:小数点位置不固定的数。浮点表示法:N=2的E次方*F其中:E位阶码,F位尾数。阶码通常为带符号的纯......
  • Python与供应链-2预测误差及指数平滑需求预测模型
    主要介绍预测误差和指数平滑模型的相关理论,然后再通过Python的statsmodels封装的指数平滑函数预测需求。1预测误差预测误差是指预测结果与预测对象发展变化的真实结果之间的差距。这种误差分为绝对误差和相对误差。绝对误差是预测值与实际观测值的绝对差距,而相对误差则是这种......
  • 57文章解读与程序——《考虑预测误差不确定性的含风电机组组合研究》已提供下载资源
    ......
  • 硬件组成-CPU-编码-浮点数
    计算机的基本硬件系统由运算器、控制器、存储器、输入设备和输出设备5大部件组成。运算器、控制器等部件被集成在一起统称为中央处理单元(CPU)。CPU是硬件系统的核心,用于数据的加工处理,能完成各种算术、逻辑运算及控制功能。存储器是计算机系统中的记忆设备,分为内部存储器和......
  • C语言中整数和浮点数在内存中的存储
    一、整数在内存中的存储我们知道整数的二进制表示形式有三种,原码、反码、补码。三种表示方法均由符号位和数值位两部分构成,符号位用0表示正,用1表示负,最高一位被当作符号位,其余位被当作数值位。其中,正整数的原码、反码、补码三种表示相同负整数的原码:对应10进制数直接转换过......
  • C#避坑指南-浮点数与整数计算的比较
    浮点数与整数计算的比较在软件开发过程中,我们经常需要进行数值计算来解决各种问题。然而,整数和浮点数在计算过程中存在一些差异,特别是在涉及除法和精度方面。本篇博客将通过一个具体的例子来比较整数和浮点数计算的区别,并说明为什么在某些情况下,应该使用浮点数进行计算以获得更......