Firebird在除法而不是舍入时会截断小数位。此外,它以分子和分母的小数位数为基础,将返回值的小数点位数作为基础。
为什么Firebird会被截断而不是四舍五入?为何将返回值基于查询中的小数位数?
Firebird2.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
在Firebird中,带小数点的文字类型NUMERIC不是DOUBLE PRECISION(或其他浮点类型)。这意味着它将应用其确切的数值计算规则。
NUMERIC
DOUBLE PRECISION
因此,select 187/60.00 from rdb$database这意味着187是an,INTEGER而60.00是a NUMERIC(18,2)。
select 187/60.00 from rdb$database
INTEGER
NUMERIC(18,2)
精确数字计算的规则可以在“精确数字-功能说明”中找到:
如果两个操作数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)。
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 节中具有以下针对字面量的生产规则:
60.00
<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>没有a的<period>an表示<period>在last之后<digit>。 22)<exact numeric literal>ENL的声明类型是实现定义的精确数字类型,其小数位数是<digit>右边的s数<period>。应该有一个能够精确表示ENL值的精确数字类型。
<exact numeric literal>
<period>
<digit>
第6.27节<数值表达式>指定以下语法规则:
1)如果二进位算术运算符的两个操作数的声明类型均为精确数值,则结果的声明类型为实现定义的精确数值类型,其精度和小数位数确定如下: a)令S1和S2为第一和第二操作数的小数位数。 b)加法和减法结果的精度是实现定义的,并且小数位数是S1和S2的最大值。 c)乘法结果的精度是实现定义的,小数位数是S1 + S2。 d) 除法结果的精度和小数位数由实现定义。
换句话说,Firebird的行为符合SQL标准。从外观上看,您尝试过的大多数其他数据库(SQL Server可能例外)要么在执行除法时使用相对较大的比例值,要么似乎使用近似数值(也称为双精度)行为。
一种解决方法是使用近似数字文字。使用零或指数E0将使数字成为双精度,而没有额外的10的幂。例如:
E0
select 187E0/60.00 from rdb$database; -- result: 3.116666666666667 -- or select 187/60.00E0 from rdb$database; -- result: 3.116666666666667