【问题标题】:How to deal with Number precision in Actionscript?如何处理 Actionscript 中的数字精度?
【发布时间】:2010-10-12 13:54:58
【问题描述】:

我使用 BlazeDS 将 BigDecimal 对象序列化为 Actionscript。一旦它们将 Actionscript 作为 Number 对象命中,它们的值如下:

140475.32变成140475.31999999999998

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

【问题讨论】:

  • 在 AS 3 中仅供参考,看起来他们实际上添加了“toPrecision”方法。 livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/…
  • 如果有人偶然发现这一点,接受的答案并非在所有情况下都是正确的。一个简单的例子是 0.575。如果你做 Math.round(.575 * 100)/100 你会得到 .57。

标签: apache-flex actionscript-3 actionscript


【解决方案1】:

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】:

    这是我对问题的通用解决方案(我有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,对方法的参数进行了很好的简化,使精度更加直观。 例如:

    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
    

    注意我在此处添加了此内容,以防万一有人将此视为答案并且不向下滚动...

    【讨论】:

    • 为什么是 "=" 而不是 ""?
    • 这与原生的 toFixed 函数有何不同?
    • @grapefrukt - toFixed 只是在原始问题中给出了同样的问题!因为那只是给你正确的位数而没有正确的四舍五入。其实 toPrecision 看起来是这样的,但我不知道这已经包含在 AS 中,从 2.0 开始我就没有碰过它!
    • @jder - 风格。 x *= y 是 x = x * y
    • toPrecision 实际上返回一个字符串,这意味着您必须重新解析它。不错,Adobe!
    【解决方案3】:

    令人惊讶的是,MS Excel 中的 round 函数为我们提供了与您在上面介绍的不同的值。 例如在 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);
    }
    

    【讨论】:

      【解决方案4】:

      我们能够重用 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 支持,哇哦! 确保您的 BigDecimal as3 类实现 IExternalizable

      干杯,jb

      【讨论】:

        【解决方案5】:

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

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

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

        以下是更正后的代码:

        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 类还需要一个文本属性来匹配您选择发送的将初始化 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;
            }
        
        }
        

        【讨论】:

        • 您有完整的示例来演示其设置方式吗?
        【解决方案6】:

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

        受保护的功能 button1_clickHandler(event:MouseEvent):void { var formatter:NumberFormatter = new NumberFormatter(); formatter.precision = 2; formatter.rounding = NumberBaseRoundType.NEAREST; var a:Number = 14.31999999999998; 跟踪(格式化程序。格式(a)); //14.32 }

        【讨论】:

          【解决方案7】:

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

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

          【讨论】:

            【解决方案8】:

            您可以在 Flash PLayer Jira 错误跟踪系统https://bugs.adobe.com/jira/browse/FP-3315 中投票并观看增强请求

            同时使用 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)

            【讨论】:

              【解决方案9】:

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

              【讨论】:

                【解决方案10】:

                我将 BigDecimal 的 Java 转换为 ActionScript。 自从我们为金融应用计算以来,我们别无选择。

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

                【讨论】:

                  【解决方案11】:

                  我在 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));                                                
                  

                  【讨论】:

                  • 唯一遗憾的是它没有本地化。如果您的语言使用逗号作为小数点分隔符,则必须使用 NumberFormatter。
                  • 此外,它是一个字符串,而不是一个数字。有时这是可取的(例如在您的跟踪语句中),但并非总是如此,事实上,并非大多数时候。另外,使用parseInt() 转换回来很慢
                  • toFixed 也不适用于所有情况。如果您将 roundUp 替换为 .575,它会产生 .57(.255 会产生 .26;这个特定的数字会产生奇怪的浮点数),因此它似乎会遇到与公认解决方案相同的问题。
                  【解决方案12】:

                  你可以使用属性:rounding = "nearest"

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

                  -- 查利--

                  【讨论】:

                    【解决方案13】:

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

                    【讨论】:

                    • Java BigDecimal 可以转换为 AS3 字符串。 BigDecimal 对于非常大的数字是必要的:Java 中不存在数字;而 double 和 float 类型还不够大。
                    【解决方案14】:

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

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

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

                    (但是你打印时没有小数卡在里面。)

                    但是,这对于真正大数字不起作用,因为您仍然会丢失精度。

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 2019-05-22
                      • 2022-01-20
                      相关资源
                      最近更新 更多