如何处理Actionscript中的Number精度?

时间:2009-03-11 00:15:24

标签: flex actionscript-3 actionscript

我将BigDecimal对象与BlazeDS序列化为Actionscript。一旦他们将Actionscript作为Number对象命中,它们的值就像:

140475.32变成140475.31999999999998

我该如何处理?问题是,如果我使用精度为2的NumberFormatter,则该值将被截断为140475.31。有什么想法吗?

14 个答案:

答案 0 :(得分:33)

这是我解决问题的通用解决方案(我有blogged about this here

var toFixed:Function = function(number:Number, factor:int) {
  return Math.round(number * factor)/factor;
}

例如:

trace(toFixed(0.12345678, 10)); //0.1
  • 0.12345678乘以10;这给了我们1.2345678
  • 当我们围绕1.2345678时,我们会得到1.0
  • 最后,1.0除以10等于0.1

另一个例子:

trace(toFixed(1.7302394309234435, 10000)); //1.7302
  • 1.7302394309234435乘以10000;这给了我们17302.394309234435
  • 当我们围绕17302.394309234435时,我们得到17302
  • 最后,17302除以10000等于1.7302

<小时/> 的修改

基于匿名答案below,对方法的参数进行了很好的简化,使得精度更加直观。 e.g:

var setPrecision:Function = function(number:Number, precision:int) {
 precision = Math.pow(10, precision);
 return Math.round(number * precision)/precision;
}

var number:Number = 10.98813311;
trace(setPrecision(number,1)); //Result is 10.9
trace(setPrecision(number,2)); //Result is 10.98
trace(setPrecision(number,3)); //Result is 10.988 and so on

N.B。我在这里添加了这个,以防万一有人将此视为答案而不向下滚动...

答案 1 :(得分:20)

Frasers功能只是略有变化,对于任何感兴趣的人。

function setPrecision(number:Number, precision:int) {
 precision = Math.pow(10, precision);
 return (Math.round(number * precision)/precision);
}

所以要使用:

var number:Number = 10.98813311;
trace(setPrecision(number,1)); //Result is 10.9
trace(setPrecision(number,2)); //Result is 10.98
trace(setPrecision(number,3)); //Result is 10.988 and so on

答案 2 :(得分:10)

我在ActionScript 3中使用了Number.toFixed(precision)来执行此操作:http://livedocs.adobe.com/flex/3/langref/Number.html#toFixed%28%29

它正确处理舍入并指定要显示的小数后面的位数 - 与Number.toPrecision()不同,它限制了要显示的总位数,而不管小数位的位置。

var roundDown:Number = 1.434;                                             
// will print 1.43                                                        
trace(roundDown.toFixed(2));                                              

var roundUp:Number = 1.436;                                               
// will print 1.44                                                        
trace(roundUp.toFixed(2));                                                

答案 3 :(得分:4)

我将BigDecimal的Java转换为ActionScript。 自从我们计算财务申请以来,我们别无选择。

http://code.google.com/p/bigdecimal/

答案 4 :(得分:1)

您可以使用property:rounding =“nearest”

在NumberFormatter中,舍入有4个值,您可以选择:rounding =“none | up | down | nearest”。我认为根据你的情况,你可以选择舍入=“最近”。

- chary -

答案 5 :(得分:1)

我发现BlazeDS也支持将Java BigDecimal对象序列化为ActionScript字符串。因此,如果您不需要ActionScript数据为Numbers(您没有在Flex / ActionScript端进行任何数学运算),那么String映射效果很好(没有舍入怪异)。请参阅此链接以了解BlazeDS映射选项:http://livedocs.adobe.com/blazeds/1/blazeds_devguide/help.html?content=serialize_data_2.html

答案 6 :(得分:1)

GraniteDS 2.2在ActionScript3中具有BigDecimal,BigInteger和Long实现,Java / Flex之间的这些类型的序列化选项,甚至是代码生成工具选项,以便为相应的Java生成AS3大数字变量。

在此处查看更多内容:http://www.graniteds.org/confluence/display/DOC22/2.+Big+Number+Implementations

答案 7 :(得分:1)

我们能够在Web上重用一个可用的BigDecimal.as类,并通过从AMF3Output子类化扩展blazeds,你需要在flex xml文件中指定你自己的端点类,在你可以插入的自定义端点中您自己的序列化程序,它实例化AMF3Output子类。

public class EnhancedAMF3Output extends Amf3Output {

    public EnhancedAMF3Output(final SerializationContext context) {
        super(context);
    }

    public void writeObject(final Object o) throws IOException {           
        if (o instanceof BigDecimal) {
            write(kObjectType);
            writeUInt29(7); // write U290-traits-ext (first 3 bits set)
            writeStringWithoutType("java.math.BigDecimal");
            writeAMFString(((BigDecimal)o).toString());
        } else {
            super.writeObject(o);
        }
    }
}

就这么简单!那么你有使用blazeds的原生BigDecimal支持,wooohoo! 确保你的BigDecimal as3类实现了IExternalizable

欢呼,jb

答案 8 :(得分:1)

伙计们,请检查解决方案:

            protected function button1_clickHandler(event:MouseEvent):void
            {
                var formatter:NumberFormatter = new NumberFormatter();
                formatter.precision = 2;
                formatter.rounding = NumberBaseRoundType.NEAREST;
                var a:Number = 14.31999999999998;

                trace(formatter.format(a)); //14.32
            }

答案 9 :(得分:1)

我为Actionscript客户端移植了BigDecimal的IBM ICU实现。 Someone else has published their nearly identical version here as a google code project.我们的版本添加了一些方便的比较方法。

您可以扩展Blaze AMF端点以添加对BigDecimal的序列化支持。请注意,其他答案中的代码似乎不完整,根据我们的经验,它无法在生产中使用。

AMF3假定通过引用发送重复的对象,特征和字符串。在序列化时,对象引用表需要保持同步,否则客户端将在反序列化期间松开这些表的同步并开始抛出类转换错误,或者破坏不匹配的字段中的数据,但是转换为ok ... < / p>

以下是更正后的代码:

public void writeObject(final Object o) throws IOException {
    if (o instanceof BigDecimal) {
        write(kObjectType);
        if(!byReference(o)){   // if not previously sent
            String s = ((BigDecimal)o).toString();                  
            TraitsInfo ti = new TraitsInfo("java.math.BigDecimal",false,true,0);
            writeObjectTraits(ti); // will send traits by reference
            writeUTF(s);
            writeObjectEnd();  // for your AmfTrace to be correctly indented
        }
    } else {
            super.writeObject(o);
        }
}

还有另一种发送类型化对象的方法,它不需要客户端上的Externalizable。客户端将在对象上设置 textValue 属性:

TraitsInfo ti = new TraitsInfo("java.math.BigDecimal",false,false,1);           
ti.addProperty("textValue");
writeObjectTraits(ti);
writeObjectProperty("textValue",s);

在任何一种情况下,您的Actionscript类都需要此标记:

[RemoteClass(alias="java.math.BigDecimal")]

Actionscript类还需要一个text属性来匹配你选择发送的将初始化BigDecimal值的属性,或者在Externalizable对象的情况下,需要一些像这样的方法:

public  function writeExternal(output:IDataOutput):void {       
    output.writeUTF(this.toString());
}
public  function readExternal(input:IDataInput):void {          
    var s:String = input.readUTF();
    setValueFromString(s);
}

此代码仅涉及从服务器到客户端的数据。为了从客户端到服务器的另一个方向反序列化,我们选择扩展AbstractProxy,并使用包装类在创建实际对象之前临时存储BigDecimal的字符串值,因为您无法实例化BigDecimal然后分配值,因为Blaze / LCDS的设计应该是所有对象的情况。

这是绕过默认处理的代理对象:

public class BigNumberProxy extends AbstractProxy {

    public BigNumberProxy() {
        this(null);
    }

    public BigNumberProxy(Object defaultInstance) {
        super(defaultInstance);
        this.setExternalizable(true);

        if (defaultInstance != null)
           alias = getClassName(defaultInstance);
    }   

    protected String getClassName(Object instance) {
        return((BigNumberWrapper)instance).getClassName();
    }

    public Object createInstance(String className) {
        BigNumberWrapper w = new BigNumberWrapper();
        w.setClassName(className);
        return w;
    }

    public Object instanceComplete(Object instance) {
    String desiredClassName = ((BigNumberWrapper)instance).getClassName();
    if(desiredClassName.equals("java.math.BigDecimal"))
        return new BigDecimal(((BigNumberWrapper)instance).stringValue);
    return null;
}

    public String getAlias(Object instance) {
        return((BigNumberWrapper)instance).getClassName();
    }

}

此语句必须在您的应用程序中的某个位置执行,以将代理对象绑定到您要控制的类。我们使用静态方法:

PropertyProxyRegistry.getRegistry().register(
    java.math.BigDecimal.class, new BigNumberProxy());

我们的包装类看起来像这样:

public class BigNumberWrapper implements Externalizable {

    String stringValue;
    String className;

    public void readExternal(ObjectInput arg0) throws IOException, ClassNotFoundException {
        stringValue = arg0.readUTF();       
    }

    public void writeExternal(ObjectOutput arg0) throws IOException {
        arg0.writeUTF(stringValue);     
    }

    public String getStringValue() {
        return stringValue;
    }

    public void setStringValue(String stringValue) {
        this.stringValue = stringValue;
    }

    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

}

答案 10 :(得分:1)

令人惊讶的是,MS Excel中的圆函数给出了我们上面提到的不同值。 例如在Excel中

Round(143,355;2) = 143,36

所以我的Excel圆形解决方法就像:

public function setPrecision(number:Number, precision:int):Number {
precision = Math.pow(10, precision);

const excelFactor : Number = 0.00000001;

number += excelFactor;

return (Math.round(number * precision)/precision);
}

答案 11 :(得分:0)

如果您事先知道所需的精度,则可以存储缩放的数字,以便您需要的最小量是一个整数值。例如,将数字存储为美分而不是美元。

如果这不是一个选项,那么这样的事情怎么样:

function printTwoDecimals(x)
{
   printWithNoDecimals(x);
   print(".");
   var scaled = Math.round(x * 100);
   printWithNoDecimals(scaled % 100);
}

(但是你打印的时候没有小数点。)

但这对真正的大数字不起作用,因为你仍然会失去精确度。

答案 12 :(得分:0)

您可以在https://bugs.adobe.com/jira/browse/FP-3315

的Flash PLayer Jira错误跟踪系统中投票并观看增强请求

同时使用Number.toFixed()解决方法看看: (http://livedocs.adobe.com/flex/3/langref/Number.html#toFixed%28%29

或使用那里的开源实现:(http://code.google.com/p/bigdecimal/)或(http://www.fxcomps.com/money.html

至于序列化工作,如果你使用Blazeds或LCDS它会很小,因为它们支持Java BigDecimal序列化(到String)cf. (http://livedocs.adobe.com/livecycle/es/sdkHelp/programmer/lcds/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=serialize_data_3.html

答案 13 :(得分:-1)

这似乎更像是一个运输问题,数字是正确的,但忽略了规模。如果必须将数字存储为服务器上的BigDecimal,您可能希望在发送之前将服务器端转换为不太模糊的格式(Number,Double,Float)。