代码之家  ›  专栏  ›  技术社区  ›  Heinzi

浮点值的“上舍入半”

  •  7
  • Heinzi  · 技术社区  · 15 年前

    我们陷入了一个数据库(不幸的是)使用浮点数而不是十进制值。这使得舍入有点困难。请考虑以下示例(SQL Server T-SQL):

    SELECT ROUND(6.925e0, 2)   --> returns 6.92
    

    ROUND round half up ,但从那以后 floating point numbers cannot accurately represent decimal numbers 显示“错误”结果(从最终用户的角度)。 我理解 为什么? 这就发生了。

    我已经想出了两个可能的解决方案(都返回一个浮点值,不幸的是,这也是一个要求):

    1. 舍入前转换为十进制数据类型: SELECT CONVERT(float, ROUND(CONVERT(decimal(29,14), 6.925e0), 2))
    2. 乘以直到第三个数字位于小数点的左侧(即精确表示),然后进行四舍五入: SELECT ROUND(6.925e0 * 1000, -1) / 1000

    我应该选择哪一个?有更好的解决方案吗?(不幸的是,由于某些遗留应用程序访问同一数据库,我们无法更改数据库中的字段类型。)

    有现成的吗 最佳实践 此解决方案(通用?)有问题吗?

    (显然,常用的“四舍五入两次”技术在这里没有帮助,因为6.925已经四舍五入到小数点后三位——尽可能在浮点中这样做。)

    4 回复  |  直到 10 年前
        1
  •  6
  •   Mark Dickinson Alexandru    15 年前

    您的第一个解决方案似乎更安全,而且在概念上似乎更适合这个问题:尽快从float转换为decimal,在decimal类型中执行所有相关计算,然后在写入db之前执行最后一分钟转换回float。

    编辑:在检索浮点值并转换为十进制值后,您可能仍然需要执行额外的一轮(例如到小数点后3位,或任何适合您的应用程序的操作),以确保最终得到实际需要的十进制值。 6.925e0 转换为十进制可能会再次(假设十进制格式的精度为16位)给出非常接近但不完全等于的值, 6.925 多发一轮就能解决这个问题。

    第二个解决方案在我看来不可靠:如果 6.925e0级 恰巧,由于通常的二进制浮点数问题,数量太小了?然后乘以 1000 ,结果 可以 仍然是一个触摸下面 6925 使舍入步向下舍入而不是向上舍入。如果你知道你的值总是在点后最多有3个数字,你可以通过做一个额外的循环来解决这个问题。 之后 乘以1000,类似于 ROUND(ROUND(x * 1000, 0), -1) .

    (免责声明:虽然我在处理其他上下文中的浮点和小数问题方面有丰富的经验,但我对SQL几乎一无所知。)

        2
  •  2
  •   fishinear curtisk    10 年前

    老问题,但我很惊讶这里没有提到正常的做法,所以我只是补充一下。 通常,您会添加一小部分您知道比您正在处理的数字精度小得多的数字,例如:

    SELECT ROUND(6.925e0 + 1e-7, 2)
    

    当然,添加的量必须大于所用浮点类型的精度。

        3
  •  0
  •   Ignacio Vazquez-Abrams    15 年前

    使用十进制等任意精度格式。这样你就可以把它留给语言来实现它的正确(或错误,视情况而定)。

        4
  •  0
  •   Tarek    13 年前

    我使用以下命令成功地对浮动列进行了四舍五入:

    选择“转换”(float,round(round(convert,decimal(38,14),float_column_name),3),2))