Number serialization with the standard AMF3 protocol suffers from a lack of precision and support: Java long
(64 bits integers),
and
types are converted to
ActionScript 3 or String
(see ).
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
).
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)...
The 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 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.
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.
<?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.
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.
}
}
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
.