Подтвердить что ты не робот

Как бороться с точностью числа в ActionScript?

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

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

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

4b9b3361

Ответ 1

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

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.


Edit

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

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

Ответ 2

Небольшое отклонение от 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

Ответ 3

Я использовал 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

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

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

Ответ 5

Вы можете использовать свойство: rounding = "ближайшее"

В NumberFormatter округление имеет 4 значения, которые вы можете выбрать: rounding = "none | up | down | near". Я думаю, что с вашей ситуацией вы можете выбрать округление = "ближайшее".

- chary -

Ответ 6

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

Ответ 7

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

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

Ответ 8

Мы смогли повторно использовать один из доступных классов BigDecimal.as в Интернете и расширенные blazeds с помощью sublassing из 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

cheers, jb

Ответ 9

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

            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
            }

Ответ 10

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

Вы можете расширить конечную точку Blaze AMF, чтобы добавить поддержку сериализации для BigDecimal. Обратите внимание, что код в другом ответе кажется неполным, и по нашему опыту он не работает на производстве.

AMF3 предполагает, что дублирующиеся объекты, черты и строки отправляются по ссылке. Таблицы ссылок на объекты должны храниться синхронно во время сериализации или клиент потеряет синхронизацию этих таблиц во время десериализации и начнет бросать вызовы при запуске класса или искажать данные в полях, которые не совпадают, но отличать ok...

Вот скорректированный код:

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;
    }

}

Ответ 11

Удивительно, что круговая функция в MS Excel дает нам разные значения, которые вы указали выше. Например, в Excel

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

Итак, мой обходной путь для Excel round похож:

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);
}

Ответ 12

Если вам известна точность, которую вам нужно заранее, вы можете сохранить числа, масштабированные так, чтобы наименьшая сумма, в которой вы нуждаетесь, является целой стоимостью. Например, сохраните цифры как центы, а не доллары.

Если это не вариант, как насчет этого:

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

(Тем не менее, вы печатаете без запятых.)

Это не будет работать для действительно больших чисел, хотя, потому что вы все равно можете потерять точность.

Ответ 13

Вы можете проголосовать и посмотреть запрос на улучшение в системе отслеживания ошибок Jayer Flash Player на 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)

Ответ 14

Это больше похоже на транспортную проблему, число правильное, но масштаб игнорируется. Если номер должен быть сохранен как BigDecimal на сервере, вы можете захотеть преобразовать его на стороне сервера в менее двусмысленный формат (Number, Double, Float) перед его отправкой.