C# 如何计算浮点型精度,它有意义吗?

C# 如何计算浮点型精度,它有意义吗?,c#,math,floating-point,ieee-754,C#,Math,Floating Point,Ieee 754,我在理解float类型的精度时遇到问题。 msdn的精度为6到9位。但我注意到精度取决于数字的大小: 浮点数=1.0000001f; Console.WriteLinesmallNumber;//1.0000001 bigNumber=10000001F; Console.WriteLinebigNumber;//100000000 小数字比大数字更精确,我理解IEEE754,但我不理解MSDN如何计算精度,这有意义吗 此外,您还可以使用浮点格式的数字表示。请在您输入的输入中写入10000000

我在理解float类型的精度时遇到问题。 msdn的精度为6到9位。但我注意到精度取决于数字的大小:

浮点数=1.0000001f; Console.WriteLinesmallNumber;//1.0000001 bigNumber=10000001F; Console.WriteLinebigNumber;//100000000 小数字比大数字更精确,我理解IEEE754,但我不理解MSDN如何计算精度,这有意义吗


此外,您还可以使用浮点格式的数字表示。请在您输入的输入中写入100000000值,然后单击右侧的+1。然后将输入值更改为1,然后再次单击+1。您可能会看到精度上的差异。

是舍入误差前的位数是精度的度量,但您不能仅从两个数字评估精度,因为您可能离舍入阈值更近或更远

为了更好地理解这种情况,您需要了解浮动是如何表示的

这些数据存储为:

bool(1bit sign) * integer(24bit mantisa) << integer(8bit exponent)
正如你所看到的,二进制中0.3 dec的序列是无限的,就像我们不能在decadic中写1/3一样,因此,如果将其裁剪为24位,则会丢失其余部分,并且数字不再是你想要的

如果比较0.3和0.125,则0.125是精确的,0.3不是,但0.125比0.3小得多。因此,您的度量是不正确的,除非探索更接近的值,这些值将涵盖舍入步骤,并计算来自该集合的最大差异。例如,你可以比较

1.0000001f
1.0000002f
1.0000003f
1.0000004f
1.0000005f
1.0000006f
1.0000007f
1.0000008f
1.0000009f
记住fabsx roundx的最大差值,然后对

100000001
100000002
100000003
100000004
100000005
100000006
100000007
100000008
100000009
然后比较这两种差异

除此之外,你还错过了一件非常重要的事情。这就是从文本转换成二进制和二进制时的错误,通常更大。首先,试着不舍入地打印数字,例如强制在小数点后打印20位小数

此外,数字存储在二进制中,因此为了打印它们,您需要将其转换为十进制,其中包括乘法和除法10。数字中缺少零垫的位数越多,打印错误就越大。为了尽可能精确,我们使用了一个技巧,即用十六进制打印数字,无舍入错误,然后根据整数数学将十六进制字符串本身转换为十进制。这比单纯的浮点打印要精确得多。有关更多信息,请参阅相关QA:

现在回到浮点数表示的精确位数。对于数字的整数部分,这很简单:

dec_digits = floor(log10(2^24)) = floor(7.22) = 7
然而,对于小数点后的数字,这并不像前几个十进制数字那样精确,因为要进行很多舍入。有关更多信息,请参阅:


我认为他们在文档中的意思是,根据数字,精度在小数点后6到9位之间。按照你链接的页面上解释的标准,有时候微软在文档方面有点懒惰,就像我们其他人一样。 浮点数的问题是它不准确。如果将数字1.05放入链接中的站点中,您将注意到它不能准确地以浮点形式存储。它实际上存储为1.0499959523162841796875。这种存储方式可以更快地进行计算。这对钱来说并不好,例如,如果你的商品定价为1.05美元,而你卖出了10亿美元,那该怎么办

小数字比大数字更精确

不正确的比较。另一个数字具有更高的有效数字

1.0000001f正在尝试N位十进制精度。 100000001f尝试N+1

我在理解float类型的精度时遇到问题

为了更好地理解浮点精度,请考虑二进制。使用%a使用C99或更高版本的编译器进行打印

浮子存储在底座2上。有效位是一个整数/2的幂

浮点通常具有24位二进制精度。23位显式编码,1位隐含

在[1.0…2.0]之间,有223个不同的浮点值。 在[2.0…4.0]之间,有223个不同的浮点值。 在[4.0…8.0]之间,有223个不同的浮点值。

浮点数的可能值在10次幂之间分布不均匀。将浮点数分组为10次幂小数精度会导致精度的小数位数出现6到9位的抖动

如何计算浮点型精度

要查找C99之后的后续浮点值之间的差异,请使用nextafterf

说明性代码:

#include<math.h>
#include<stdio.h>

void foooo(float b) {
  float a = nextafterf(b, 0);
  float c = nextafterf(b, b * 2.0f);
  printf("%-15a %.9e\n", a, a);
  printf("%-15a %.9e\n", b, b);
  printf("%-15a %.9e\n", c, c);
  printf("Local decimal precision %.2f digits\n", 1.0 - log10((c - b) / b));
}

int main(void) {
  foooo(1.0000001f);
  foooo(100000001.0f);
  return 0;
}

MSDN文档是荒谬和错误的

糟糕的概念。二进制浮点格式在十进制数字中没有任何精度,因为它根本没有十进制数字。它用符号、固定数量的二进制数字位和二次幂指数表示数字

高端错误。浮点格式精确地表示许多数字,精度无限 N例如,“3”是精确表示的。你可以用十进制写任意远,3.0000000000…,所有的十进制数字都是正确的。另一个例子是1.40129846432481709237295832899161312802619418765157717570682838897991082685860601486638188362158203125•10−45这个数字在十进制中有105个有效数字,但浮点格式正好表示它是2−149

低端错误。当“999999.97”从十进制转换为浮点时,结果为1000000。因此,甚至没有一个十进制数字是正确的

这不是衡量准确性的标准。因为浮点有效位有24位,所以其最低位的分辨率比其最高位的分辨率高约223倍。这大约是6.9位,因为log10223大约是6.9位。但这只是告诉我们,决议的粗糙的代表性。当我们将一个数字转换为浮点格式时,我们得到的结果与该数字的差异最多为该分辨率的½,因为我们四舍五入到最接近的可表示值。因此,到浮点的转换的相对误差最多为224中的1部分,对应于上述意义上的7.2位数。如果我们用数字来测量分辨率,那么我们说分辨率大约是7.2位数,而不是6-9位数

这些数字来自哪里

因此,如果“~6-9位数”不是一个正确的概念,不是来自数字的实际界限,也不是测量精度,那么它从何而来?我们不能确定,但6和9确实出现在浮点格式的两个描述中

6是保证的最大数字x:

如果任何最多有x个有效数字的十进制数字在浮点格式的有限范围内,并转换为格式中表示的最近值,则当结果转换为最多有x个有效数字的最近十进制数字时,转换结果等于原始数字。 因此,可以合理地说,float可以保留至少六个十进制数字。然而,正如我们将看到的,没有涉及九位数的界限

9是保证这一点的最小数字x:

如果将任何有限浮点数转换为具有x位的最接近的十进制数字,则当结果转换为可在浮点数中表示的最接近值时,该转换的结果等于原始数字。 作为类比,如果float是一个容器,那么保证容纳它的最大“十进制容器”是六位数字,保证容纳它的最小“十进制容器”是九位数字。6和9类似于浮子容器的内部和外部测量

假设你有一块7.2单位长的砖块,你看它在一排砖块上的位置,每排砖块长1单位。如果将块的起点放在砖的起点,它将延伸7.2个砖。然而,其他人选择在哪里开始,他们可能会在砖头中间开始。然后它将覆盖该砖块的一部分,接下来的所有6块砖块,以及最后一块砖块的一部分,例如.5+6+.7=7.2。因此,一个7.2单元的砌块只能保证覆盖6块砖。相反,如果选择放置位置,8块砖可以覆盖7.2个单位的砌块。但是如果其他人选择了他们的出发点,第一个可能只隐藏了1个街区。然后你还需要7块砖和另一部分,所以需要9块砖

这个类比之所以成立,是因为二次幂和十次幂之间的距离不规则。2101024接近1031000。10是浮点格式中用于1024(含1024)到2048(不含2048)的数字的指数。因此,从1024到2048的这个间隔就像是一个块,被放置在100-1000块结束和1000-10000块开始之后

但请注意,这个包含9位数字的属性是外部度量,它不是float可以执行的功能,也不是它可以提供的服务。如果要以十进制格式保存,则float需要它,而不是它提供的东西。所以它不是一个浮点数可以存储多少位数的界限

进一步阅读


为了更好地理解浮点运算,考虑学习或是一本好的教科书。恐怕,我不明白你的问题是什么。也许这会有所帮助:如果未存储为1.0499959523162841796875,则第二个数字在1和1之间比第一个数字多0。该模型导致分析程序时出现逻辑错误。正确的模式是将十进制数字“1.05”转换为带有舍入误差的浮点,该操作的结果为1.049999523162841796875。根据IEEE-754标准,每个非NaN的浮点数据代表一个特定的数字。它不代表时间间隔。近似实数运算的是浮点运算,而不是近似实数的浮点运算……使用此模型支持 分析浮点运算,设计它们以达到预期目标,并编写证明它们是正确的。
#include<math.h>
#include<stdio.h>

void foooo(float b) {
  float a = nextafterf(b, 0);
  float c = nextafterf(b, b * 2.0f);
  printf("%-15a %.9e\n", a, a);
  printf("%-15a %.9e\n", b, b);
  printf("%-15a %.9e\n", c, c);
  printf("Local decimal precision %.2f digits\n", 1.0 - log10((c - b) / b));
}

int main(void) {
  foooo(1.0000001f);
  foooo(100000001.0f);
  return 0;
}
0x1p+0          1.000000000e+00
0x1.000002p+0   1.000000119e+00
0x1.000004p+0   1.000000238e+00
Local decimal precision 7.92 digits
0x1.7d783ep+26  9.999999200e+07
0x1.7d784p+26   1.000000000e+08
0x1.7d7842p+26  1.000000080e+08
Local decimal precision 8.10 digits