浮动不一致的平均值

Avg of float inconsistency

select return 正好在 23,000 行
除了 return 60 到 200 行之间(而不是相同的行)
except 应该 return 0 因为它是 select a except select a

PK: [docSVenum1].[enumID], [docSVenum1].[valueID], [FTSindexWordOnce].[wordID]

[tf] 是一个浮点数,我得到的浮点数不准确
但我天真地认为 avg(float) 是可重复的
Avg(float) 似乎是可重复的

解决方法是什么?
TF 介于 0 和 1 之间,我只需要 5 位有效数字
我只需要 avg(TF) 是相同的数字 运行 运行
Decimal(9,8) 给了我足够的精度,如果我转换为 decimal(9,8) 除了正确 returns 0
我可以将 [TF] 更改为 decimal(9,8) 但这将是一些工作和大量回归测试,因为一些使用 [tf] 的测试需要一天的时间才能达到 运行
将 [TF] 更改为 decimal(9,8) 是最佳解决方案吗?

  SELECT [docSVenum1].[enumID], [docSVenum1].[valueID], [FTSindexWordOnce].[wordID]
       , avg([FTSindexWordOnce].[tf]) AS [avgTFraw]
    FROM [docSVenum1] 
    JOIN [docFieldLock] 
           ON [docFieldLock].[sID] = [docSVenum1].[sID] 
          AND [docFieldLock].[fieldID] = [docSVenum1].[enumID] 
          AND [docFieldLock].[lockID] IN (4, 5) /* secLvl docAdm */ 
    JOIN [FTSindexWordOnce] 
           ON [FTSindexWordOnce].[sID] = [docSVenum1].[sID]
GROUP BY [docSVenum1].[enumID], [docSVenum1].[valueID], [FTSindexWordOnce].[wordID]

except 

  SELECT [docSVenum1].[enumID], [docSVenum1].[valueID], [FTSindexWordOnce].[wordID]
       , avg([FTSindexWordOnce].[tf]) AS [avgTFraw]
    FROM [docSVenum1] 
    JOIN [docFieldLock] 
           ON [docFieldLock].[sID] = [docSVenum1].[sID] 
          AND [docFieldLock].[fieldID] = [docSVenum1].[enumID] 
          AND [docFieldLock].[lockID] IN (4, 5) /* secLvl docAdm */ 
    JOIN [FTSindexWordOnce] 
           ON [FTSindexWordOnce].[sID] = [docSVenum1].[sID]
GROUP BY [docSVenum1].[enumID], [docSVenum1].[valueID], [FTSindexWordOnce].[wordID] 

order by [docSVenum1].[enumID], [docSVenum1].[valueID], [FTSindexWordOnce].[wordID]

在这种情况下,tf 是 tf-idf
的词频 tf归一化是主观的,不需要太多精度
Avg(tf) 需要从 select 到 select 保持一致,否则结果不一致
在带有连接的单个 select 中,我需要一个一致的 avg(tf)
使用小数和 tf 的低精度得到了一致的结果

这非常类似于:SELECT SUM(...) is non-deterministic when adding the column-values of datatype float

问题在于,对于不准确的数据类型 (FLOAT/REAL),浮点数算术运算的顺序很重要。来自连接的演示:

DECLARE @fl FLOAT = 100000000000000000000
DECLARE @i SMALLINT = 0
WHILE (@i < 100)
BEGIN
    SET @fl = @fl + CONVERT(float, 5000)
    SET @i = @i + 1
END
SET @fl = @fl - 100000000000000000000
SELECT CONVERT(NVARCHAR(40), @fl, 2)
-- 0.000000000000000e+000


DECLARE @fl FLOAT = 0
DECLARE @i SMALLINT = 0
WHILE (@i < 100)
BEGIN
    SET @fl = @fl + CONVERT(float, 5000)
    SET @i = @i + 1
END
SET @fl = @fl + 100000000000000000000
SET @fl = @fl - 100000000000000000000
SELECT @fl
-- 507904

LiveDemo

可能的解决方案:

  • CAST 准确数据类型的所有参数,如 DECIMAL/NUMERIC
  • 更改table并将FLOAT更改为DECIMAL
  • 您可以尝试强制查询优化器以相同的顺序计算总和。

The good news is that when a stable query result matters to your application, you can force the order to be the same by preventing parallelism with OPTION (MAXDOP 1).


看起来 initial link 已经死了。 WebArchive