Как бороться с точностью чисел в Actionscript? - PullRequest
17 голосов
/ 11 марта 2009

У меня есть объекты BigDecimal, сериализованные с BlazeDS в Actionscript. Как только они нажимают Actionscript как объекты Number, они имеют такие значения, как:

140475.32 превращается в 140475.31999999999998

Как мне с этим справиться? Проблема в том, что если я использую NumberFormatter с точностью до 2, то значение усекается до 140475.31. Есть идеи?

Ответы [ 14 ]

33 голосов
/ 11 марта 2009

Это мое общее решение проблемы (у меня об этом написано здесь ) :

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.


Редактировать

Основываясь на анонимном ответе ниже , есть приятное упрощение параметра метода, которое делает точность намного более интуитивной. например:

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. Я добавил это здесь на всякий случай, если кто-то видит это как ответ и не прокручивает вниз ...

20 голосов
/ 09 октября 2009

Просто небольшая вариация 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
10 голосов
/ 23 сентября 2009

Я использовал Number.toFixed(precision) в ActionScript 3, чтобы сделать это: 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));                                                
4 голосов
/ 08 октября 2009

Я преобразовал Java BigDecimal в ActionScript. У нас не было выбора, так как мы вычисляем для финансового приложения.

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

1 голос
/ 19 октября 2012

Удивительно, но функция округления в 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);
}
1 голос
/ 06 октября 2011

Я перенес реализацию IBM ICU BigDecimal для клиента Actionscript. Кто-то еще опубликовал свою почти идентичную версию здесь как проект кода Google. Наша версия добавляет несколько удобных методов для сравнения.

Вы можете расширить конечную точку 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;
    }

}
1 голос
/ 20 сентября 2011

ребята, просто проверьте решение:

            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
            }
1 голос
/ 26 августа 2010

Мы смогли повторно использовать один из доступных классов BigDecimal.as в Интернете и расширенные блэйджи, используя подклассы из AMF3Output, вам нужно будет указать свой собственный класс конечной точки в файлах 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);
        }
    }
}

так просто! тогда у вас есть встроенная поддержка BigDecimal с использованием Blazeds, Wooohoo! Убедитесь, что ваш класс BigDecimal as3 реализует IExternalizable

ура, JB

1 голос
/ 10 августа 2010

GraniteDS 2.2 имеет реализации BigDecimal, BigInteger и Long в ActionScript3, опции сериализации между Java / Flex для этих типов и даже опции инструментов генерации кода для генерации переменных больших чисел AS3 для соответствующих Java.

Подробнее здесь: http://www.graniteds.org/confluence/display/DOC22/2.+Big+Number+Implementations.

1 голос
/ 29 июня 2010

Я обнаружил, что BlazeDS также поддерживает сериализацию объектов Java BigDecimal в строки ActionScript. Поэтому, если вам не нужны данные ActionScript, чтобы быть числами (вы не выполняете математические операции на стороне Flex / ActionScript), тогда отображение String работает хорошо (без странностей округления). См. Эту ссылку для параметров отображения BlazeDS: http://livedocs.adobe.com/blazeds/1/blazeds_devguide/help.html?content=serialize_data_2.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...