`
tigerl
  • 浏览: 97261 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

超过Long类型表数范围的大数值相加

阅读更多
也许,你很少会遇见这样的计算,当你要计算的值,超过了最大long能表数的范围后,怎么办?本文提供一种比较愚笨的办法,有比这更好的算法,希望贴出来!


原理是,按字符串来处理,如果2个数相加的和<0的时候,说明已经超过了long的最大表数范围,所以按字符串处理,如果不小于0则按long计算!(因为long的最大表数+1为负数)计算的时候按18位截取字符串,因为long的10进制最大表数位数是19位,所以两个18位的最大值相加也还是long类型表数范围内,也即最大位数不会超过19位,而且从左往右数的第一位永远是1,也即进位,不会溢出!比如9+9=18,2个1位数相加,就拿最大值相加也是18,最多也就是2位,而且从左往右第一位永远不会大于1,所以,如果2个一位数相加的结果是2位,那么保留个位数,十位数作为进位,继续相加!

废话不多说,直接看代码,注释就不写那么多了,没那么复杂,debug一次就明了!

package cn.com;

public class BigNumber {
	private final static int threshold = 18;
	
	public static void main(String[] args) {
		
		long l1 = 444L; 
		long l2 = 5555L; 
		long l3 = 9223372036854775807L;
		long l4 = 1L; //5555555555555555555
		long l5 = 555555555555555555L; //5555555555555555555
										//1111111111111111110
									   //11111111111111111110
		System.out.println(String.valueOf(l5).length());
//		l3 = l4;
		String value = add(l1,l2);
		System.out.println(value);
		long s = System.currentTimeMillis();
		value = add(l3,l4);
		long e = System.currentTimeMillis();
		System.out.println("consuming times :"+(e-s));
		System.out.println("string value = "+value+", and length = "+String.valueOf(value).length());
		System.out.println("string value = "+Long.MAX_VALUE+", and length = "+String.valueOf(value).length());
		System.out.println("long value = "+(l3+l4));
	}
	
	public static String summation(String s1 , String s2){
		int len1 = s1.length();
		int len2 = s2.length();
		int maxlen = len1>len2?len1:len2;
		int minlen = len1<len2?len1:len2;
		
		StringBuffer pre0 = new StringBuffer();
		for(int i=0; i<maxlen-minlen; i++){
			pre0.append(0); 
		}
		
		if(len1 < len2){
			s1 = pre0.toString()+s1;
		}else{
			s2 = pre0.toString()+s2;
		}
		
		String temp = "";
		int carry = 0;//进位
		String[] ss1 = null;
		String[] ss2 = null;
		if(maxlen > threshold){
			ss1 = group(s1,maxlen);
			ss2 = group(s2,maxlen);
		}else{
			ss1 = new String[]{s1,""};
			ss2 = new String[]{s2,""};
		}

		for(int i = ss1.length-1; i>=0; i--){
			String temp_str = parseString(ss1[i],ss2[i],carry);
			if(temp_str.length() > threshold){
				temp = temp_str.substring(1,temp_str.length()) + temp;
				carry = 1;
			}else{
				if(temp_str.length() > ss1[i].length()){
					temp = temp_str.substring(1,temp_str.length()) + temp;
					carry = 1;
				}else{
					temp = temp_str + temp;
					carry = 0;
				}
				
			}
		}
		if(carry != 0){
			temp = carry + temp;	
		}
		return temp;
	}
	
	public static String add(String s1, String s2){
		long l1;
		long l2;
		try{
			l1 = Long.parseLong(s1);
			l2 = Long.parseLong(s2);
			if(l1+l2 < 0){
				return summation(s1, s2);
			}else{
				return String.valueOf(l1+l2);
			}
		}catch(NumberFormatException e){
			return summation(s1, s2);
		}
	}
	
	//根据给定的两个long类型的数值,相加后,得到字符串结果,如果结果的长度大于19,说明已经超过了long的最大表数
	public static String add(long l1 , long l2){
		if(l1 + l2 <0){
			return summation(String.valueOf(l1), String.valueOf(l2));
		}else{
			return String.valueOf(l1+l2);
		}
	}
	
	private static String parseString(String s1,String s2,int i){
		long l1 = 0;
		long l2 = 0;
		if(s1!=null && !s1.equals("")){
			l1 = Long.parseLong(s1);
		}
		if(s2!=null && !s2.equals("")){
			l2 = Long.parseLong(s2);
		}
		
		return (l1+l2+i>0) ? (String.valueOf(l1+l2+i)) : ""; 
	}
	
	private static String[] group(String s,int len){
		String[] ss = new String[(len/threshold)+1];
		for (int i = 0; i <= len/threshold; i++) {
			if(i==len/threshold)
				ss[i] = s.substring(i*threshold,len);
			else
				ss[i] = s.substring(i*threshold,i*threshold+threshold);	
		}
		return ss;
	}
	
}





方法里边首先会判断,是否超过了long的表数范围,如果超过才会按字符串处理,否则按long处理
======================================
下面是完善的,长度小的字符串不补0的方法
package com.chinamobile.omae.appmaster.schedule.util;

import java.util.regex.Pattern;

public class BigNumCal {
	private final static int threshold = 18;
	private final static Pattern pattern = Pattern.compile("\\D");

	public static void main(String[] args) {
		String s1 = "a1234";
		String s2 = "888888888888888888888888888888888888888888888888888888";
		add(s1,s2);
	}

	// 字符串加法运算
	private static String summation(String s1, String s2) {
		int len1 = s1.length();
		int len2 = s2.length();
		int maxlen;
		String maxStr = null;
		String minStr = null;
		if(len1>len2){
			maxlen = len1;
			maxStr = s1;
			minStr = s2;
		}else{
			maxlen = len2;
			maxStr = s2;
			minStr = s1;
		}
		String temp = "";
		int carry = 0;// 进位
		String[] maxs = null;
		String[] mins = null;
		if (maxlen > threshold) {
			maxs = group(maxStr, maxlen);
			mins = group(minStr, maxlen);
		} else {
			maxs = new String[] { s1, "" };
			mins = new String[] { s2, "" };
		}
		
		//长度长的字符串分组后的长度,以这个长度为标准来进行循环计算
		//这里为了避免两个数组大小不等而引发的数组越界异常,单独写了一个方法,获取数组中的字符串getString()方法
		int mins_len = mins.length;
		String max_str = null;
		String min_str = "";
		for (int i = maxs.length - 1; i >= 0; i--) {
			max_str = getString(maxs,i);
			
			if(mins_len >= 0)
				min_str = getString(mins,mins_len-1);
			else
				min_str = "";
			
			String temp_str = parseString(max_str, min_str,	carry);
			
			if(mins_len>=0)
				mins_len--;
			//如果大于阈值,则进位置为1,截取阈值范围内的字符串(从第一位截取,第0位是进位)并保留,不再做运算
			if (temp_str.length() > threshold) {
				temp = temp_str.substring(1, temp_str.length()) + temp;
				carry = 1;
			} else {//如果小于阈值,但是结果的长度大于最大字符串数组某下标的长度,也进1,比如8+8=16,结果是16,2位数,大于1位数,所以也需要进1
				if (temp_str.length() > maxs[i].length()) {
					temp = temp_str.substring(1, temp_str.length()) + temp;
					carry = 1;
				} else {
					temp = temp_str + temp;
					carry = 0;
				}
			}
		}
		
		if (carry != 0) //如果不为0则加上,否则就别加了,因为进位为0加上的话结果是"08787XXXX",开头是0,不好看
			temp = carry + temp;
		return temp;
	}

	// 根据给定的两个String类型的数值,相加后,得到字符串结果,如果结果的长度大于19,说明已经超过了long的最大表数
	// 会自动判断传进来的字符串是否超过了long的最大表数范围,并判断2个字符串转换为long类型后的和是否超过long的表数范围,如果没超过,则进行long运算,否则进行字符串运算
	public static String add(String s1, String s2) {
		
		if(pattern.matcher(s1).find() || pattern.matcher(s2).find())
			throw new IllegalArgumentException("Illegal type of String ["+s1+"],must be number of String");
		
		long l1;
		long l2;
		
		try {
			l1 = Long.parseLong(s1);
			l2 = Long.parseLong(s2);
			
			if (l1 + l2 < 0) 
				return summation(s1, s2);
			else 
				return String.valueOf(l1 + l2);
		} catch (NumberFormatException e) {//如果捕获到格式化异常则说明溢出了,按字符串处理
			return summation(s1, s2);
		}
	}

	// 根据给定的两个long类型的数值,相加后,得到字符串结果,如果结果的长度大于19,说明已经超过了long的最大表数
	// 自动判断传入的long值,相加的和是否小于0,如果小于则超过了long的最大表数范围,用字符串处理E172629180
	public static String add(long l1, long l2) {
		if (l1 + l2 < 0) {
			return summation(String.valueOf(l1), String.valueOf(l2));
		} else {
			return String.valueOf(l1 + l2);
		}
	}

	// 根据给定的字符串,转换为long并求和,再转换为String
	private static String parseString(String s1, String s2, int i) {
		long l1 = 0;
		long l2 = 0;
		if (s1 != null && !s1.equals("")) {
			l1 = Long.parseLong(s1);
		}
		if (s2 != null && !s2.equals("")) {
			l2 = Long.parseLong(s2);
		}

		return (l1 + l2 + i > 0) ? (String.valueOf(l1 + l2 + i)) : "";
	}

	// 根据给定的字符串,长度len,截取字符串,如果s的长度大于阈值,则按len位一截取,否则不截取,返回字符串本身
	private static String[] group(String s, int len) {
		String[] ss = null;
		if (s.length() > threshold) {
			ss = new String[(len / threshold) + 1];
			for (int i = 0; i <= len / threshold; i++) {
				if (i == len / threshold)
					ss[i] = s.substring(i * threshold, len);
				else
					ss[i] = s.substring(i * threshold, i * threshold
							+ threshold);
			}
		} else {
			ss = new String[] { s };
		}
		return ss;
	}

	// 根据给定的String数组,下标,判断如果没有越界,则返回下标所在的值,如果越界返回空字符串“”
	private static String getString(String[] ss, int index) {
		if(index < 0){
			return "";
		}
		if (index > ss.length - 1) {
			return "";
		} else {
			return ss[index];
		}
	}
}



=============================
    太惭愧用如此低效的方法,就当是一种学习的过程,这里jdk提供了一种很高效的方法,java.math.BigInteger类提供了所有基本整数操作的对应运算,还额外提供了好多方法,具体可以参考jdk的官方文档!

经过测试,两个long的最大值相乘耗时竟然0毫秒,太高效了!
BigInteger b1 = new BigInteger("9223372036854775807");
BigInteger b3 = new BigInteger("9223372036854775807");
long st = System.currentTimeMillis();
BigInteger b4 = b1.multiply(b3);//相乘
System.out.println(" BigInteger b4 = "+b4.toString());
long et = System.currentTimeMillis();
System.out.println((et-st));
分享到:
评论

相关推荐

    vue中 数字相加为字串转化为数值的例子

    今天小编就为大家分享一篇vue中 数字相加为字串转化为数值的例子,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

    N!大数阶乘 阶乘得数过大

    当N!过大,无法用INT LONG __INT64 接收时,只能通过二维数组,将每一位数据截取出来。

    VBSCRIPT中文手册

    加法运算符 (+) 两数相加。 And 运算符 执行两个表达式的逻辑连接。 Array 函数 返回含一数组的 变体 。 Asc 函数 返回字符串首字母的 ANSI 字符代码。 赋值运算符 (=) 给变量或属性赋值。 Atn 函数 返回数的...

    vb Script参考文档

    加法运算符 (+) 两数相加。 And 运算符 执行两个表达式的逻辑连接。 Array 函数 返回含一数组的 变体 。 Asc 函数 返回字符串首字母的 ANSI 字符代码。 赋值运算符 (=) 给变量或属性赋值。 Atn 函数 返回数的...

    C语言程序设计标准教程

    下表列出了Turbo C中各类整型量所分配的内存字节数及数的表示范围。 类型说明符 数的范围 分配字节数 int -32768~32767 ■■ short int -32768~32767 ■■ signed int -32768~32767 ■■ unsigned int 0~65535 ■...

    2.Java基础语法.ppt

    byte,short,char 只要在表数范围中,不用强转可以直接进行赋值。但是要是超出范围,就必须要进行强转了 类型的优先级别:byte,short,char,int,long,float,double (低–高) 右&gt;左–强制类型转换 右&lt;左–自动转换 ...

    VBScript 语言参考中文手册CHM

    CLng 函数 返回已被转换为 Long 子类型的变体的表达式。 颜色常数 颜色常数列表。 比较常数 用于比较运算的常数列表。 连接运算符 (&) 强制两个表达式的字符串连接。 Const 语句 声明用于字母值的常数。 Cos ...

    VBScript 语言参考

    加法运算符 (+) 两数相加。 And 运算符 执行两个表达式的逻辑连接。 Array 函数 返回含一数组的 变体 。 Asc 函数 返回字符串首字母的 ANSI 字符代码。 赋值运算符 (=) 给变量或属性赋值。 Atn 函数 返回数的...

    VBSCRIP5 -ASP用法详解

    加法运算符 (+) 两数相加。 And 运算符 执行两个表达式的逻辑连接。 Array 函数 返回含一数组的 变体 。 Asc 函数 返回字符串首字母的 ANSI 字符代码。 赋值运算符 (=) 给变量或属性赋值。 Atn 函数 返回数的...

    JVM面试总结

    操作这些原始数据类型数据的字节码(指令)本身就已经指出了操作数的数据类型,例如iadd、ladd、fadd和dadd指令都是把两个数相加,其操作数类型分别是int、long、float和double。虚拟机没有给boolean(布尔)类型设置...

    整理后java开发全套达内学习笔记(含练习)

    注意:两个 byte 数相加,变 int 型) short 16bit, -2^15~2^15-1 (2^15=32768) int 32bit, -2^31~2^31-1 (2147483648,20亿,10位有效数字) long 64bit, -2^63~2^63-1 (900亿亿,20位有效数字) float 32bit, 9...

    高精度浮点数幂指运算

    对于高精度运算一般基本浮点型double、long double都满足不了精度要求,时需要用数组来存储数值,并用数组每一个元算参与运算,执行相乘和移位相加,达到高精度要求。

    C++出错提示英汉对照表

    Numeric constant too large -------------------数值常太大 Out of memory -------------------内存不够用 houjiuming Parameter ''xxx'' is never used ------------------能数xxx没有用到 Pointer required on...

    java经典面试2010集锦100题(不看你后悔)

    C) 双精度类型double比单精度类型float具有更高的精度和更大的表示范围,但float类型具有速度快、占用内存小的优点。 D) 在Java中布尔值可以用true或false来表示,但是同时也可以用1或0来表示。 题目5:b 程序...

    数据结构(C++)有关练习题

    内容及步骤: 1、 设计一个图的类,采用临接表法进行存储,该图每个结点的数据类型类模板的模板参数进行定义(注:需先设计一个结点类Node); 2、 为该类分别设计一个实现深度优先搜索和广度优先搜索的成员...

Global site tag (gtag.js) - Google Analytics