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

构建统计Map时如何对浮点数结果保留两位小数取整?

如何将Groovy计算的平均值保留两位小数?

我正在处理一段XMP搜索响应,需要统计每个姓名对应的Ticket与Taxes之和的平均值。先给大家看看我的XMP响应内容:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<SearchRS>
  <SearchStatus>SUCCESS</SearchStatus>
  <Itinerary>
    <Name>Joe</Name>
    <Ticket>111.11</Ticket>
    <Taxes>1.11</Taxes>
  </Itinerary>
  <Itinerary>
    <Name>Bob</Name>
    <Ticket>222.22</Ticket>
    <Taxes>2.22</Taxes>
  </Itinerary>
  <Itinerary>
    <Name>Joe</Name>
    <Ticket>333.33</Ticket>
    <Taxes>3.33</Taxes>
  </Itinerary>
  <Itinerary>
    <Name>Bob</Name>
    <Ticket>444.44</Ticket>
    <Taxes>4.44</Taxes>
  </Itinerary>
  <Itinerary>
    <Name>Joe</Name>
    <Ticket>0.0</Ticket>
    <Taxes>0.0</Taxes>
  </Itinerary>
</SearchRS>

我写了一段Groovy代码来实现统计逻辑:

def xml = new XmlSlurper().parseText(xmlString)
def map = [:]
xml.'**'.findAll {it.name() == 'Name'}.unique().each { name ->
    map[name] = xml.'**'.findAll {it.name() == 'Itinerary' && name == it.Name.text() }
        .collect { Double.parseDouble(it.Ticket.text()) + Double.parseDouble(it.Taxes.text())}
        .findAll {it}
        .with { sum() / size() }
}

运行后得到的结果是:

[Joe:10.9101234, Bob:20.319999999999997]

我希望把结果保留两位小数,变成这样的格式:

[Joe:10.91, Bob:20.31]

但我不知道该把round(2)方法放在哪里,恳请大家帮忙!


解决方案

其实核心是要处理浮点数精度问题,同时指定保留两位小数的规则。这里有两种靠谱的方式:

方式一:用BigDecimal处理数值(推荐,避免精度误差)

直接用BigDecimal来解析和计算,它能精准处理小数,然后在除法时指定保留两位小数和四舍五入规则:

def xml = new XmlSlurper().parseText(xmlString)
def map = [:]
xml.'**'.findAll {it.name() == 'Name'}.unique().each { name ->
    // 收集每个符合条件的Itinerary的Ticket+Taxes总和,用BigDecimal避免精度问题
    def totals = xml.'**'.findAll {it.name() == 'Itinerary' && name == it.Name.text() }
        .collect { 
            new BigDecimal(it.Ticket.text()) + new BigDecimal(it.Taxes.text()) 
        }
        .findAll { it > 0 } // 过滤掉0.0的记录,和你原来的findAll{it}效果一致
    
    // 计算平均值,保留两位小数,使用ROUND_HALF_UP实现常规四舍五入
    map[name] = totals ? totals.sum().divide(totals.size(), 2, BigDecimal.ROUND_HALF_UP) : 0.0
}

方式二:在原有代码基础上修改

如果你想沿用原来的Double计算方式,可以在得到平均值后转成BigDecimal并设置精度:

def xml = new XmlSlurper().parseText(xmlString)
def map = [:]
xml.'**'.findAll {it.name() == 'Name'}.unique().each { name ->
    map[name] = xml.'**'.findAll {it.name() == 'Itinerary' && name == it.Name.text() }
        .collect { Double.parseDouble(it.Ticket.text()) + Double.parseDouble(it.Taxes.text())}
        .findAll {it}
        .with { 
            // 将计算结果转成BigDecimal,设置两位小数和四舍五入规则
            new BigDecimal(sum() / size()).setScale(2, BigDecimal.ROUND_HALF_UP) 
        }
}

这两种方式都能得到你想要的[Joe:10.91, Bob:20.31]格式结果,而且第一种方式从根源上避免了浮点数精度导致的末尾多位9的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 07:31:03