Flex 准确基于四舍五入的浮点运算

先说清楚为什么要做这个东西。

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

这种时候咋办

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值