Sybase 15.7浮点数转数字时存在舍入误差。

4
有人能解释一下以下内容吗?对我来说,它看起来只是一个bug:
select convert(numeric(8,4), convert(float, '12.4155499999999996418864611769'))

12.4156

当然,答案应该是12.4155。为了进行双重检查,我创建了一个名为MyTab的表格,其中包含单个sybase "float"列,并插入"12.4155499999999996418864611769"。然后使用convert转储hex并得到0x4028D4C2F837B4A2。这确实是我所插入的字符串的IEEE 754双精度表示。然后,如果我(如上所述)将其转换为numeric(8,4),它会给出12.4156。我认为这样做是错误的,还是我疯了?
1个回答

6

您可能面临 Sybase 在将双精度浮点数转换为十进制时出现的 double-rounding 错误。

该算法可能是首先将 IEEE 754 数字(在您的情况下由 0x4028D4C2F837B4A2 表示)打印为具有 17 位小数精度的数字,因为这个数字在某种意义上是 IEEE 754 双精度格式的“十进制精度”。对于您的示例,将其转换为十进制的结果为 12.415550000000000。

然后,由于您要求小数点后保留 4 位小数,中间的十进制表示会按照舍入至最接近值或舍入至奇偶相同的原则进行四舍五入。两种方法都是错误的,因为第一次舍入已经丢失了一些关键信息(即原始数字略低于 12.41555,而不是恰好等于它)。

我不熟悉Sybase,但很可能没有简单的解决方法。您可以要求进行十进制转换并增加足够多的额外数字以避免问题(也就是说,增加21位数字,如下所示),但您可以预期由于与原始问题相同的原因,第17位之后的数字将打印为“0”。您可以从您拥有的双精度数中减去12(该操作恰好是精确的,即不会引入任何近似值),以便在转换为十进制时获得一些数字,但这还不足以确保所有双精度值的正确舍入。

注:

  1. 作为参考,我们所讨论的双精度数的确切值为12.415549999999999641886461176909506320953369140625。

  2. 根据Mark Dickinson在this question中的回答,进行第一次转换时,比第二次四舍五入所需的20个小数位多的数字将使结果免受“双重舍入”的任何明显影响。你问题中的数字是双倍精度浮点数中最大连续九个数字的一个很好的候选者,但在Mark的2.12818792307269553358078502102171540639252016258831784842556110831434197718043638405555406495645619729155240037555858106390933161420388023706431461384056688295540725831155392678607931808851292893574214797681879999999999999999999941026584542575391157788777223962620780080784703190447744595561259568772261019375946489162743091583251953125E-122面前相形见绌。


2
@aka.nice:这并不是由于双重舍入,而是因为12.4155499999999996418864611769在转换为单精度时变成了12.41555023193359375。保留4位小数,正确的结果应该是12.4156。(无论使用'f'与否都一样。) - Rick Regan
@aka.nice:我同意,如果你想在C中使用浮点数,那么你应该使用“f”来确保正确的(单精度)二进制舍入。但是我不确定你为什么提到了浮点数(这个问题是关于双精度的)。 - Rick Regan
@RickRegan 噢,我误解了“float”列是Sybase FLOAT类型(单精度)。这可以解释四舍五入的问题...我现在要删除这些错误的注释。 - aka.nice

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接