SQL SUM - 我不想在求和浮点数据时丢失精度

时间:2013-08-01 22:40:48

标签: sql ms-access floating-point sum decimal

我知道当您使用 Money 时,使用Decimal数据类型会更好(如果不是必须的话),尤其是当您使用大额资金时:)。但是我希望将我的产品的价格存储为较少的内存要求float数字,因为它们并不需要这么精确。现在,当我想计算所售产品的整体收入时,它可能会成为一个非常大的数字,它也必须具有很高的精度。我想知道如果我在SQL查询中通过SUM关键字进行求和,结果会是什么。我想它会存储在Double变量中,这肯定会失去一些精确度。如何强制它使用Decimal数字进行计算?也许知道SQL引擎的 internals 的人可以回答我的问题。很高兴提到我使用Access数据库引擎,但任何一般的答案也将受到赞赏。这可能是我将使用的查询示例:

SELECT SUM(Price * Qty) FROM Invoices

SELECT SUM(Amount) FROM Invoices

AmountPrice存储为float(Single)数据类型,Qty存储为int32

3 个答案:

答案 0 :(得分:3)

如果要将计算作为double进行计算,则将其中一个值强制转换为该类型:

SELECT SUM(cast(Price as double) * Qty)
FROM Invoices;

SELECT SUM(cast(Amount as double))
FROM Invoices;

真双精度

请注意,数据库之间的命名不一致。例如,“binary_float”是5个字节(基于IEEE 4字节浮点数),“binary_double”是9个字节(基于IEEE 8字节双倍)。但是,“float”在SQL Server中是8字节,而在MySQL中是4字节。 SQL Server和Postgres使用“real”作为4字节版本。 MySQL和Postgres对8字节版本使用“double”。

编辑:

写完之后,我在问题中看到了对Access的引用(这应该是一个标签)。在Access中,您可以使用cdbl()代替cast()

SELECT SUM(cdbl(Price) * Qty)
FROM Invoices;

SELECT SUM(cdbl(Amount))
FROM Invoices;

答案 1 :(得分:1)

如果选择在float和4字节(无符号)int之间(两者都需要在内存中存储相同的存储量),则有利有弊:

  • 假设价格有浮动,浮动无法准确处理美分 格式为$$$$$。cc(1/100并不能精确表示 浮点数 - 单一格式和双格式格式),这样就可以了 引入通常不可接受的舍入误差 与金钱有关的申请。
  • int - 假设您以美分表示价格 - 将允许 对于有符号值,精确值在-2 ^ 31到2 ^ 31-2 ^ 0(约2 * 10 ^ 9)美分的范围内 无符号的0到2 ^ 32-2 ^ 0(约4 * 10 ^ 9)。缺点是它可能会感觉到 “不自然”使用美分而不是美元和美分,但这是 主要是开发人员心中的一个问题:实际的“问题” - 如果你想打电话给他们 - 在打印价值时出现 美元和美分,需要稍微复杂的格式 但这是一个非常小的价格与其余的相关 应用程序可以简化。

稍后,在求和或执行其他计算时,整数分和数值首先转换为双精度浮点。双精度格式允许精确地表示整数值(假设整数美分) - (2 ^ 53-2 ^ 0)到2 ^ 53-2 ^ 0,这可能(您需要检查)满足您的需求。但请记住,在双倍中你仍然会有整数美分,需要转换成美元和美分。

<强> EDIT_ _ __ _ __ _ __ _ __ _ __ _ __ _ __ _

“精度的6-7个十进制数字”最容易通过单精度格式中可表示的整数范围来解释。由于SP格式有效数字是24位长(1隐式+ 23显式),因此允许2 ^ 0到2 ^ 24-2 ^ 0或1到16777215范围内的整数.16777215超过6(999999)但小于7 (9999999)十进制数字,因此“6-7十进制数字”。双精度格式具有53位有效数字(1 + 52),其整数范围为2 ^ 0到2 ^ 53-2 ^ 0。

实际SP精度是“精确的24个连续二进制数字。”

如果您可以以50个单位为增量使用美分,那么SP的范围将是2 ^ -1到2 ^ 23-2 ^ -1或0.5到8388607.5

如果您可以以25个单位为增量进行分数,那么SP的范围将是2 ^ -2到2 ^ 22-2 ^ -2或0.25到4194303.75。

答案 2 :(得分:0)

实际上,正如 @Phylogenesis 在第一条评论中所说的那样,当我想到的时候,我们没有卖出足够多的项来溢出double值的精度,就像物品一样不足以超出float值的精度。我猜测,我测试并发现如果你运行简单的SELECT SUM(Amount) FROM Invoices查询,结果将是double值。但是按照 @Gordon Linoff 的建议,对于强迫症患者最安全的方法是使用强制转换为DecimalCurrency(Access)。因此,Access语法中的查询将是:

SELECT SUM(CCur(Price) * Qty)
FROM Invoices;

SELECT SUM(CCur(Amount))
FROM Invoices;

CCur函数将Single(c# float)值转换为Currency(c# decimal)。很高兴知道转换到Double不是必需的,因为引擎本身就是这样做的。因此,更简单的方法也是安全的,只需运行简单的查询。