2014-09-30 67 views
10

火鸟在分割时截断小数位,而不是四舍五入。此外,它将返回值中小数点的数目作为分子和分母的小数位数。为什么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的32年5月5日:

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 

回答

11

在具有小数点火鸟文字是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基础规范的事实火鸟认为60.00是一个精确的数字是正确的,因为它具有以下第5节中的文字生产规则。3 <文字>:

<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)没有<period><exact numeric literal>有一个继上<digit>暗示<period>
22)<exact numeric literal> ENL的声明类型是一个实现定义的精确数值类型,其比例尺是<period>右侧的<digit> s的数量。应该有一个确切的数字类型能够准确地表示ENL的值。

科6.27 <数值表达式>指定以下语法规则:

1)如二元算术运算器的两个操作数的声明的类型是精确数值,则声明的类型的结果的是一个实现定义的精确数字类型,其精度和标度的确定方式如下:
a)设S1和S2分别为第一个和第二个操作数的比例。
b)加减法结果的精度是实现定义的,并且该尺度是S1和S2的最大值。
c)乘法结果的精度是实现定义的,并且比例是S1 + S2。
d)分割结果的精度和范围是实现定义的。

换句话说,Firebird的行为符合SQL标准。通过外观来看,你试过的其他数据库大部分(可能是SQL Server除外),要么在执行除法时使用相对较大的值,要么使用近似数值(又称双精度)行为。

解决方法是使用近似的数字文字。使用指数零或E0将使该数字成为双精度,而不需要额外的十个幂。例如:

select 187E0/60.00 from rdb$database; -- result: 3.116666666666667 
-- or 
select 187/60.00E0 from rdb$database; -- result: 3.116666666666667