我不断得到浮点数(即float
,double
或long double
)是否只有一个精度值,或者具有精确值的混合答案而变化。
一个名为float vs. double precision的主题似乎意味着浮点精度是绝对的。
然而,另一个名为Difference between float and double的主题是
一般情况下,double有 15到16 精度的十进制数字
另一个source说,
float类型的变量通常具有约 7位有效数字的精度
double类型的变量通常具有约 16位有效数字的精度
如果我正在使用敏感代码,当我的值不准确时,我不喜欢引用上面的近似值。所以让我们记录下来吧。浮点精度是可变的还是不变的,为什么?
答案 0 :(得分:28)
精度是固定的,对于双精度,精确度为正好为53位二进制数(如果我们排除隐式前导1,则为52)。这是大约15位小数。
OP要求我详细说明为什么正好有53个二进制数字意味着“大约”15个十进制数字。
为了直观地理解这一点,让我们考虑一种不太精确的浮点格式:而不是像双精度数字那样的52位尾数,我们只是使用4位尾数。
所以,每个数字看起来都像:( - 1) s ×2 yyy ×1.xxxx(其中s
是符号位,{ {1}}是指数,yyy
是规范化的尾数)。对于即时讨论,我们只关注尾数而不是符号或指数。
这是一个1.xxxx
所有1.xxxx
值的表格(所有舍入都是一半到偶数,就像默认的浮点舍入模式一样):
xxxx
您说提供了多少个十进制数字?你可以说2,因为两位小数位范围内的每个值都被覆盖,尽管不是唯一的;或者你可以说3,它涵盖了所有唯一值,但不提供三位小数范围内所有值的覆盖率。
为了论证,我们会说它有2位小数:小数精度将是可以表示这些小数位的所有值的位数。
好的,那么,如果我们将所有数字减半(所以我们使用 xxxx | 1.xxxx | value | 2dd | 3dd
--------+----------+----------+-------+--------
0000 | 1.0000 | 1.0 | 1.0 | 1.00
0001 | 1.0001 | 1.0625 | 1.1 | 1.06
0010 | 1.0010 | 1.125 | 1.1 | 1.12
0011 | 1.0011 | 1.1875 | 1.2 | 1.19
0100 | 1.0100 | 1.25 | 1.2 | 1.25
0101 | 1.0101 | 1.3125 | 1.3 | 1.31
0110 | 1.0110 | 1.375 | 1.4 | 1.38
0111 | 1.0111 | 1.4375 | 1.4 | 1.44
1000 | 1.1000 | 1.5 | 1.5 | 1.50
1001 | 1.1001 | 1.5625 | 1.6 | 1.56
1010 | 1.1010 | 1.625 | 1.6 | 1.62
1011 | 1.1011 | 1.6875 | 1.7 | 1.69
1100 | 1.1100 | 1.75 | 1.8 | 1.75
1101 | 1.1101 | 1.8125 | 1.8 | 1.81
1110 | 1.1110 | 1.875 | 1.9 | 1.88
1111 | 1.1111 | 1.9375 | 1.9 | 1.94
= -1)会发生什么?
yyy
按照与以前相同的标准,我们现在处理1个十进制数字。因此,你可以看到,根据指数,你可以有更多或更少的十进制数字,因为二进制和十进制浮点数不会相互干净地映射。
同样的参数适用于双精度浮点数(使用52位尾数),只有在这种情况下,根据指数得到15或16位小数。
答案 1 :(得分:24)
所有现代计算机都使用二进制浮点运算。这意味着我们有一个二进制尾数,单精度通常为24位,双精度为53位,扩展精度为64位。 (扩展精度在x86处理器上可用,但在ARM或其他类型的处理器上不可用。)
24,53和64位尾数意味着对于2 k 和2 k + 1 之间的浮点数,下一个更大的数字是2 k-23 ,2 k-52 和2 k-63 。这是决议。每个浮点运算的舍入误差最多为其一半。
那么这又如何转化为十进制数? 取决于。
取k = 0且1≤x<1。 2.分辨率为2 -23 ,2 -52 ,2 -63 ,约为1.19×10 -7 < / sup>,2.2×10 -16 和1.08×10 -19 。这有点小于7,16和19位小数。然后取k = 3和
8≤x<1。 16.两个浮点数之间的差异现在大8倍。对于8≤x<1。 10你得到的只是6,小于15,小于18。但是对于10≤x<10。 16你得到一个小数点多一点!
如果x仅小于2 k + 1 并且只有一点多于10 n ,则得到最高的十进制数,例如1000≤ x&lt; 1024.如果x略高于2 k 且小于10 n ,则得到最小的十进制数,例如 1 / 1024 ≤x&lt; 1 / 1000 。相同的二进制精度可以产生十进制精度,最多可达1.3位数或log 10 (2×10)。
当然,你可以阅读文章“What every computer scientist should know about floating-point arithmetic。”
答案 2 :(得分:9)
使用其硬件协处理器(最初为8087)的80x86代码提供三个级别的精度:32位,64位和80位。那些非常接近1985年的IEEE-754 standard。最近的标准规定了128-bit format。浮点格式有24,53,65和113个尾数位,分别对应精度为7.22,15.95,19.57和34.02的十进制数字。
公式为mantissa_bits / log_2 10,其中10的对数基数为3.321928095。
虽然任何特定实现的精度确实不不同,但是当浮点值转换为十进制时,它可能会出现。请注意,值moduleA/
没有精确的二进制表示。这是一个重复位模式(0.0001100110011001100110011001100 ...),就像我们习惯于十进制一样,0.3333333333333大约是1/3。
许多语言通常不支持80位格式。某些C编译器可能会提供0.1
,它使用80位浮点数或128位浮点数。唉,它也可能使用64位浮点数,具体取决于实现方式。
NPU具有80位寄存器,并使用完整的80位结果执行所有操作。在NPU堆栈内计算的代码可从此额外精度中受益。遗憾的是,代码生成不良或代码编写不当可能会通过将中间计算存储在32位或64位变量中来截断或舍入中间计算。
答案 3 :(得分:8)
浮点精度是否可变或不变,为什么?
通常,给定相同2次幂范围内的任何数字,浮点精度是不变的 - 固定值。绝对精度随每个2阶功率而变化。在整个FP范围内,精度大致相对于幅度。将此相对二进制精度与小数精度相关联会导致摆动在DBL_DIG
和DBL_DECIMAL_DIG
十进制数字之间变化 - 通常为15到17。
什么是精度?使用FP,讨论相对精度是最有意义的。
浮点数的格式为:
签署*有效* pow(基数,指数)
它们具有对数分布。 about 在100.0和3000.0(范围为30x)之间有多个不同的浮点数,因为它们介于2.0和60.0之间。无论底层存储表示如何,都是如此。
1.23456789e100
与1.23456789e-100
具有相同的相对精度。
大多数计算机将double
实现为binary64。此格式具有53位二进制精度。
1.0和2.0之间的n
数字与((2.0-1.0)/ pow(2,52)中的1份绝对精度相同。
64.0和128.0之间的数字,也是n
,在((128.0-64.0)/ pow(2,52)中具有相同的绝对精度1。
即使是2之间的数字组,也具有相同的绝对精度。
在FP数的整个正常范围内,这近似于均匀的相对精度。
当这些数字表示为十进制时,精度摆动:数字1.0到2.0的绝对精度比数字2.0到4.0多1位。比4.0到8.0还要多2位
C提供DBL_DIG
,DBL_DECIMAL_DIG
及其float
和long double
对应方。 DBL_DIG
表示最小相对小数精度。 DBL_DECIMAL_DIG
可以被认为是最大相对小数精度。
通常这意味着给定double
将具有15到17个十进制数字的精度。
考虑1.0
及其下一个可表示的double
,数字在第17个有效十进制数字之前不会改变。每个下一个double
都是pow(2,-52)
或大约2.2204e-16
。
/*
1 234567890123456789 */
1.000000000000000000...
1.000000000000000222...
现在将"8.521812787393891"
及其下一个可表示的数字视为十进制字符串,使用16位有效十进制数字。转换为double
的这两个字符串都是相同的 8.521812787393891142073699...
,即使它们在第16位数字上有所不同。说这个double
有16位数的精度被夸大了。
/*
1 234567890123456789 */
8.521812787393891
8.521812787393891142073699...
8.521812787393892
答案 4 :(得分:6)
不,它是可变的。起点是非常弱的IEEE-754标准,它只能确定浮动指针数的格式,因为它们存储在内存中。单精度可以计算7位精度,双精度可以计算15位数。
但该标准的一个主要缺陷是它没有具体说明如何进行计算。而且遇到了麻烦,特别是英特尔8087浮点处理器给程序员带来了许多不眠之夜。该芯片的一个重要设计缺陷是它存储的浮点值的值比存储器格式的更多位。设计选择背后的理论是,这允许中间计算更精确并且导致更少的舍入误差。
听起来不错,但实际上并没有好转。编译器编写器将尝试生成代码,以尽可能长地保留存储在FPU中的中间值。对代码速度很重要,将值存储回内存是很昂贵的。麻烦的是,他经常必须存储值,FPU中的寄存器数量有限,代码可能跨越函数边界。此时值被截断并失去很多精度。对源代码的微小更改现在可以产生截然不同的值。此外,程序的非优化构建产生与优化程序不同的结果。以完全不可诊断的方式,您必须查看机器代码以了解结果不同的原因。
英特尔重新设计了处理器以解决此问题,SSE指令集使用与内存格式相同的位数进行计算。然而,慢慢地接受,重新设计编译器的代码生成器和优化器是一项重大投资。三大C ++编译器都已切换。但是,例如,.NET Framework中的x86抖动仍会生成FPU代码,它始终会生成。
然后存在系统误差,失去精度是转换和计算的必然副作用。转换首先,人类在基数10中工作,但处理器使用基数2.我们使用的漂亮的圆数,如0.1,无法在处理器上转换为漂亮的整数。 0.1作为10的幂之和是完美的,但是没有2的幂的有限和产生相同的值。转换它会产生无限数量的1和0,就像你不能完全写下10/3那样。所以它需要被截断以适应处理器并产生一个值得关闭的值。从十进制值+/- 0.5位。
计算会产生错误。乘法或除法将结果中的位数加倍,将其四舍五入以使其适合存储的值会产生+/- 0.5位错误。减法是最危险的操作,可能会导致有效数字的 lot 丢失。如果您计算1.234567f - 1.234566f,那么结果只剩下1位有效数字。这是一个垃圾结果。总结具有几乎相同值的数字之间的差异在数值算法中非常常见。
获得过多的系统错误最终是数学模型中的一个缺陷。举个例子,你永远不想使用高斯消除,它对精度非常不友好。并且总是考虑另一种方法,LU分解是一种很好的方法。然而,数学家参与构建模型并考虑结果的预期精度并不常见。像Numerical Recipes这样的通用书也没有足够重视精确度,尽管它通过提出更好的模型间接地引导你远离坏模型。最后,程序员经常遇到问题。嗯,这很容易,任何人都可以做到这一点,而且我已经没有一份好工作了:)
答案 5 :(得分:5)
浮点变量的类型定义了值的范围以及可以表示多少小数位(!)。由于十进制和二进制分数之间没有整数关系,小数部分实际上是近似值。
第二:另一个问题是执行精确算术运算。想一想1.0/3.0
或PI。这些值不能用有限数量的数字表示 - 既不是十进制也不是二进制。因此,必须对值进行舍入以适应给定的空间。可用的小数位数越多,精度越高。
现在考虑应用多个这样的操作,例如PI / 3.0。这需要进行两次舍入:PI本身并不准确,结果也不是。如果表示它变得更糟,这将松散两倍。
所以,回到float
和double
:float
根据标准(C11,附件F,其余部分)可用的位数较少,因此roundig将不太精确而不是double
。想想一个带有2个小数位的十进制数(m.ff,称之为浮点数)和一个带四个的数字(m.ffff,称之为double)。如果所有计算都使用了double,那么在结果只有2个正确的小数位之前,你可以有更多的操作,而不是你已经以float开头,即使浮点结果就足够了。
请注意,在某些(嵌入式)CPU(如ARM Cortex-M4F)上,硬件FPU仅支持folat(单精度),因此双算术成本会更高。其他MCU根本没有硬件浮点计算器,因此必须模拟我的软件(非常昂贵)。在大多数GPU上,浮动的执行成本也比双倍便宜,有时甚至超过10倍。
答案 6 :(得分:5)
存储具有二进制的精确数字计数,正如其他答案所解释的那样。
有一件事要知道,CPU可以在内部以不同的精度运行操作,例如80位。这意味着像这样的代码可以触发:
void Kaboom( float a, float b, float c ) // same is true for other floating point types.
{
float sum1 = a+b+c;
float sum2 = a+b;
sum2 += c; // let's assume that the compiler did not keep sum2 in a register and the value was write to memory then load again.
if (sum1 !=sum2)
throw "kaboom"; // this can happen.
}
计算更复杂的可能性更大。
答案 7 :(得分:4)
我将在这里添加非常好的答案,并说由于您已将此问题标记为C ++,因此无法保证浮点数据的精度。绝大多数实现在实现浮点类型时使用IEEE-754,但这不是必需的。 C ++语言唯一需要的是(C ++规范§3.9.1.8):
有三种浮点类型:float,double和long double。 double类型提供至少与float一样多的精度,long double类型提供至少与double一样多的精度。 float类型的值集是double类型的值集的子集; double类型的值集是long double类型的值集的子集。 浮点类型的值表示是实现定义的。积分和浮动类型统称为算术类型。标准模板std :: numeric_limits(18.3)的特化应指定实现的每种算术类型的最大值和最小值。
答案 8 :(得分:3)
存储float
所需的空间量将保持不变,同样为double
;相对而言,有用精度的数量通常会在2 23 中的一部分与{sup> 24 中的一部分之间变为float
或一部分之间变化在{sup> 52 和2 53 中double
。非常接近于零的精度不是那么好,第二小的正值是最小值的两倍,而这又是无限大于零。然而,在整个范围内,精度将如上所述变化。
请注意,虽然在整个范围内使相对精度变化小于2的类型通常是不实际的,但精度的变化有时会导致计算产生的计算精度远低于出现的计算精度。他们应该。例如,考虑16777215.0f + 4.0f - 4.0f
。所有的值都可以使用相同的比例精确表示为float
,而与大的值最接近的值是16,777,215中的+/-一部分,但是第一次加法会产生部分{{1}的结果范围,其中值仅由8,388,610中的一个部分分隔,导致结果四舍五入为16,777,220。因此,减去4会产生16,777,216而不是16,777,215。对于float
附近float
的大多数值,添加16777216
并减去4.0f
会使原始值保持不变,但是在转折点处的更改精度会导致结果在最低处加一点额外的。
答案 9 :(得分:0)
对此的答案很简单但很复杂。这些数字以二进制形式存储。根据它是浮点数还是双精度数,计算机使用不同的二进制数来存储数字。您获得的精度取决于您的二进制。如果你不知道二进制数是如何工作的,那么查找它是个好主意。但简单地说,有些数字比其他数字需要更多的零和零。
因此精度固定(二进制数字的数量相同),但实际精度取决于您使用的数字。