转换XML时的FP精度错误 - >表&处理它

时间:2010-12-24 12:20:40

标签: sql-server xml openxml rounding-error precision

在SQL Server 2008中,我在XML属性中具有值“0.01”。使用OPENXML,我将XML分解为临时表。如果适用的列是real(单精度)类型,则表中的值为0.01。好。但是,如果精度为float(双精度),则为0.00999999977648258。这毫无意义。为什么要这样做?

我的下一个问题是,无论值如何在临时表中表示,当我对其运行聚合函数时,它总是返回为0.00999999977648258。这导致验证错误:该过程报告输入太小(<0.01),这是不正确的。

为什么会出现这些舍入错误以及如何克服它们的任何想法?

已经尝试过:将列设为varchar。

EDIT2:

基于答案,我理解问题是由于IEEE数字不能精确地表示0.01。为此,我的下一个问题是:

  

“WHERE {computed}&lt; 0.01”,为什么会这样   0.01这里也不圆?如果是,那么等式将评估为   预期的(即0.00999999977648258是   不是&lt; 0.00999999977648258)

编辑:显示示例代码

此代码将产生错误。将指示的浮点数更改为实数&amp;错误“消失”。至少就临时表而言。

DECLARE @XMLText varchar(max)

SET @XMLText = 
'<query prodType="1">
  <param type="1" lowMin="10" hiMax="300">
    <item low="18" hi="20" mode="1" weight="1" />
    <item low="220" hi="220" mode="0" weight="1" />
  </param>
  <param type="2" lowMin="4" hiMax="6">
    <item low="5" hi="5" mode="1" weight="1" />
    <item low="6" hi="6" mode="0" weight="0.01" />
  </param>
  <param type="3" lowMin="0" hiMax="300">
    <item low="34" hi="34" mode="1" weight="0.75" />
    <item low="40" hi="60" mode="1" weight="0.25" />
  </param>
</query>'

DECLARE @hxml int, @sp INT, @StartXCount int

EXEC sp_xml_preparedocument @hxml OUTPUT, @XMLText

IF @sp != 0 BEGIN
    SET @Result = '0'
    RETURN
END

DECLARE @t table (
    LowMin         real,
    HiMax          real,
    ParamTypeID    int,
    ParamWeight    float, -- real <<<
    Low            real,
    Hi             real,
    Mode           tinyint          
)

INSERT INTO @t
SELECT      *
FROM        OPENXML (@hxml, '/query/param/item', 2)
WITH        (
                LowMin       real     '../@lowMin',
                HiMax        real     '../@hiMax',
                ParamTypeID  int      '../@type',
                ParamWeight  real     '@weight',
                Low          real     '@low',
                Hi           real     '@hi',
                Mode         tinyint  '@mode'
            )

SELECT * FROM @t

2 个答案:

答案 0 :(得分:1)

0.01无法准确存储在IEEE类型中,因为它不能用分母中幂2的分数表示。

但是,我可以复制的内容与你所说的相反:

SELECT  CAST(0.01 AS FLOAT) AS value
FOR XML PATH(''), TYPE

<value>1.000000000000000e-002</value>

SELECT  CAST(0.01 AS REAL) AS value
FOR XML PATH(''), TYPE

<value>9.9999998e-003</value>

您能否发布您的确切查询?

<强>更新

我的代码会得到相同的结果:0,01 ParamWeightFLOAT0,00999999977648258REAL

更新2:

IEEE种类型存储为符号,尾数和有效数字。对于32 - 位值,尾数是最大幂2的二进制对数(最小值),而有效数是23 - 位二进制分数(来自{{1的数字) }} 1,未存储前导2。)。

在您的情况下,尾数1-7,有效数(2^-7 = 1/128 = 0,0078125)1.01000111101011100001010

结果数字是这些数字的乘积,接近(= 1 + 2348810 / 8388608 = 1,2799999713897705078125)但仍然不够接近以避免0.01'数字中的错误(精度15认为重要)< / p>

答案 1 :(得分:1)

错误是由于计算机 CAN NOT 以单精度和双精度表示浮点值0.01。此值在float和double中四舍五入到最接近的可表示值。所以在这两种情况下它都不是0.01,但在实际情况下只显示为0.01(我不知道浮点运算的ToString算法是如何工作的,所以无法告诉你为什么在一种情况下它被转换为0.01和另一个中的0.00999999977648258。

我唯一可以告诉你的是 - 在实际情况下,它被舍入到可表示的值大于0.01,在双重情况下,它被舍入到可表示的值低于0.01。因此,在双精度情况下验证失败。

要解决此问题,您可以将某个非常小的epsilon的验证测试更改为“小于0.01 - epsilon”。