Firebird在分割时截断小数位,而不是舍入。此外,它将返回值中的小数点数与分子和分母中的小数位数相对应。
为什么Firebird会截断而不是舍入?为什么它将返回值基于查询中的小数位数?
Firebird 2.5:
select 187/60.00 from rdb$database; --result: 3.11
select 187.000/60 from rdb$database; --result: 3.116
select 187.000/60.00 from rdb$database --result: 3.11666
SQL Server 2012:
select 187/60.00; --result: 3.116666
Oracle 11gR2:
select 187/60.00 from dual; --result: 3.116666666667
MySQL 5.5.32:
select 187/60.00 from dual; --result: 3.1167
PostgreSQL 9.3.1:
select 187/60.00; --result: 3.116666666667
SQLite的:
select 187/60.00; --result: 3.1166666666666667
答案 0 :(得分:13)
在具有小数点的Firebird文字中,类型为NUMERIC
,而不是DOUBLE PRECISION
(或其他浮点类型)。这意味着它将应用其精确的数值计算规则。
因此,使用select 187/60.00 from rdb$database
,这意味着187是INTEGER
而60.00是NUMERIC(18,2)
。
精确数值计算的规则可以在"Exact Numerics - Functional Specification":
中找到如果两个操作数OP1和OP2分别是刻度S1和S2的精确数字,则OP1 + OP2和OP1-OP2是精确数字,精度为18,并且缩放S1和S2中的较大值,而OP1 * OP2和OP1 / OP2精确数字,精度18和刻度S1 + S2。 (除了除法之外的这些操作的比例由SQL标准指定。标准使得所有这些操作的精度和分割的比例,实现定义:我们将精度定义为18,并将分割的比例定义为S1 + S2,与乘法情况下标准所要求的相同。)
当其中一个操作数是整数类型时,它被视为一个数字0,因此在这种情况下,您有NUMERIC(18,0)/NUMERIC(18,2)
并且基于上述规则,结果为NUMERIC(18, 0+2) = NUMERIC(18,2)
。
数字似乎被截断的事实是应用精确数值计算的结果:计算完最后一位数后计算停止。剩余的事实与计算结果无关:
60.00 / 187 \ 3.11
180
---
70
60
--
100
60
-- (stop)
40
查看SQL:2011 Foundation规范,Firebird认为60.00
是精确数字的事实是正确的,因为它在5.3节< literal>中具有以下文字生成规则:
<literal> ::=
<signed numeric literal>
| <general literal>
<unsigned literal> ::=
<unsigned numeric literal>
| <general literal>
<signed numeric literal> ::=
[ <sign> ] <unsigned numeric literal>
<unsigned numeric literal> ::=
<exact numeric literal>
| <approximate numeric literal>
<exact numeric literal> ::=
<unsigned integer> [ <period> [ <unsigned integer> ] ]
| <period> <unsigned integer>
<sign> ::=
<plus sign>
| <minus sign>
<approximate numeric literal> ::=
<mantissa> E <exponent>
<mantissa> ::=
<exact numeric literal>
<exponent> ::=
<signed integer>
<signed integer> ::=
[ <sign> ] <unsigned integer>
<unsigned integer> ::=
<digit>...
语法规则:
21)没有
<exact numeric literal>
的{{1}}在最后<period>
之后有隐含的<period>
。
22)<digit>
ENL的声明类型是实现定义的精确数字类型,其标度是<exact numeric literal>
右侧的<digit>
个数。应该有一个精确的数字类型,能够准确地表示ENL的值。
第6.27节&lt;数值表达式&gt;指定以下语法规则:
1)如果二元算术运算符的两个操作数的声明类型是精确数字,则结果的声明类型是实现定义的精确数字类型,其精度和比例确定如下:
a)设S1和S2分别为第一和第二操作数的比例 b)加法和减法结果的精度是实现定义的,标度是S1和S2的最大值 c)乘法结果的精度是实现定义的,标度是S1 + S2 d)除法结果的精确度和规模是实现定义的。
换句话说,Firebird的行为符合SQL标准。通过它看起来你试过的大多数其他数据库(可能除了SQL Server),在执行除法时使用相对较大的比例值,或者似乎使用近似数值(也称为双精度)行为。 / p>
解决方法是使用近似数字文字。使用指数零或<period>
将使数字成为双精度,而无需额外的10次幂。例如:
E0