转自: http://120183228.iteye.com/blog/1772782
先说清楚为什么要做这个东西。
1 系统在运算浮点数的时候并不一定都准确 java在没出BigDecimal的时候也是一样,运算的时候或多或少0.000000(N个0)1
无论加减乘除都有可能出现 甚至一个小数乘以100都有可能出现这种情况
2 flex
Math.round(n):四舍五入 只计算第一位 比如0.5 =1 0.45 = 0
Math.floor(n):返回小于或等于指定数字n 的最大整数
Math.ceil(n):返回大于或等于指定数字n 的最小整数
原理是通过设置最大保留小数位四舍五入(从最后一位开始四舍五入)截掉小数变整数运算 运算完后再加上小数位返回。 测试类里面有使用直接运算和使用BigDecimal.as运算两种方式,有兴趣的朋友多试试几个浮点数运算,很容易出现或多或少0.00000...1的情况。
由于项目中金额哪怕是0.01也有可能后台金额验证出错,也许你判断的时候可以忽略掉0.01的差距,但是从源头解决问题不更好吗?
- BigDecimal.as
- package
- {
- /**
- * 准确的计算浮点数 ,四舍五入方式是采用最后一位一直到保留小数位的判断。
- * */
- public class BigDecimal
- {
- /**
- * 保留小数位
- * */
- private var _maxDecimalLength:int = 2;
- /**
- * 设置保留小数位 如doubleValue有值 设置之后会四舍五入
- * */
- public function set maxDecimalLength(i:int):void{
- _maxDecimalLength = i;
- _doubleValue = round(_doubleValue.toString());
- }
- private var _validateStringThrow:Boolean = true;
- /**
- * 设置验证字符串数据时如不是数字类型是否抛异常
- * 如果不抛异常默认值为0
- * */
- public function set validateStringThrow(b:Boolean):void{
- _validateStringThrow = b;
- }
- private var _doubleValue:Number = 0;
- /**
- * 设值
- * @param value 可以是String、Number、BigDecimal的任意类型
- * @throws Error 传入的String类型不是数字
- * @throws Error 传入的类型不是String、Number、BigDecimal其中的一个类型
- * */
- public function set doubleValue(value:*):void{
- _doubleValue = getNumber(value);
- }
- public function get doubleValue():Number{
- return _doubleValue;
- }
- /**
- * @param value 可以是String、Number、BigDecimal的任意类型
- * @param arg1 保留小数位
- * @param arg2 设置验证字符串数据时如不是数字类型是否抛异常,如果不抛异常默认值为0。
- * @throws Error 传入的String类型不是数字
- * @throws Error 传入的类型不是String、Number、BigDecimal其中的一个类型
- * */
- public function BigDecimal(value:*,arg1:int = 2,arg2:Boolean = true)
- {
- _maxDecimalLength = arg1;
- _validateStringThrow = arg2;
- _doubleValue = getNumber(value);
- }
- /**
- * 获得String、Number、BigDecimal类型的值
- * @param value 可以是String、Number、BigDecimal的任意类型
- * @throws Error 传入的String类型不是数字
- * @throws Error 传入的类型不是String、Number、BigDecimal其中的一个类型
- * */
- private function getNumber(value:*):Number{
- var _num:Number = 0;
- if(value is String){
- if(new Number(value).toString()=="NaN"){
- if(_validateStringThrow){
- throw(new Error("值:"+value+" 不是正确的数字类型!"));
- }else{
- _doubleValue = 0;
- }
- }else{
- _num = round(value);
- }
- }else if(value is Number){
- _num = round(value.toString());
- }else if(value is BigDecimal){
- _num = round(value.doubleValue.toString());
- }else{
- throw(new Error("BigDecimal不支持此类型的数据:"+value));
- }
- return _num;
- }
- /**
- * 将移除的小数点加上
- * @throws _str 可以是String、Number的类型
- * @throws _mDL 相乘后应该恢复的是双倍长度的保留小数长度 相除应该是0 加减就是保留小数长度
- * */
- private function getDecimalNumber(_str:*,_mDL:Number = NaN):Number{
- var str:String = _str.toString();
- if(_mDL.toString()=="NaN") _mDL = _maxDecimalLength;
- var _retrunValue:String = "";
- if(str.length>_mDL){
- var _indexOf:Number = str.indexOf(".");
- if(_indexOf==-1){
- _retrunValue = str.substring(0,str.length-_mDL)+"."+str.substring(str.length-_mDL,str.length);
- }else{
- str = str.replace(".","");
- _retrunValue = str.substring(0,_indexOf-_mDL)+"."+str.substring(_indexOf-_mDL,str.length);
- }
- }else{
- _retrunValue = "0.";
- for (var i:int = 0; i <_mDL-str.length; i++)
- {
- _retrunValue += "0";
- }
- _retrunValue += str;
- }
- return new Number(_retrunValue);
- }
- /**
- * 将多出的小数点去掉
- *
- * */
- private function getIntegerNumber(str:*):Number{
- var _arr:Array = str.toString().split(".");
- var _retrunValue:String = _arr[0].toString();
- if(_arr.length>1){
- _retrunValue += _arr[1].toString();
- for (var i:int = _arr[1].toString().length; i < _maxDecimalLength; i++)
- {
- _retrunValue += "0";
- }
- }else{
- for (var j:int = 0; j < _maxDecimalLength; j++)
- {
- _retrunValue += "0";
- }
- }
- return new Number(_retrunValue);
- }
- /**
- * 四舍五入
- * */
- private function round(value:String):Number{
- var _arr:Array = value.split(".");
- if(_arr.length>1&&_arr[1].toString().length>_maxDecimalLength){
- var _arr0:String = _arr[0].toString();
- var _arr1:String = _arr[1].toString();
- var _v1:String = _arr0+_arr1.substring(0,_maxDecimalLength);
- var _v2:String = _arr1.substring(_maxDecimalLength,_arr1.length);
- while(true){
- if(_v2.length == 1){
- if(Number(_v2)>4){
- _v1 = (new Number(_v1)+1).toString();
- }
- break;
- }
- if(Number(_v2.charAt(_v2.length-1))>4){
- _v2 = (new Number(_v2.substring(0,_v2.length - 1))+1).toString();
- }else{
- _v2 = (new Number(_v2.substring(0,_v2.length - 1))).toString();
- }
- }
- return getDecimalNumber(_v1);
- }
- return new Number(value);
- }
- /**
- * 相加
- * @param value 可以是String、Number、BigDecimal的任意类型
- * */
- public function sum(value:*):Number{
- var n1:Number = getIntegerNumber(doubleValue);
- var n2:Number = getIntegerNumber(getNumber(value));
- _doubleValue = getDecimalNumber(n1 + n2);
- return _doubleValue;
- }
- /**
- * 相减
- * @param value 可以是String、Number、BigDecimal的任意类型
- * */
- public function sub(value:*):Number{
- var n1:Number = getIntegerNumber(doubleValue);
- var n2:Number = getIntegerNumber(getNumber(value));
- _doubleValue = getDecimalNumber(n1 - n2);
- return _doubleValue;
- }
- /**
- * 相乘
- * @param value 可以是String、Number、BigDecimal的任意类型
- * */
- public function mul(value:*):Number{
- var n1:Number = getIntegerNumber(doubleValue);
- var n2:Number = getIntegerNumber(getNumber(value));
- _doubleValue = round(getDecimalNumber((n1 * n2),_maxDecimalLength*2).toString());
- return _doubleValue;
- }
- /**
- * 相除
- * @param value 可以是String、Number、BigDecimal的任意类型
- * */
- public function div(value:*):Number{
- var n1:Number = getIntegerNumber(doubleValue);
- var n2:Number = getIntegerNumber(getNumber(value));
- if(n2==0){
- _doubleValue = 0;
- }else{
- _doubleValue = round(getDecimalNumber((n1 / n2),0).toString());
- }
- return _doubleValue;
- }
- }
- }
测试类
- <?xml version="1.0" encoding="utf-8"?>
- <s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
- xmlns:s="library://ns.adobe.com/flex/spark"
- xmlns:mx="library://ns.adobe.com/flex/mx">
- <fx:Script>
- <![CDATA[
- import mx.collections.ArrayCollection;
- import mx.controls.Alert;
- import mx.events.FlexEvent;
- [Bindable]
- private var typeData:ArrayCollection = new ArrayCollection([
- {"label":"加", "data":1},
- {"label":"减", "data":2},
- {"label":"乘", "data":3},
- {"label":"除", "data":4}
- ]);
- [Bindable]
- private var _result:String = "";
- protected function button1_clickHandler(event:MouseEvent):void
- {
- var b1:BigDecimal = new BigDecimal(t1.text);
- var b2:BigDecimal = new BigDecimal(t2.text);
- if(_type.selectedItem["data"]=="1"){
- _result = b1.sum(b2).toString();
- }else if(_type.selectedItem["data"]=="2"){
- _result = b1.sub(b2).toString();
- }else if(_type.selectedItem["data"]=="3"){
- _result = b1.mul(b2).toString();
- }else if(_type.selectedItem["data"]=="4"){
- _result = b1.div(b2).toString();
- }
- }
- protected function button2_clickHandler(event:MouseEvent):void
- {
- var b1:Number = new Number(t1.text);
- var b2:Number = new Number(t2.text);
- if(_type.selectedItem["data"]=="1"){
- _result = (b1+b2).toString();
- }else if(_type.selectedItem["data"]=="2"){
- _result =( b1-b2).toString();
- }else if(_type.selectedItem["data"]=="3"){
- _result = (b1*b2).toString();
- }else if(_type.selectedItem["data"]=="4"){
- _result = (b1/b2).toString();
- }
- }
- ]]>
- </fx:Script>
- <fx:Declarations>
- <!-- 将非可视元素(例如服务、值对象)放在此处 -->
- </fx:Declarations>
- <mx:VBox>
- <s:TextInput id="t1"/>
- <s:ComboBox id="_type" labelField="label" dataProvider="{typeData}" selectedIndex="0"></s:ComboBox>
- <s:TextInput id="t2"/>
- <s:Label text="值:{_result}"/>
- <s:Button id="BigDecimalAccount" label="使用BigDecimal运算" click="button1_clickHandler(event)"/>
- <s:Button id="NumberAccount" label="使用系统直接运算" click="button2_clickHandler(event)"/>
- </mx:VBox>
- </s:WindowedApplication>