我们坚持使用一个数据库(不幸的是)使用浮点数而不是十进制值。这使得四舍五入有点困难。请考虑以下示例(SQL Server T-SQL):
SELECT ROUND(6.925e0, 2) --> returns 6.92
ROUND
执行round half up,但是从floating point numbers cannot accurately represent decimal numbers开始,显示“错误”结果(从最终用户的角度来看)。 我理解为什么会发生这种情况。
我已经想出了两个可能的解决方案(两个都返回一个浮点数,不幸的是,这也是一个要求):
SELECT CONVERT(float, ROUND(CONVERT(decimal(29,14), 6.925e0), 2))
SELECT ROUND(6.925e0 * 1000, -1) / 1000
我应该选择哪一个?有更好的解决方案吗? (遗憾的是,由于某些遗留应用程序访问同一个数据库,我们无法更改数据库中的字段类型。)
是否有针对此(常见?)问题的完善最佳实践解决方案?
(显然,常规技术“四舍五入”在这里没有用,因为6.925已经四舍五入到小数点后三位 - 只要这可以在浮点数中。)
答案 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))