当前位置: 首页 > 面试题库 >

LOG和EXP函数中的舍入问题

白通
2023-03-14
问题内容

我正在尝试执行 累积乘法 。我正在尝试两种方法来做到这一点

样本数据:

DECLARE @TEST TABLE
  (
     PAR_COLUMN INT,
     PERIOD     INT,
     VALUE      NUMERIC(22, 6)
  ) 
INSERT INTO @TEST VALUES 
(1,601,10 ),
(1,602,20 ),
(1,603,30 ),
(1,604,40 ),
(1,605,50 ),
(1,606,60 ),
(2,601,100),
(2,602,200),
(2,603,300),
(2,604,400),
(2,605,500),
(2,606,600)

注意:value列中 的数据永远不会是整数,并且值将具有小数部分。为了显示近似问题,我将示例值保留为整数。

方法1:EXP + LOG + SUM()结束(排序依据)

在这种方法中,我使用EXP + LOG + SUM() Over(Order by)技术来查找累积乘法。在这种方法中,数值不准确;结果中存在一些舍入和近似问题。

SELECT *,
       Exp(Sum(Log(Abs(NULLIF(VALUE, 0))))
             OVER(
               PARTITION BY PAR_COLUMN
               ORDER BY PERIOD)) AS CUM_MUL
FROM   @TEST;

结果:

PAR_COLUMN  PERIOD  VALUE       CUM_MUL
----------  ------  ---------   ----------------
1           601     10.000000   10
1           602     20.000000   200             -- 10 * 20 = 200(correct)
1           603     30.000000   6000.00000000001 -- 200 * 30 = 6000.000000000 (not 6000.00000000001) incorrect
1           604     40.000000   240000
1           605     50.000000   12000000
1           606     60.000000   720000000.000001  -- 12000000 * 60 = 720000000.000000 (not 720000000.000001) incorrect
2           601     100.000000  100
2           602     200.000000  20000
2           603     300.000000  5999999.99999999 -- 20000.000000 *300.000000 = 6000000.000000 (not 5999999.99999999) incorrect
2           604     400.000000  2399999999.99999  
2           605     500.000000  1199999999999.99
2           606     600.000000  719999999999998

方法2:传统乘法(递归CTE)

该方法完美地工作,没有任何舍入或近似问题。

;WITH CTE
     AS (SELECT TOP 1 WITH TIES PAR_COLUMN,
                                PERIOD,
                                VALUE,
                                CUM_MUL = VALUE
         FROM   @TEST
         ORDER  BY PERIOD
         UNION ALL
         SELECT T.PAR_COLUMN,
                T.PERIOD,
                T.VALUE,
                Cast(T.VALUE * C.CUM_MUL AS NUMERIC(22, 6))
         FROM   CTE C
                INNER JOIN @TEST T
                        ON C.PAR_COLUMN = T.PAR_COLUMN
                           AND T.PERIOD = C.PERIOD + 1)
SELECT *
FROM   CTE 
ORDER BY PAR_COLUMN,PERIOD

结果

PAR_COLUMN  PERIOD  VALUE       CUM_MUL
----------  ------  ---------   ----------------
1           601     10.000000   10.000000
1           602     20.000000   200.000000
1           603     30.000000   6000.000000
1           604     40.000000   240000.000000
1           605     50.000000   12000000.000000
1           606     60.000000   720000000.000000
2           601     100.000000  100.000000
2           602     200.000000  20000.000000
2           603     300.000000  6000000.000000
2           604     400.000000  2400000000.000000
2           605     500.000000  1200000000000.000000
2           606     600.000000  720000000000000.000000

谁能告诉我 为什么方法1中的值不准确,以及 如何解决?我尝试通过将数据类型更改为Float并增加scalein,numeric但没有用。

我真的想使用比方法2快得多的方法1。

编辑: 现在我知道近似的原因。谁能找到解决此问题的解决方案?


问题答案:

您可以四舍五入为您的数据:

--720000000000000 must be multiple of 600

select
   round( 719999999999998/600,  0 ) * 600

--result: 720000000000000

在SQLFiddle上进行测试

create TABLE T 
  (
     PAR_COLUMN INT,
     PERIOD     INT,
     VALUE      NUMERIC(22, 6)
  ) 
INSERT INTO T VALUES 
(1,601,10.1 ),    --<--- I put decimals just to test!
(1,602,20 ),
(1,603,30 ),
(1,604,40 ),
(1,605,50 ),
(1,606,60 ),
(2,601,100),
(2,602,200),
(2,603,300),
(2,604,400),
(2,605,500),
(2,606,600)

查询1

with T1 as (
SELECT *,
       Exp(Sum(Log(Abs(NULLIF(VALUE, 0))))
             OVER(
               PARTITION BY PAR_COLUMN
               ORDER BY PERIOD)) AS CUM_MUL,
       VALUE AS CUM_MAX1,
       LAG( VALUE , 1, 1.) 
             OVER(
               PARTITION BY PAR_COLUMN
               ORDER BY PERIOD ) AS CUM_MAX2,
       LAG( VALUE , 2, 1.) 
             OVER(
               PARTITION BY PAR_COLUMN
               ORDER BY PERIOD ) AS CUM_MAX3
FROM   T )
select PAR_COLUMN,  PERIOD,  VALUE, 
       ( round( ( CUM_MUL  / ( CUM_MAX1 * CUM_MAX2 * CUM_MAX3) ) ,6) 
         * 
         cast( ( 1000000 * CUM_MAX1 * CUM_MAX2 * CUM_MAX3) as bigint )
       ) / 1000000.
       as CUM_MUL
FROM T1

结果

| PAR_COLUMN | PERIOD | VALUE |         CUM_MUL |
|------------|--------|-------|-----------------|
|          1 |    601 |  10.1 |            10.1 | --ok! because my data
|          1 |    602 |    20 |             202 |
|          1 |    603 |    30 |            6060 |
|          1 |    604 |    40 |          242400 |
|          1 |    605 |    50 |        12120000 |
|          1 |    606 |    60 |       727200000 |
|          2 |    601 |   100 |             100 |
|          2 |    602 |   200 |           20000 |
|          2 |    603 |   300 |         6000000 |
|          2 |    604 |   400 |      2400000000 |
|          2 |    605 |   500 |   1200000000000 |
|          2 |    606 |   600 | 720000000000000 |

注意我x1000000可以不使用小数



 类似资料:
  • Python3 数字 描述 exp() 方法返回x的指数,ex。 语法 以下是 exp() 方法的语法: import math math.exp( x ) 注意:exp()是不能直接访问的,需要导入 math 模块,通过静态对象调用该方法。 参数 x -- 数值表达式。 返回值返回x的指数,ex。 实例 以下展示了使用 exp() 方法的实例: #!/usr/bin/python3 im

  • 本文向大家介绍C语言中的abs()函数和exp()函数的用法,包括了C语言中的abs()函数和exp()函数的用法的使用技巧和注意事项,需要的朋友参考一下 C语言abs()函数:求绝对值(整数) 头文件: 定义函数: 函数说明:abs()用来计算参数j 的绝对值,然后将结果返回。 返回值:返回参数j 的绝对值结果。 范例 执行 C语言exp()函数:e的次幂函数(以e为底的x次方值) 头文件: e

  • 我有一个由数字组成的数据集。我想在一份报告中陈述这一点。我已经设法生成了一个具有以下格式的报告。这有点类似于会计中的试算表。但事实并非如此!表中的数据在其值中有4个小数点。但在报告中,它应该四舍五入到小数点后两位。报告还必须准确,因此需要使用。 基于@marciob、@user3679868和@joopeggen的编辑3决定根据舍入值进行计算。差额。047(.56-.553)显然被冲销了(审计标准

  • 我尝试使用scale=3和HALF\u向上舍入模式舍入0.14049,希望看到舍入值=0.141,但得到的是0.140。 根据我的理解,最后一个十进制数字9应该将4到5四舍五入,当使用小数点3时,应该将0到1四舍五入,我应该看到0.141。 这是BigDecimal setScale方法中的错误还是我对舍入的期望是错误的。如何获得类似于0.141而不是0.140的舍入值?

  • 本文向大家介绍详解C语言的exp()函数和ldexp()函数以及frexp()函数,包括了详解C语言的exp()函数和ldexp()函数以及frexp()函数的使用技巧和注意事项,需要的朋友参考一下 C语言exp()函数:e的次幂函数(以e为底的x次方值) 头文件: exp()用来计算以e 为底的x 次方值,即ex 值,然后将结果返回。其原型为: 【返回值】返回 e 的x 次方计算结果。 注意,使

  • Python3 数字 描述 log() 方法返回x的自然对数,x > 0。 语法 以下是 log() 方法的语法: import math math.log( x ) 注意:log()是不能直接访问的,需要导入 math 模块,通过静态对象调用该方法。 参数 x -- 数值表达式。 返回值返回x的自然对数,x>0。 实例 以下展示了使用 log() 方法的实例: #!/usr/bin/py