为什么在访问查询中看到-0,000000000000001?

问题描述 投票:0回答:3

我有一个SQL:

SELECT Sum(Field1), Sum(Field2), Sum(Field1)+Sum(Field2)
FROM Table
GROUP BY DateField
HAVING Sum(Field1)+Sum(Field2)<>0;

问题有时是field1和field2的总和,例如:9.5-10.3,结果是-0,800000000000001。谁能解释为什么会这样以及如何解决?

sql ms-access double floating-accuracy
3个回答
1
投票

我确定是因为float数据类型(MS Access中为Double或Single)不精确。它不像十进制那样简单,它是按10的幂进行缩放的简单值。如果我没记错的话,浮点值可以有不同的分母,这意味着它们不一定总是精确地转换为基数10。

解决方法是将Field1和Field2从float / single / double / double更改为十进制或货币。如果您给出需要存储的最小和最大值的示例,包括所需的最小和最大部分,例如0.0001或0.9999,我们可能会建议您更好。

请注意,2007年之前的Access版本在十进制值上使用ORDER BY可能会出现问题。请阅读这篇文章的评论,以获得更多关于此的观点。在很多情况下,这对人们来说不是问题,但在其他情况下则可能是这样。

通常,浮点数应用于最终可能变得过小的或过大的值(小于或大于小数的值都可以容纳)。您需要了解,浮点数会以某种精度为代价保持更准确的比例。也就是说,小数点将发生溢出或下溢,而浮点数则可以继续下去。但是浮点数只有有限的有效数字,而十进制的数字都是有效的。

如果无法更改列类型,那么在此期间,您可以通过四舍五入最终计算来解决该问题。直到最后一刻才舍入。

更新

对我使用十进制的建议的批评已经得到了平整,而不是关于意外的ORDER BY结果的问题,但是在相同位数的情况下,浮点数总体上更加准确。

对此事实没有争议。但是,我认为人们使用实际上已经计算或期望以10为基数的价值观来工作更普遍。我在论坛上一遍又一遍地看到有关其浮点数据类型出了什么问题的问题,而我却没有看到同样的关于十进制的问题。对我来说,这意味着人们应该从小数开始,当他们准备好飞跃到如何和何时使用浮点数时,他们可以学习并在有能力的时候开始使用它。

同时,虽然人们总是建议使用小数点当您知道它不那么准确时]可能有点令人沮丧,但请不要让自己与现实世界离婚,因为在现实世界中,四舍五入的误差比较大。精度略有降低的代价很有价值。

让我指出批评者的例子

[Decimal(1) / 3 * 3产生1.999999999999999999999999999] >>

用熟悉的话来说,是“校正到27个有效数字”,“对于所有实际目的都是正确的。”

因此,如果我们有两种处理实际上是在说同一件事的方法,并且它们都可以非常精确地将数字表示为可笑的有效数字,并且都需要四舍五入,但是其中一种具有显然更熟悉

比其他方法舍入错误,我不能接受建议更熟悉的方法无论如何都是不好的。对于可以执行a - a而无法获得0作为答案的系统,新手该怎么做?他会感到困惑,并在试图搞怪的同时停下来工作。然后,他将在留言板上寻求帮助,并告诉拍拍答案“使用小数”。然后,他将[五年,直到他成长到足以使一天变得好奇,最后学习并真正掌握了float的功能并能够正确使用它为止。也就是说,在最后的分析中,我不得不抨击我建议使用小数,似乎在外层空间中只差一点点。

最后,我想指出的是,以下陈述并非完全正确,因为它过于笼统:

浮点型和双精度型类型将数字存储在基数2中,而不是基数10中。

准确地说,大多数现代系统都以2为底数存储浮点数据类型。但并非全部!有些人使用或已经使用了base10。据我所知,有些系统使用base 3更接近

e

,因此比base 2表示具有更好的基数经济性(好像这真的很重要,达到99.999%所有计算机用户)。另外,说“ float和double类型”可能会有点误导,因为双重IS浮动,但float并非双重。 Float是浮点数的缩写,但是Single和Double
是float(ing point)subtypes,表示可用的总精度。还有单扩展和双扩展浮点数据类型。
问题有时是field1和field2的值像:9.5-10.3和结果是-0.800000000000001。可以有人解释为什么会这样,如何解决?

为什么会这样

floatdouble类型将数字存储在基数2中,而不是在基数10中存储。有时,数字可以用有限的位数精确表示。

9.5 → 1001.1

有时不能。

10.3 → 1010.0 1001 1001 1001 1001 1001 1001 1001 1001...

在后一种情况下,数字将四舍五入为

可以

表示为double的最接近值:1010.0100110011001100110011001100110011001100110011010 base 2 = 10.300000000000000710542735760100185871124267578125 base 10
当减法以二进制形式完成时,您得到:

-0.11001100110011001100110011001100110011001100110100000 = -0.800000000000000710542735760100185871124267578125

输出例程通常会隐藏大多数“噪声”数字。

    Python 3.1将其四舍五入为-0.8000000000000007
  • SQLite 3.6将其四舍五入为-0.800000000000001
  • [printf %g将其四舍五入为-0.8
  • 请注意,即使在显示值为-0.8的系统上,它也与-0.8的最佳double近似值不同,即:
  • - 0.11001100110011001100110011001100110011001100110011010 = -0.8000000000000000444089209850062616169452667236328125

因此,在任何使用double的编程语言中,表达式9.5 - 10.3 == -0.8将为false。

decimal非解决方案>>

关于此类问题,最常见的答案是“使用十进制算术”。在这个特定示例中,这确实的确获得了更好的输出。使用Python的decimal.Decimal类:

>>> Decimal('9.5') - Decimal('10.3') Decimal('-0.8')

但是,您仍然必须处理

>>> Decimal(1) / 3 * 3 Decimal('0.9999999999999999999999999999') >>> Decimal(2).sqrt() ** 2 Decimal('1.999999999999999999999999999')

这些可能是

更熟悉的

舍入错误,而不是二进制数所具有的舍入错误,但这并不能使它们不太重要
实际上,由于以下各项的组合,二进制分数要比具有相同位数的十进制分数更准确。

hidden bit对于基数2唯一,和

    十进制的次优radix economy
  • 由于具有专用硬件,因此(在PC上也
  • 很多更快。

    基础十没有什么特别的。根据我们的手指数量,这只是一个任意选择。

    准确地说,新生婴儿的体重为0x7.5磅(更常见的说法是7磅5盎司),而说它的重量为7.3磅。(是的,两者之间相差0.2盎司,但在公差范围内。)通常,十进制在表示物理测量值方面没有优势。
  • 钱不一样

    已测量

到一定精度的物理量不同,货币

计算,因此是精确的数量。奇怪的是,它以0.01的倍数计算,而不是像大多数其他离散量一样以1的倍数计算。

如果您的“ 10.3”确实表示$ 10.30,则您应该使用十进制数字类型来精确表示该值。

((除非您使用的是历史股价从1/16美分起的那一天开始,在这种情况下,无论如何二进制都足够;-))]否则,这只是一个显示问题。

您得到的答案正确无误,为15位有效数字。对于所有实际目的来说都是正确的。如果只想隐藏“噪音”,请使用SQL ROUND函数。

这可能是浮点数实现的效果。有时不能精确表示数字,有时由于相同的原因,运算结果与我们期望的结果略有出入。
解决方法是对这些值使用舍入函数以截去无关的数字。这样(我只是将小数点后四舍五入到4位有效数字,但是当然您应该使用适合数据的精度):

SELECT Sum(Field1), Sum(Field2), Round(Sum(Field1)+Sum(Field2), 4) FROM Table GROUP BY DateField HAVING Round(Sum(Field1)+Sum(Field2), 4)<>0;


2
投票
问题有时是field1和field2的值像:9.5-10.3和结果是-0.800000000000001。可以有人解释为什么会这样,如何解决?

1
投票
解决方法是对这些值使用舍入函数以截去无关的数字。这样(我只是将小数点后四舍五入到4位有效数字,但是当然您应该使用适合数据的精度):

SELECT Sum(Field1), Sum(Field2), Round(Sum(Field1)+Sum(Field2), 4) FROM Table GROUP BY DateField HAVING Round(Sum(Field1)+Sum(Field2), 4)<>0;

© www.soinside.com 2019 - 2024. All rights reserved.