AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 168815
Accepted
Triynko
Triynko
Asked: 2017-04-01 12:29:59 +0800 CST2017-04-01 12:29:59 +0800 CST 2017-04-01 12:29:59 +0800 CST

为什么 TSQL 会为 POWER(2.,64.) 返回错误的值?

  • 772

select POWER(2.,64.)返回18446744073709552000而不是18446744073709551616. 它似乎只有 16 位精度(四舍五入)。

即使使精度明确select power(cast(2 as numeric(38,0)),cast(64 as numeric(38,0))),它仍然返回舍入结果。

这似乎是一个非常基本的操作,因为它可以像这样以 16 位精度任意剥落。它可以正确计算的最高值只是POWER(2.,56.),失败了POWER(2.,57.)。这里发生了什么?

真正可怕的是它select 2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.;实际上返回了正确的值。简洁明了。

sql-server azure-sql-database
  • 4 4 个回答
  • 1878 Views

4 个回答

  • Voted
  1. Best Answer
    Jack Douglas
    2017-04-01T12:40:08+08:002017-04-01T12:40:08+08:00

    从在线文档:

    POWER ( float_expression , y )  
    

    论据

    float_expression 是浮点类型或可以隐式转换为浮点类型的表达式

    这意味着无论您作为第一个参数传递的任何内容都将在函数执行float(53) 之前被隐式转换为 a 。但是,情况并非(总是?)。

    如果是这种情况,它将解释精度的损失:

    将使用科学记数法的浮点值转换为十进制或数字仅限于精度为 17 位的值。任何精度高于 17 的值都舍入为零。

    另一方面,字面2.量是类型numeric……:

    DECLARE @foo sql_variant;
    SELECT @foo = 2.;
    SELECT SQL_VARIANT_PROPERTY(@foo, 'BaseType');
    GO
    
    | (无列名) |
    | :--------------- |
    | 数字 |
    

    dbfiddle在这里

    …并且乘法运算符返回具有更高优先级的参数的数据类型。

    似乎在 2016 (SP1) 上保留了所有精度:

    SELECT @@version;
    GO
    
    | (无列名) |
    | :------------------------------------------------ -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------- |
    | Microsoft SQL Server 2016 (SP1) (KB3182545) - 13.0.4001.0 (X64) <br> 2016 年 10 月 28 日 18:17:30 <br> 版权所有 (c) Microsoft Corporation<br> Windows Server 上的速成版(64 位) 2012 R2 标准 6.3 <X64>(内部版本 9600:)(管理程序)<br> |
    
    SELECT POWER(2.,64.);
    GO
    
    | (无列名) |
    | :-------------------- |
    | 18446744073709551616 |
    

    dbfiddle在这里

    …但在 2014 (SP2) 上,它们不是:

    SELECT @@version;
    GO
    
    | (无列名) |
    | :------------------------------------------------ -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ------------------------------------ |
    | Microsoft SQL Server 2014 (SP2) (KB3171021) - 12.0.5000.0 (X64) <br> 2016 年 6 月 17 日 19:14:09 <br> 版权所有 (c) Microsoft Corporation<br> Windows NT 上的速成版(64 位) 6.3 <X64>(内部版本 9600:)(管理程序)<br> |
    
    SELECT POWER(2.,64.);
    GO
    
    | (无列名) |
    | :-------------------- |
    | 18446744073709552000 |
    

    dbfiddle在这里

    • 17
  2. Paul White
    2017-04-02T06:48:27+08:002017-04-02T06:48:27+08:00

    2 64float的结果在(和real就此而言)完全可以表示。

    当这个精确的结果被转换回numeric(第一个POWER操作数的类型)时,问题就出现了。

    在引入数据库兼容级别 130 之前,SQL Server将隐式转换舍入float到最多 17 位。numeric

    在兼容级别 130 下,在转换过程中会保留尽可能多的精度。这记录在知识库文章中:

    SQL Server 2016 在处理某些数据类型和不常见操作方面的改进

    要在 Azure SQL 数据库中利用这一点,您需要将其设置COMPATIBILITY_LEVEL为 130:

    ALTER DATABASE CURRENT SET COMPATIBILITY_LEVEL = 130;
    

    需要进行工作负载测试,因为新安排不是万能的。例如:

    SELECT POWER(10., 38);
    

    ...应该抛出一个错误,因为 10 38无法存储numeric(最大精度为 38)。120兼容性下出现溢出错误,但130下的结果是:

    99999999999999997748809823456034029568 -- (38 digits)
    
    • 14
  3. Joe Obbish
    2017-05-04T12:39:45+08:002017-05-04T12:39:45+08:00

    通过一点数学,我们可以找到一种解决方法。对于奇数n:

    2 ^ n 
    = 2 ^ (2k + 1)
    = 2 * (2 ^ 2k)
    = 2 * (2 ^ k) * (2 ^ k)
    

    对于偶数n:

    2 ^ n 
    = 2 ^ (2k)
    = 1 * (2 ^ 2k)
    = 1 * (2 ^ k) * (2 ^ k)
    

    在 T-SQL 中编写它的一种方法:

    DECLARE @exponent INTEGER = 57;
    
    SELECT (1 + @exponent % 2) * POWER(2., FLOOR(0.5 * @exponent)) * POWER(2., FLOOR(0.5 * @exponent));
    

    在 SQL Server 2008 上测试,结果是 144115188075855872 而不是 144115188075855870。

    这一直有效到 113 的指数。看起来 NUMERIC(38,0) 最多可以存储 2 ^ 126,所以没有完全覆盖,但如果需要,公式可以分成更多部分.

    • 3
  4. ypercubeᵀᴹ
    2017-05-04T06:48:10+08:002017-05-04T06:48:10+08:00

    只是为了好玩,递归 CTE 解决方案:

    with 
      prm (p, e) as           -- parameters, to evaluate: p**e
        (select 2, 64),       -- (2 ** 64)  
      pow (power, exp) as 
        (select cast(p as numeric(30,0)), 
                e
         from prm 
         union all 
         select cast(power * power * (case when exp % 2 = 0 then 1 else p end) 
                     as numeric(30,0)), 
                exp / 2 
         from prm, pow 
         where exp > 1 
        ) 
    select power 
    from pow 
    where exp = 1 ;
    
    • 0

相关问题

  • SQL Server - 使用聚集索引时如何存储数据页

  • 我需要为每种类型的查询使用单独的索引,还是一个多列索引可以工作?

  • 什么时候应该使用唯一约束而不是唯一索引?

  • 死锁的主要原因是什么,可以预防吗?

  • 如何确定是否需要或需要索引

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目

    • 12 个回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Jin 连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve