浮点值的“上半部分”

时间:2010-03-03 10:24:46

标签: sql-server language-agnostic floating-point rounding

我们坚持使用一个数据库(不幸的是)使用浮点数而不是十进制值。这使得四舍五入有点困难。请考虑以下示例(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
  3. 我应该选择哪一个?有更好的解决方案吗? (遗憾的是,由于某些遗留应用程序访问同一个数据库,我们无法更改数据库中的字段类型。)

    是否有针对此(常见?)问题的完善最佳实践解决方案?

    (显然,常规技术“四舍五入”在这里没有用,因为6.925已经四舍五入到小数点后三位 - 只要这可以在浮点数中。)

4 个答案:

答案 0 :(得分:6)

你的第一个解决方案似乎更安全,而且似乎在概念上更接近问题:尽快从float转换为十进制,在十进制类型中进行所有相关计算,然后在最后一分钟转换回float在写入数据库之前。

编辑:在检索浮点值并转换为十进制后,您可能仍需要立即执行额外的回合(例如,到3个小数位,或适用于您的应用程序的任何内容),以确保最终得到实际意图的十进制值。转换为十进制的6.925e0将再次成为可能(假设十进制格式具有> 16位精度),以提供与6.925非常接近但不完全相等的内容;额外的一轮将会解决这个问题。

第二个解决方案对我来说看起来并不可靠:如果6.925e0的存储值恰好是由于通常的二进制浮点问题而导致的,那么这个问题太少了?然后在乘以1000后,结果可能仍然是6925下的触摸,因此舍入步骤向下舍入而不是向上舍入。如果您知道您的值在该点之后总是最多有3位数,那么您可以通过在乘以1000之后执行额外的一轮来解决此问题,例如ROUND(ROUND(x * 1000, 0), -1)

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

答案 1 :(得分:2)

老问题,但我很惊讶这里没有提到正常做法,所以我只是添加它。 通常情况下,您会添加少量您知道的数量远小于您正在使用的数字的准确度,例如:像这样:

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

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

答案 2 :(得分:0)

使用任意精度格式,例如DECIMAL。这样你就可以把它留给语言来使它正确(或者视情况而定)。

答案 3 :(得分:0)

我设法使用以下命令正确地舍入浮点列:

SELECT CONVERT(float,ROUND(ROUND(CONVERT(decimal(38,14),float_column_name),3),2))