我有一个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,80000000000000001。有谁能解释为什么会发生这种情况,以及如何解决它?
发布于 2010-09-12 13:12:23
我敢肯定这是因为浮点数据类型(在MS Access中又称为双精度或单精度)是不精确的。如果我没记错的话,浮点值可以有不同的分母,这意味着它们并不总是准确地转换回基数10。
解决方法是将Field1和Field2从float/single/double改为decimal或currency。如果您给出需要存储的最小和最大值的示例,包括所需的最小和最大分数,例如0.0001或0.9999,我们可能会给您更好的建议。
请注意,2007年之前的Access版本在小数值的ORDER BY方面可能会出现问题。请阅读这篇文章的评论,了解更多关于这一点的观点。在许多情况下,这对人们来说不是问题,但在其他情况下可能是问题。
通常,对于最终可能非常小或很大(小于或大于小数所能容纳的值)的值,应该使用浮点数。您需要了解,float以牺牲一些精度为代价来保持更精确的小数位数。也就是说,小数将溢出或下溢,而浮点数只能继续。但是浮点数只有有限数量的有效数字,而小数的数字都是有意义的。
如果您不能更改列类型,那么在此期间,您可以通过舍入最终的计算来解决这个问题。不要在最后可能的时刻才转身。
更新
我对使用小数的建议提出了批评,这不是关于按结果意外排序的问题,但在相同位数的情况下,浮点数总体上更准确。
这一事实无可争辩。然而,我认为更常见的情况是,人们使用的值实际上是计数的,或者是期望以十为基数表示的。我在论坛上一次又一次地看到关于他们的浮点数据类型出了什么问题的问题,而我没有看到关于decimal的同样的问题。对我来说,这意味着人们应该从小数开始,当他们准备好如何以及何时使用float时,他们可以学习它,并在他们有能力的时候开始使用它。
同时,虽然当你知道小数不是那么准确时,人们总是推荐小数,这可能有点令人沮丧,但不要让自己脱离现实世界,因为在现实世界中,以略微降低精度为代价,拥有更熟悉的舍入误差是有价值的。
让我向我的批评者指出,示例
产生1.999999999999999999999999999
的Decimal(1) / 3 * 3
用我们熟悉的话来说,就是“正确到27位有效数字”,这是“在所有实际用途中都是正确的”。
因此,如果我们有两种方法来做实际上是说同一件事的事情,这两种方法都可以非常精确地表示出可笑的有效位数,而且都需要舍入,但其中一种方法的舍入误差明显比另一种方法更常见,我不能接受推荐更熟悉的一种方法是不好的。对于一个可以执行a - a
而不能得到0
回答的系统,初学者应该做些什么?他将会感到困惑,在他试图弄清楚的时候,他的工作就会停止。然后他会在留言板上寻求帮助,得到的答案是“使用十进制”。然后他就可以再活五年,直到有一天他变得足够好奇,最终学习并真正掌握了float在做什么,并能够正确地使用它。
也就是说,归根结底,我不得不说,抨击我推荐小数似乎在外层空间有点离谱。
最后,我想指出以下说法并不完全正确,因为它过于泛化了:
浮点型和双精度型以2为基数存储数字,而不是以10为基数。
准确地说,大多数现代系统都以2为基数存储浮点数据类型,但不是全部!有些人使用或曾经使用过基数10。据我所知,有些系统使用基数3,它更接近e,因此比基数2表示法具有更好的基数经济性(好像这对99.999%的计算机用户来说真的很重要)。另外,说"float和double类型“可能有点误导,因为double是float,但float不是double。Float是浮点的缩写,但Single和Double是浮点(Ing point)子类型,它们表示可用的总精度。还有单扩展和双扩展浮点数据类型。
发布于 2010-09-21 12:28:41
问题有时是field1和field2的总和,值类似: 9.5-10.3,结果是-0.800000000000001。有谁能解释为什么会发生这种情况,以及如何解决它?
为什么会发生这种情况
float
和double
类型以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
输出例程通常会隐藏大部分“噪声”数字。
-0.8000000000000007
-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')
这些可能是比二进制数更常见的舍入误差,但这并不会降低它们的重要性。
事实上,二进制分数比具有相同位数的十进制分数更准确,因为组合了以下各项:
它也快得多(在PC上),因为它有专用的硬件。
十进制没有什么特别之处。这只是一个基于我们拥有的手指数量的任意选择。
说一个新生儿的体重是0x7.5磅(更熟悉的术语是7磅5盎司)和7.3磅是一样准确的。(是的,两者之间存在0.2盎司的差异,但这是在可接受的范围内。)通常,小数在表示物理度量方面没有优势。
钱是不一样的
与测量到一定精度的物理量不同,货币是计数的,因此是精确的数量。奇怪的是,它是以0.01的倍数来计算的,而不是像大多数其他离散量那样是1的倍数。
如果你的"10.3“实际上意味着$10.30,那么你应该使用一个十进制数类型来准确地表示这个值。
(除非您正在处理历史股票价格,当时它们是1/16美元,在这种情况下,二进制无论如何都是足够的;-)
否则,这只是一个显示问题。
你得到了15位有效数字的正确答案。这在所有实际目的中都是正确的。如果只想隐藏“噪音”,可以使用SQL ROUND
函数。
发布于 2010-09-12 13:11:50
这可能是浮点数实现的结果。由于同样的原因,有时数字不能精确地表示,有时运算的结果与我们预期的稍有不同。
修复方法是对这些值使用四舍五入函数来截断无关的数字。就像这样(我只是在小数点后四舍五入到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;
https://stackoverflow.com/questions/3693825
复制相似问题