构建统计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
相关产品推荐
相关产品推荐

