You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Power Query M中List.Generate条件在4-16区间失效问题排查

问题描述

现有一组数据:v值从3到24以0.5为步长(即v = List.Numbers(3, 43, 0.5)),对应p值列表为{89, 178, 328, 522, 758, 1040, 1376, 1771, 2230, 2758, 3351, 3988, 4617, 5166, 5584, 5862, 6028, 6117, 6161, 6183, 6192, 6192, 6192, 6192, 6192, 6192, 6192, 6192, 6192, 6192, 6192, 6192, 6192, 6192, 6192, 6192, 5956, 5832, 5708, 5584, 5460, 5336, 5212}。为实现保留一位小数的插值,编写了Power Query M代码,但运行后出现异常:

  • v≤4时插值正常;
  • 4≤v<16时,List.Generate函数生成6个元素(预期为5个),导致出现重复值(例如v=4.5时p值重复,v=4.6时仍为该值);
  • v≥16后插值恢复正常。

测试调整前置行或p值后问题仍存在,请问该问题的原因是什么?是否与数据类型有关?

附原代码:

let
    Source = Excel.CurrentWorkbook(){[Name="LK"]}[Content],
    //Add two columns which are the v and p columns offset by one
    //It is faster to subtract this way than by adding an Index column
    offset= 
        Table.FromColumns(
            Table.ToColumns(Source) 
                & {List.RemoveFirstN(Source[v]) & {null}}
                & {(List.RemoveFirstN(Source[p])) & {null}},
            type table[v=Decimal.Type, p=Decimal.Type, sv=Decimal.Type, sp=Decimal.Type]),
    
    // Add a column with a list of the interest rates for each data interpolated between the segments
    #"p_interpol" = Table.AddColumn(offset, "p_interpol", each let
            sv_val=[sv], 
            p_increment = ([sp]-[p])/(([sv]-[v])*10),
            Lists= List.Generate(
                ()=>[v_val=[v],p_val=[p]],
                each [v_val]< sv_val,
                each [v_val=[v_val]+0.1, p_val = [p_val]+p_increment],
                each [p_val])
        in Lists),
    
    // add another column with a list of days corresponding to the interest rates
    #"v_interpol" = Table.AddColumn(#"p_interpol", "v_interpol", each List.Numbers([v], ([sv]-[v])/0.1, 0.1)),
    
    //remove the last row as it will have an error
    remErrRow = Table.RemoveLastN(#"v_interpol",1),

    //create the new table which has the rates for every duration
    LK_interpol = Table.FromColumns(
                        {List.Combine(remErrRow[v_interpol]),List.Combine(remErrRow[p_interpol])},
                        type table[v=Decimal.Type, p=Decimal.Type])
in
    LK_interpol

异常示例:v=4.5时p值522重复出现,v=4.6时仍显示522。


问题原因与解决方案

原因分析

问题核心是浮点数精度误差,和Decimal数据类型的二进制存储特性直接相关:

  • 0.1无法用二进制浮点数精确表示,多次累加后会产生微小误差(例如4.0累加5次0.1后,实际结果可能是4.499999999999999而非精确的4.5);
  • 当v≥4且<16时,sv_val是精确的0.5步长值(比如4.5),而[v_val]因累加误差略小于sv_val,导致List.Generate的终止条件[v_val]<sv_val判定为真,多执行一次生成操作,最终得到6个元素(预期5个);
  • v≥16后,p值进入平台期(连续多个6192),此时p_increment=0,即使多生成一个元素,p值也不会变化,所以看起来插值正常,但生成逻辑的问题依然存在。

解决方案

修改List.Generate的终止条件,避免依赖浮点数的精确比较,改为基于预期生成的元素数量控制,或者对数值取整后再比较。以下是两种可行方案:

方案一:基于元素数量控制生成次数

通过计数来限制生成的元素数量,彻底规避浮点数比较误差:

Lists= List.Generate(
    ()=>[v_val=[v],p_val=[p], count=1],
    each [count] <= ([sv]-[v])/0.1,
    each [v_val=[v_val]+0.1, p_val = [p_val]+p_increment, count=[count]+1],
    each [p_val])

方案二:对数值取整后比较

将[v_val]和sv_val都保留一位小数后再比较,消除精度误差:

Lists= List.Generate(
    ()=>[v_val=[v],p_val=[p]],
    each Number.Round([v_val],1) < Number.Round(sv_val,1),
    each [v_val=[v_val]+0.1, p_val = [p_val]+p_increment],
    each [p_val])

修改后的完整代码(以方案一为例)

let
    Source = Excel.CurrentWorkbook(){[Name="LK"]}[Content],
    offset= 
        Table.FromColumns(
            Table.ToColumns(Source) 
                & {List.RemoveFirstN(Source[v]) & {null}}
                & {(List.RemoveFirstN(Source[p])) & {null}},
            type table[v=Decimal.Type, p=Decimal.Type, sv=Decimal.Type, sp=Decimal.Type]),
    
    #"p_interpol" = Table.AddColumn(offset, "p_interpol", each let
            sv_val=[sv], 
            p_increment = ([sp]-[p])/(([sv]-[v])*10),
            expectedCount = ([sv]-[v])/0.1,
            Lists= List.Generate(
                ()=>[v_val=[v],p_val=[p], count=1],
                each [count] <= expectedCount,
                each [v_val=[v_val]+0.1, p_val = [p_val]+p_increment, count=[count]+1],
                each [p_val])
        in Lists),
    
    #"v_interpol" = Table.AddColumn(#"p_interpol", "v_interpol", each List.Numbers([v], ([sv]-[v])/0.1, 0.1)),
    remErrRow = Table.RemoveLastN(#"v_interpol",1),
    LK_interpol = Table.FromColumns(
                        {List.Combine(remErrRow[v_interpol]),List.Combine(remErrRow[p_interpol])},
                        type table[v=Decimal.Type, p=Decimal.Type])
in
    LK_interpol

内容的提问来源于stack exchange,提问作者Katrin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 09:25:44