graniteds.orgCommunity Documentation

Chapter 13. Big Numbers Implementations

13.1. Working with Long, BigInteger or BigDecimal AS3 Types
13.2. Serializing Long, BigInteger or BigDecimal
13.3. Integration with Code Generation Tools
13.4. Note on Performance

Number serialization with the standard AMF3 protocol suffers from a lack of precision and support: Java long (64 bits integers), BigInteger and BigDecimal types are converted to ActionScript 3 Number or String (see "Converting data from Java to ActionScript"). These conversions lead to either approximation (significant bits may be lost) or uselessness (you can't do any arithmetic operation with strings and you can't control the way their string representations are produced).

Because GraniteDS doesn't allow string to number or number to string conversions (see Mapping Java and AS3 Objects), BigInteger and BigDecimal, like long types, are both converted to Number by default, with even more potential approximations.

Starting with the release 2.2, GraniteDS offers ActionScript 3 implementations for Long, BigInteger and BigDecimal, and features a serialization mechanism that preserves the exact value of each type (see API documentation here).

The GraniteDS Long class let you do calculation with 64 bits signed integers. All arithmetic operations are provided, as well as bitwise, bit shift and comparison operator equivalents:

The Long Type:

import org.granite.math.Long;

var a:Long = new Long("9223372036854775807"); // or 0x7fffffffffffffff (max long value)
trace(a); // "9223372036854775807"
trace(a.toHexString()); // "7fffffffffffffff"

a = a.subtract(7);
trace(a); // "9223372036854775800"
trace(a.toHexString()); // "7ffffffffffffff8"

a = a.rightShift(4); // or a.divide(16)
trace(a); // "576460752303423487"
trace(a.toHexString()); // "7ffffffffffffff"

// etc.

// Wrong values with Numbers:
var b:Number = new Number("9223372036854775807"); // max long value
trace(b); // "9223372036854776000" (truncated value)...
        

As you already have noticed from the above code, Long instances (as well as BigInteger and BigDecimal instances) are immutable: a.multiply(2) won't change the value of a, unless if you save the returned value of the method into the variable a (ie: a = a.multiply(2)).

The BigInteger class, as its Java equivalent, represent an immutable arbitrary-precision integer. It provides analogues to all of ActionScript 3's primitive integer operators (+, -, *, /), as well as comparison operators.

The BigInteger Type:

import org.granite.math.BigInteger;

var a:BigInteger = new BigInteger("9223372036854775807"); // max long value

a = a.add(1);
trace(a); // "9223372036854775808"

a = a.multiply(1000000);
trace(a); // "9223372036854775808000000"

// etc.
        

With the BigInteger class, you cannot face the risk of an overflow due to the limited storage of a standard numeric type: a BigInteger value can be arbitrary big and its value is only limited by the Flash VM memory.

The BigDecimal class, as its Java equivalent, represent an immutable, arbitrary-precision signed decimal number. It provides operations for arithmetic, scale manipulation, rounding, comparison and format conversion.

The BigDecimal Type:

import org.granite.math.BigDecimal;
import org.granite.math.RoundingMode;

var a:BigDecimal = new BigDecimal("1"); // or BigDecimal.ONE

a = a.divide(3, 2, RoundingMode.DOWN);
trace(a); // "0.33"

// etc.
        

With the BigDecimal class, you can control precisely the scale and the rounding behavior of a division. The above code means: divide 1 by 3, with 2 digits to the right of the decimal point left in the result and apply a down rounding mode (truncate all extra digits). Like BigInteger instances, BigDecimal instances have no precision limitation other than the Flash VM memory.

Note

Arithmetic binary methods are more versatile than their Java equivalents. You may pass not only BigDecimal instances as parameters to add, subtract, multiply and divide, but also int, Number or String literals. They will be automatically converted to BigDecimal instances and that's why a.add(3) is legal, as well as a.add("3") and a.add(new BigDecimal("3"). This is also true for the Long and BigInteger types.

See the API documentation for more informations.

As said above, without any specific configuration, long, Long, BigInteger or BigDecimal Java types are converted to AS3 Number (and vice-versa). To enable serialization into their ActionScript 3 equivalents, you must enable specific externalizers in your granite-config.xml file:



<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE granite-config PUBLIC "-//Granite Data Services//DTD granite-config internal//EN"
    "http://www.graniteds.org/public/dtd/2.3.0/granite-config.dtd">

<granite-config>
    <externalizers>
        <externalizer
            type="org.granite.messaging.amf.io.util.externalizer.LongExternalizer">
            <include instance-of="java.lang.Long"/>
        </externalizer>
        <externalizer
            type="org.granite.messaging.amf.io.util.externalizer.BigIntegerExternalizer">
            <include instance-of="java.math.BigInteger"/>
        </externalizer>
        <externalizer
            type="org.granite.messaging.amf.io.util.externalizer.BigDecimalExternalizer">
            <include instance-of="java.math.BigDecimal"/>
        </externalizer>
    </externalizers>
<granite-config>
        

You may of course enable only the externalizers you need, instead of configuring all of them.

With this configuration, you will be able to receive and send big numbers without potential lose of precision. Suppose you have a Java service that returns and receives BigDecimal values:



import java.math.BigDecimal;
public class TestBigDecimal {
    public BigDecimal returnBigValue() {
        return new BigDecimal("10000000000000000000000000000.001");
    }
    public void receiveBigValue(BigDecimal value) {
        // do something with the value.
    }
}
        

Within your Flex code, provided that the BigDecimalExternalizer is configured, you could use this kind of code:

import org.granite.math.BigDecimal;

private var testBigDecimalService:RemoteObject = null;
private var value:BigDecimal = null;

...

protected function onReturnBigValueResult(event:ResultEvent):void {
    value = event.result as BigDecimal;
}

...

protected function sendBigValue():void {
    testBigDecimalService.receiveBigValue(new BigDecimal("0.3333"));
}
        

The same kind of code will work with long, Long and BigInteger types as well.

Beside calling methods that return or receive big numbers, you may have Java bean or entity properties that use long, Long, BigInteger or BigDecimal types. The standard GraniteDS code generation tools (see Gas3 Code Generator) follow the standard serialization mechanism (ie: converting long and big number types to AS3 numbers) and generates Number typed variables for Java long and big number types.

In order to tell the code generation tools to generate AS3 Long, BigInteger and BigDecimal typed variables, you must enable three related options.

With the GraniteDS Eclipse builder, you will have to go to the "Options" panel and enable these three options:

With the Gas3 Ant task, you will use the following configuration in build.xml:



<gas3
    externalizelong="true"
    externalizebiginteger="true"
    externalizebigdecimal="true"
    ...>
   ...
</gas3>
        

Again, you may enable only one or more of these options, but you must follow the corresponding granite-config.xml configuration.

Suppose you have this kind of Java bean:



import java.math.BigDecimal;
import java.math.BigInteger;
public class MyBean {
    private BigDecimal bd;
    private BigInteger bi;
    private Long l1;
    private long l2;
    public BigDecimal getBd() {
        return bd;
    }
    public void setBd(BigDecimal bd) {
        this.bd = bd;
    }
    // other get/set...
}
        

With all options enabled, the result of generation will be has follow:

import org.granite.math.BigDecimal;
import org.granite.math.BigInteger;
import org.granite.math.Long;

[RemoteClass(alias="path.to.MyBean")]
public class MyBean {

    private var _bd:BigDecimal;
    private var _bi:BigInteger;
    private var _l1:Long;
    private var _l2:Long;

    public function get bd():BigDecimal {
        return _bd;
    }
    public function set bd(value:BigDecimal):void {
        _bd = value;
    }

    // other get/set...
}
        

With standard Gas3 configuration, the ActionScript 3 type generated for each property would have been Number.

The ActionScript3 implementation of big numbers give reasonable operation performance, but not as good as their Java equivalents. At this time, due to the lack of a native 64 bits type in the Flash VM, arithmetic operations of the Long, BigInteger and BigDecimal AS3 implementations rely partly on short (16 bits) native operations rather than integer 32 bits operations, in order to control overflows. This leads to overall good performance, but not suitable for massive and complex computations.