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

甲骨文 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 (或其他浮点类型)。这意味着它将应用其精确的数字计算规则。

所以对于 select 187/60.00 from rdb$database 这意味着 187 是一个 INTEGER 并且 60.00 是一个 NUMERIC(18,2)

精确数值计算的规则可以在 "Exact Numerics - Functional Specification" 中找到:



当其中一个操作数是整数类型时,它被视为标度为 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> ::=
    <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>...

和语法规则:



第 6.27 节 指定了以下语法规则:



换句话说,Firebird 的行为符合 SQL 标准。从外观上看,您尝试过的大多数其他数据库(SQL Server 可能除外),要么在执行除法时使用相对较大的比例值,要么似乎使用近似数字(又名 double )行为。

一种解决方法是使用近似数字文字。使用指数零或 E0 将使数字成为 double ,而无需额外的 10 次幂。例如:
select 187E0/60.00 from rdb$database; -- result: 3.116666666666667
-- or
select 187/60.00E0 from rdb$database; -- result: 3.116666666666667

关于sql - 为什么 Firebird 在除法时会截断小数位?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/26128353/

10-15 03:35