我将两个floats
除以100,然后乘以100。然后返回一个百分比。
我的问题是:当减法的右边部分返回2位数的浮点数时,为什么最终结果是没有四舍五入的浮点数?
这些是一个序列:
/* 1 */
-- Returns 0.956521739130435, which is correct.
select cast(198 as float)/(cast(198 as float) + cast(9 as float)) -- correct
/* 2 */
-- Returns 95.6521739130435, which is correct.
select 100*(cast(198 as float)/(cast(198 as float) + cast(9 as float))) --correct
/* 3 */
-- It's the same as previous one, but with a ROUND
-- Returns 95.65, which is correct.
select round(100*(cast(198 as float)/(cast(198 as float) + cast(9 as float))),2)
/* 4 */
-- Returns 4.34999999999999, should be 100-95.65, but it's not. ROUND is ignored. Why?
select 100-round(100*(cast(198 as float)/(cast(198 as float) + cast(9 as float))),2)
|-------------- This returns 95.65 --------------------------------|
另一个序列:
/* 1 */
-- Returns 0.956521739130435, which is correct.
select cast(198 as float)/(cast(198 as float) + cast(9 as float))
/* 2 */
-- Returns 0.9565, which is correct.
select round(cast(198 as float)/(cast(198 as float) + cast(9 as float)), 4)
/* 3 */
-- Returns 95.65, which is correct.
select 100*round(cast(198 as float)/(cast(198 as float) + cast(9 as float)), 4)
/* 4 */
-- Returns 4.34999999999999, should be 100-95.65, but it's not. ROUND is ignored. Why?
select 100-(100*round(cast(198 as float)/(cast(198 as float) + cast(9 as float)), 4))
|-------------------- This returns 95.65 --------------------------------|
我很好奇为什么会发生这种情况,尽管可以很容易在开始时用一个ROUND
来解决:
select round(100-(100*(cast(198 as float)/(cast(198 as float) + cast(9 as float)))), 2)
我问的原因是因为它不容易复制。我尝试重现它,在2,000次中,它只发生了12次。小于1%,但第二个小数点后的浮点数带有重复数字(即3.47999999999),这很有意义:
declare @rand int = 1
While(@rand <= 2000)
begin
select 100-round(100*(cast(abs(checksum(NewId()) % 1500) as float)/(cast(abs(checksum(NewId()) % 1500) as float) + cast(abs(checksum(NewId()) % 1500) as float))),2)
set @rand = @rand + 1
end
我猜我的另一个问题是:使用select round(100*(cast(198 as float)/(cast(198 as float) + cast(9 as float))),2)
返回 95.65 时,sql编辑器返回什么类型?
答案 0 :(得分:0)
扩展Jeroen的评论:
SQL Server的FLOAT
类型是双精度浮点值。与(大多数)浮点格式一样,该值以二进制形式存储。正如数字1/3不能用小数点后的有限位数来表示一样,数字95.65也不能用有限的位数来表示。可以存储在FLOAT
中的最接近95.65的值具有确切值:
95.650000000000005684341886080801486968994140625
如果从100中减去该数字,您将得到:
4.349999999999994315658113919198513031005859375
显示时,此数字四舍五入为15位有效数字,并且打印的值是:
4.34999999999999
如前所述,您可以使用DECIMAL
类型而不是FLOAT
来解决此问题。
如果您想了解有关浮点数学的更多信息,StackOverflow和其他地方有很多可用资源。
-编辑-
我将使用括号表示法来重复小数。当我写
0.(3)
这意味着
0.333333333333333333333333333...
等等。
让我们从头开始。 168
可以存储在float
中。 168+9
是177
。可以将其存储在float
中。如果将168
除以177
,则数学上正确的答案是:
0.95(6521739130434782608695)
但是该值不能存储在float
中。可以存储在浮点数中的最接近的值为:
0.9565217391304348115710354250040836632251739501953125
采用该数字并乘以100
,数学上正确的答案是:
95.65217391304348115710354250040836632251739501953125
由于您将float
乘以100
,所以得到了float
,并且该数字无法存储在float
中,因此最可能的值是:< / p>
95.6521739130434838216388016007840633392333984375
您要求将此float
舍入到小数点后两位。数学上正确的答案是:
95.65
但是由于您要求对float
进行四舍五入,因此答案也是float
,并且该值无法存储在float
中。可能的最接近值为:
95.650000000000005684341886080801486968994140625
您要求从100
中减去。数学上正确的值是:
4.349999999999994315658113919198513031005859375
实际上,该值 可以存储在float
中。这就是被选择的值。
将此数字转换为字符串时,SQL Server将结果四舍五入为15个有效数字。因此,该数字在打印时显示为:
4.34999999999999
当您在Java控制台上运行相同的计算时,将执行完全相同的计算,但是当打印该值时,Java将四舍五入为16个有效数字:
4.349999999999994
-另一个编辑-
为什么96.65
不能完全存储在float
中? float
类型以二进制格式存储数字。如果要以二进制形式表示96.65
,则数学上的精确值为:
1011111.1010011001100110011001100110011001100110011001(1001)
您可以看到图案。正如1/3表示为以十进制表示的无限重复值一样,此值也具有以二进制表示的无限重复值。您可以看到模式(1001)一遍又一遍地重复。
float
只能容纳53
个有效位。因此,这四舍五入为:
1011111.1010011001100110011001100110011001100110011010
如果将该数字转换回十进制,则会得到准确的值:
95.650000000000005684341886080801486968994140625
-另一个编辑-
您问当再次对结果调用Round时会发生什么情况。
我们以数字开头:
4.349999999999994315658113919198513031005859375
您要求将它四舍五入为2位。数学上正确的答案是:
4.35
由于要对浮点数进行四舍五入,因此此结果也必须是浮点数。用二进制表示该值。数学上正确的答案是:
100.0101100110011001100110011001100110011001100110011001(1001)
同样,这是一个重复的二进制值。但是float不能存储无限数量的位。该值四舍五入为53个有效位。结果是:
100.0101100110011001100110011001100110011001100110011
如果将其转换为十进制,则精确值为:
4.3499999999999996447286321199499070644378662109375
这是您选择的值。现在,SQL Server需要在屏幕上打印出来。和以前一样,四舍五入到15位有效数字。结果是:
4.35000000000000
它将删除结尾的零,并且您在屏幕上看到的结果是:
4.35
最后一轮没有做任何魔术。答案仍存储为浮点数,并且答案仍然不是精确值。发生这种情况时,SQL Server选择在打印浮点数时将值舍入到15个有效数字。在这种情况下,四舍五入的值恰好与您期望的确切值匹配。
如果在打印时将值四舍五入到14位,则原始查询似乎具有您期望的值。
如果将值四舍五入到16位,则最后一轮的结果将显示为
4.3499999999999996