Java中使用long类型实现精确的四则运算
2016-08-22 16:42
597 查看
引子
Effective Java 2nd Edition 第48条建议:如果需要精确的答案,请避免使用float和doble。float和double类型主要是为了科学计算和工程计算而设计的。他们执行二进制制浮点运算(binary floating-point arithmetic),这是为了在广泛的数值范围上提供较为精确的快速近似计算而精心设计的。然而,他们并没有提供完全精确的结果,所以不应该被用于需要精确结果的场合。float和double类型尤其不适合用于货币计算,因为要让float或double精确的表示0.1(或者10的任何其他负数次方值)是不可能的。解决这个问题的正确办法是使用BigDecimal、int或long进行货币计算。1. float和double为何是近似计算
float和double无法精确计算是由他们的设计决定的。详细信息请参考下面这两篇篇博文:java float double精度为什么会丢失?浅谈java的浮点数精度问题
Java中float/double取值范围与精度
对浮点数的IEEE 754表示法不熟悉的同学,可以参考下面的博文:
细说浮点数
IEEE 754 浮点数的表示精度探讨
2. 使用long实现精确计算
本例子实现了使用long类型来完成浮点数的精确的加减乘,由于除法本身很多是不能整除的,所以对除法的精确计算没有想到好的方法。由于long的表示范围为 -2^63 —– 2^63 - 1,即十进制的: -9223372036854775808 —–9223372036854775807
所以对于使用long完成精确计算必须保证计算结果及其中间结果不超过long的范围。对于比较大的数,使用BigDecimal靠谱。
上代码:
package effectivejava.chapter8; import java.math.BigDecimal; /** * * 本例子实现了使用long类型来完成浮点数的精确的加减乘,由于除法本身很多是不能整除的,所以对除法的精确计算没有想到好的方法。 * 由于long的表示范围为-2^63 ----- 2^63 - 1,即十进制的: -9223372036854775808-----9223372036854775807 * 所以对于使用long完成精确计算必须保证计算结果及其中间结果不超过long的范围。对于比较大的数,使用BigDecimal靠谱。 */ public class ExactComputationByLong { /** * 1. 获取两个小数的右移小数点之后的long值及移动位数; 2. 统一移动位数为两个之中大的那个; 3. 相加; 4. 将小数点左移到相加的数中。 */ public static String add(String strA, String strB) { ExactLong exactA = new ExactLong(strA); ExactLong exactB = new ExactLong(strB); int maxScale = getMaxScale(exactA, exactB); changeToMaxSacle(exactA, maxScale); changeToMaxSacle(exactB, maxScale); long addNum = exactA.getData() + exactB.getData(); String resultInit = getDataFromLong(addNum, maxScale); return getResultRemoveLastZeros(resultInit); } /** * 过程与加法相同。 */ public static String subtract(String strA, String strB) { ExactLong exactA = new ExactLong(strA); ExactLong exactB = new ExactLong(strB); int maxScale = getMaxScale(exactA, exactB); changeToMaxSacle(exactA, maxScale); changeToMaxSacle(exactB, maxScale); long subNum = exactA.getData() - exactB.getData(); String resultInit = getDataFromLong(subNum, maxScale); return getResultRemoveLastZeros(resultInit); } /** * 过程与加法相似,唯一不同是移动位数为maxScale * 2。 */ public static String multiply(String strA, String strB) { ExactLong exactA = new ExactLong(strA); ExactLong exactB = new ExactLong(strB); if (exactA.getData() == 0 || exactB.getData() == 0) { return "0"; } int maxScale = getMaxScale(exactA, exactB); changeToMaxSacle(exactA, maxScale); changeToMaxSacle(exactB, maxScale); long subNum = exactA.getData() * exactB.getData(); String resultInit = getDataFromLong(subNum, maxScale * 2); return getResultRemoveLastZeros(resultInit); } /** * 对于除法,我没有想到好的方法来精确计算。而且除法本身有很多不是整除的,所以除法一般都要确定一个精度,然后再计算。水平有限,这里就用BigDecimal的一个实现代替吧☺ */ public static String divide(String strA, String strB) { return new BigDecimal(strA).divide(new BigDecimal(strB), BigDecimal.ROUND_DOWN).toPlainString(); } private static void changeToMaxSacle(ExactLong exactData, int maxScale) { if (maxScale != exactData.getScale()) { exactData.setData((long) (exactData.getData() * Math.pow(10, maxScale - exactData.getScale()))); exactData.setScale(maxScale); } } private static int getMaxScale(ExactLong exactA, ExactLong exactB) { int maxScale = exactA.getScale() > exactB.getScale()?exactA.getScale():exactB.getScale(); return maxScale; } /** * 将dataL小数点左移scale位 */ private static String getDataFromLong(long dataL, int scale) { String dataStr = dataL + ""; if (scale < 0) { throw new IllegalArgumentException("The degree must greater than 0!!"); } if (scale == 0) { return dataStr; } if (dataStr.length() > scale) { return dataStr.substring(0, dataStr.length() - scale) + "." + dataStr.substring(dataStr.length() - scale); } else { StringBuilder dataSB = new StringBuilder("0."); for (int i = 0;i < scale - dataStr.length();i++) { dataSB.append("0"); } dataSB.append(dataStr); return dataSB.toString(); } } /** * 去掉小数点之后多余的0 */ private static String getResultRemoveLastZeros(String resultInit) { int index = resultInit.length(); // 去处小数位最后可能的多个0 for (int i = resultInit.length() - 1;i >= 0;i--) { if (resultInit.charAt(i) != '0') { index = i + 1; break; } } return resultInit.substring(0, index); } public static void main(String[] args) { String strA = "0.001234560"; String strB = "10.2345"; String strAdd = add(strA, strB); System.out.println("Add:\n" + strA + "\n" + strB + "\n" + strAdd); String strSub = subtract(strA, strB); System.out.println("Sub:\n" + strA + "\n" + strB + "\n" + strSub); String strMult = multiply(strA, strB); System.out.println("Mult:\n" + strA + "\n" + strB + "\n" + strMult); String sttDiv = divide(strA, strB); System.out.println("Div:\n" + strA + "\n" + strB + "\n" + sttDiv); } } /** * 将一个字符串表示的浮点数用long表示,scale表示小数点右移的位数。 */ class ExactLong { private long data; private int scale; public ExactLong(String dataStr) { if (dataStr.indexOf(".") == -1) {// 如123456 data = Long.parseLong(dataStr); scale = 0; return; } if (dataStr.indexOf(".") != dataStr.lastIndexOf(".")) {// 如123.456.7 throw new IllegalArgumentException(dataStr + " can not cast to long!"); } else {// 如12345.678 scale = dataStr.length() - dataStr.indexOf(".") - 1; long beforePoint = Long.parseLong(dataStr.substring(0, dataStr.indexOf(".")));// 小数点之前的数 long afterPoint = Long.parseLong(dataStr.substring(dataStr.indexOf(".") + 1, dataStr.length()));// 小数点之后的数 data = (long) (beforePoint * Math.pow(10, scale) + afterPoint); } } public long getData() { return data; } public void setData(long data) { this.data = data; } public int getScale() { return scale; } public void setScale(int scale) { this.scale = scale; } }
相关文章推荐
- Java中使用long类型实现精确的四则运算
- Java 里使用 long 类型的数据一定要在数值后面加上 “L”
- (旋转数组问题)给定一个整数类型的循环有序数组,求循环数组的特定值,使用二分查找法(JAVA实现)
- Java编程long数据类型的使用问题
- 2.1、Spring Web MVC是什么 Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职
- java中的方法返回值使用泛型,实现灵活的返回值类型
- Java 使用BigDecimal实现精确计算
- java使用栈和逆波兰表达式实现四则运算
- JAVAWEB开发之Struts2详解(二)——Action接受请求参数、类型转换器、使用Struts2的输入校验、以及遵守约定规则实现Struts2的零配置
- Java中使用Bigdecimal类型进行精确商业计算
- java中使用BigDecimal类来实现浮点数的精确运算
- [Java] 不使用第三个变量实现两个整数类型变量互换(异或运算符)
- JAVA中实现double类型精确计算
- Java内置类型的Autoboxing实现机制和Integer的CacheInteger的使用
- Java-实现两个int类型变量值的交换,要求不使用中间变量
- Java中使用BigDecimal实现精确舍五入及工程运算
- Java 基本类型 long 的一些使用陷阱
- JAVA上加密算法的实现用例MD5/SHA1,DSA,DESede/DES,Diffie-Hellman的使用
- 使用JAVA中的动态代理实现数据库连接池