先说清楚为什么要做这个东西。
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>
最后讨论一个四舍五入的疑问,是先四舍五入再运算还是运算完后四舍五入。
列入 1.064 - 1.055
如果先运算四舍五入保留两位小数 那么就等于1.06 - 1.06 = 0.00
如果运算完后四舍五入保留两位小数 那么就等于 1.064 - 1.055 = 0.009 0.009四舍五入等于0.01
这种时候咋办