代码之家  ›  专栏  ›  技术社区  ›  MadMurf

将小数格式化为字符串以提高性能

  •  5
  • MadMurf  · 技术社区  · 17 年前

    我正在编写一个应用程序,它需要输出不同长度的小数,并根据字符串的大小进行调整。 不带小数点 作为另一个系统的输入写入平面文件。例如

     12345  -> Length:10, Scale:2              -> 0001234500
     123.45 -> Length:10, Scale:2              -> 0000012345
     123.45 -> Length:10, Scale:3              -> 0000123450
    -123.45 -> Length:10, Scale:3, signed:true -> -000123450
     123.45 -> Length:10, Scale:3, signed:true -> +000123450
    

    我为处理这个问题编写的函数在下面,将被调用成百上千次,所以我想确保没有更好、更有效的方法来完成这个任务。我已经研究过让decimalFormat为我做更多工作的方法,但是我看不到它处理我需要用小数点但没有小数点的格式。

    protected String getFormattedDecimal( String value, int len, int scale, Boolean signed ) throws Exception{
        StringBuffer retVal = new StringBuffer();
    
        //Need a BigDecimal to facilitiate correct formatting
        BigDecimal bd = new BigDecimal( value );
    
        //set the scale to ensure that the correct number of zeroes 
        //at the end in case of rounding
        bd = bd.setScale( scale );
    
        //taking it that if its supposed to have negative it'll be part of the num
        if ( ( bd.compareTo( BigDecimal.ZERO ) >= 0 ) && signed ){
            retVal.append( "+" );
        }           
    
        StringBuffer sbFormat = new StringBuffer();
        for (int i = 0; i < len; i++)
        {
            sbFormat.append('0');
        }
    
        DecimalFormat df = new DecimalFormat( sbFormat.toString() );
    
        retVal.append( df.format( bd.unscaledValue() ) );
    
        return retVal.toString();
    }
    
    3 回复  |  直到 17 年前
        1
  •  8
  •   CPerkins    17 年前

    我的性能增强实现如下。它的速度大约是基于decimalMatter的解决方案的4.5倍:在我的机器上运行,使用Eclipse和一个像样的自制测试工具,结果是:

    • 老路用了5421毫秒打了60万个电话(每个电话平均0.009035毫秒)
    • 新的通话方式需要1219毫秒才能拨打60万次电话(每次通话平均0.002032毫秒)

    一些注释:

    • 我的解决方案使用固定大小的零块作为填充。如果你预计每边需要比我用的30号左右更多的填充物,你就必须增加尺寸…很明显,如果需要,您可以动态地增加它。
    • 你上面的评论与代码不太匹配。具体来说,如果返回了符号字符,则返回的长度大于请求的长度(您的注释中另有说明)。我选择相信代码而不是注释。
    • 我将方法设置为静态的,因为它不需要实例状态。这是个人品味的东西-YMMV。

    另外:为了模仿原文的行为(但注释中没有给出),这:

  • 如果传入值中的小数位数多于小数位数,则抛出算术异常 如果传入值中的整数位数多于适合的整数位数(len scale),则返回的字符串比len长。 如果signed为true,则返回的字符串将比len长一个
    • 然而 :如果len为负,则原始值返回逗号分隔的字符串。这将引发IllegalArgumentException
    package com.pragmaticsoftwaredevelopment.stackoverflow;
    ...
       final static String formatterZeroes="00000000000000000000000000000000000000000000000000000000000";
       protected static String getFormattedDecimal ( String value, int len, int scale, Boolean signed ) throws IllegalArgumentException {
           if (value.length() == 0) throw new IllegalArgumentException ("Cannot format a zero-length value");
           if (len <= 0) throw new IllegalArgumentException ("Illegal length (" + len + ")");
           StringBuffer retVal = new StringBuffer();
           String sign=null;
           int numStartIdx; 
           if ("+-".indexOf(value.charAt(0)) < 0) {
              numStartIdx=0;
           } else {
              numStartIdx=1;
              if (value.charAt(0) == '-')
                 sign = "-";
           }
           if (signed && (value.charAt(0) != '-'))
              sign = "+";
           if (sign==null)
              sign="";
           retVal.append(sign);
    
    
           int dotIdx = value.indexOf('.');
           int requestedWholePartLength = (len-scale);
    
           if (dotIdx < 0) { 
              int wholePartPadLength = (requestedWholePartLength - ((value.length()-numStartIdx)));
              if (wholePartPadLength > 0)
                 retVal.append(formatterZeroes.substring(0, wholePartPadLength));
              retVal.append (value.substring(numStartIdx));
              if (scale > 0)
                 retVal.append(formatterZeroes.substring(0, scale));
           }
           else {
              int wholePartPadLength = (requestedWholePartLength - (dotIdx - numStartIdx));
              if (wholePartPadLength > 0)
                 retVal.append(formatterZeroes.substring(0, wholePartPadLength));
              retVal.append (value.substring(numStartIdx, dotIdx));
              retVal.append (value.substring (dotIdx+1));
              int fractionalPartPadLength = (scale - (value.length() - 1 - dotIdx));
              if (fractionalPartPadLength > 0)
                 retVal.append(formatterZeroes.substring(0, fractionalPartPadLength));
    
    
           }
    
           return retVal.toString();
       }
    
  •     2
  •  4
  •   ChssPly76    17 年前

    如果要以字符串形式开始输入,为什么需要将其转换为bigdecimal并返回?

    似乎找到小数点的位置要快得多,将其与长度/比例进行比较,并相应地填充字符串。

        3
  •  2
  •   akf    17 年前

    我同意CHSSPLY76 WRT手动字符串操作。

    但是,如果你要去 BigDecimal / DecimalFormat 路由,您可能需要考虑共享 十进制格式 而不是每次迭代都创建一个新的。注意,这些类不是线程安全的,因此如果使用多个线程进行处理,则需要使用以下内容 ThreadLocal 保存格式化程序的存储器 Thread .

    顺便问一句,您是否对此进行了测试,发现性能不可接受,或者您只是在寻找最有效的解决方案? Note what Donald Knuth says on the topic of early optimization .