【问题标题】:SQL SUM - I don't want to loose precision when summing floating point dataSQL SUM - 我不想在对浮点数据求和时降低精度
【发布时间】:2013-08-01 22:40:47
【问题描述】:

我知道当您使用 Money 时,最好(如果不是必须的话)使用 Decimal 数据类型,尤其是当您使用 Large Amount of Money 时:) .但我想将我的产品价格存储为内存要求较低的float 数字,因为它们并不真正需要这样的精度。现在,当我想计算所售产品的全部收入时,它可能会变成一个非常大的数字,而且它也必须具有很高的精度。我想知道如果我在 SQL 查询中通过 SUM 关键字进行此求和会产生什么结果。我想它将存储在Double 变量中,这肯定会失去一些精度。如何强制它使用Decimal 数字进行计算?也许了解 SQL 引擎内部的人可以回答我的问题。很高兴提到我使用 Access Database Engine,但任何一般性的答案也将不胜感激。这可能是我将使用的查询示例:

SELECT SUM(Price * Qty) FROM Invoices

SELECT SUM(Amount) FROM Invoices

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

【问题讨论】:

  • 如果您有任何具有非整数值的昂贵项目,则使用 32 位浮点数的最初决定会产生风险。美分数字不再有意义,小数点前有 6 位数字。将值存储、乘以和求和为整数美分(或适当的最小货币单位)可能会更好。
  • @PatriciaShanahan 不,我最初的决定没有风险。因为物品远没有那么贵。它们都低于 1000 美元,无需精确到美分。但是您关于存储integer 美分的观点似乎很有趣。虽然当我向用户展示它们时,我总是必须做将它们转换为 $s 的附加工作。
  • 以浮点数形式存储的数量是多少?
  • Amount=Qty*Priceqty 通常是一个小的 2 3 max 10 数字,如果它更多(例如批发 100),那么我们不需要精度(30.5 * 100 = 3050)。所以我想我可以将AmountPrice 安全地存储为float 数字。 float 允许 7 位精度。所以我可以存储 99999.99$。我们真的不需要更多。当您进行求和时会出现问题。

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


【解决方案1】:

如果您想以双精度形式进行计算,则将其中一个值转换为该类型:

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 对 4 字节版本使用“真实”。 MySQL 和 Postgres 对 8 字节版本使用“double”。

编辑:

写完之后,看到了问题中对Access的引用(这应该真的是一个标签)。在 Access 中,您将使用 cdbl() 而不是 cast()

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

SELECT SUM(cdbl(Amount))
FROM Invoices;

【讨论】:

    【解决方案2】:

    如果在浮点数和 4 字节(无符号)整数(两者都需要相同的内存存储量)之间进行选择,则有利有弊:

    • 假设价格上涨,浮动无法准确处理美分 $$$$$.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 范围内精确表示整数值(假设整数美分),这可能(您需要检查)满足您的需求。但请记住,在 double 中仍然会有整数美分,需要转换为美元和美分。

    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。

    【讨论】:

    • 您说在格式为 $$$$$.cc 1/100th 的数字中没有精确表示,但精度允许 7 位数字。也许这是因为浮点数的性质。我的意思是有些“美分”是可表示的,有些则不是,这取决于具体的数字。 _你能详细说明一下吗?_那 $$$$.cc 呢?我认为至少美分可能是准确的。谢谢。
    • 浮点格式是二进制的。分数可以通过将两个的负幂相加形成:-1、-2、-3、-4、-5 等,得到 0.5、0.25、0.125、0.0625、0.03125 等。没有分数的组合会产生精确的美分除了 0.00 二进制给出 0.00 十进制,0.01 给出 0.25,0.10 给出 0.5 和 0.11 给出 0.75。
    【解决方案3】:

    实际上,正如 @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,因为引擎会自行完成。因此,更简单且安全的方法是只运行简单查询。

    【讨论】:

      猜你喜欢
      • 2022-11-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-23
      • 2015-05-15
      • 1970-01-01
      • 2010-12-26
      • 2015-07-01
      相关资源
      最近更新 更多