public static void main(String[] args){
System.out.println(0.4 + 0.8); // = 1.2 ?
System.out.println(2 - 1.1); // = 0.9 ?
System.out.println(0.2 * 3); // = 0.6 ?
System.out.println(1.2 / 3); // = 0.4 ?
}
也许你会天真的认为,第一行打印 1.2,第二行打印 0.9,第三行打印 0.6,第四行打印 0.4,因为依据多年的数学惯性逻辑输出结果与预期的一致是很理所当然的事情嘛!
但是当程序跑完之后,输出的结果与预期的大有出入,来看下后台打印的结果:
1.2000000000000002
0.8999999999999999
0.6000000000000001
0.39999999999999997
结果看到这样的结果,是不是很让人郁闷呢?当然了,这些数据是我故意挑的,并不是所有涉及浮点数的运算操作都会算出这样预期之外的结果,但是一件很明了的事情就是,
当操作涉及浮点数运算的时候,我们一定要谨防这样的事情发生。
上面代码中,加也好,减也好,乘也好,除也好,它们都是属于 double 级别的运算,那为什么会打印输出这样的结果呢?原因是,并不是所有的浮点数都能够被精确的表示成一个
double 类型值,有些浮点数值不能够被精确的表示成 double 类型值,因此它会被表示成与它最接近的 double 类型的值。所以很遗憾,0.4 + 0.8 ≠ 1.2 。……
怎么来解决这个问题呢?在 java 中提供了一个 BigDecimal 类来执行精确小数的计算,BigDecimal 类提供了以下操作:算术、标度操作、舍入、比较、哈希算法和格式转换。
BigDecimal 类提供的方法:
加法:add
减法:subtract
乘法:multiply
除法:divide
…… ……
BigDecimal 类提供的更多的方法请自行查看 API,下面用 BigDecimal 类改写上面的代码实现:
public static void main(String[] args){
System.out.println(new BigDecimal(0.4).add(new BigDecimal(0.8))); // = 1.2 ?
System.out.println(new BigDecimal(2).subtract(new BigDecimal(1.1))); // = 0.9 ?
System.out.println(new BigDecimal(0.2).multiply(new BigDecimal(3))); // = 0.6 ?
System.out.println(new BigDecimal(1.2).divide(new BigDecimal(3))); // = 0.4 ?
}
也许你正在查类 BigDecimal 的 API,API 上对类 BigDecimal 有一大串的文字说明,也许你还没来得及看完,但能够确定的是,类 BigDecimal 确实能够准确保证精确小数的执行,
那我上面代码的注释是不是忘记去掉了啊?不是。要是真这么干,那就大祸了。先来看一下后台的打印输出结果:
1.20000000000000006661338147750939242541790008544921875
0.899999999999999911182158029987476766109466552734375
0.600000000000000033306690738754696212708950042724609375
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
at java.math.BigDecimal.divide(BigDecimal.java:1603)
at example.BigDecimalApp.main(BigDecimalApp.java:20)
如果真这么玩了,你会看到结果更恶心了,这还不算,而且还抛了异常,这是为什么呢?别急,来看一下 API 上是怎么说的:
“
public BigDecimal(double val)
将 double 转换为 BigDecimal,后者是 double 的二进制浮点值准确的十进制表示形式。返回的 BigDecimal 的标度是使 (10scale × val) 为整数的最小值。
注:
1. 此构造方法的结果有一定的不可预知性。有人可能认为在 Java 中写入 new BigDecimal(0.1) 所创建的 BigDecimal 正好等于 0.1(非标度值 1,其标度为 1),但是它实际上等于
0.1000000000000000055511151231257827021181583404541015625。这是因为 0.1 无法准确地表示为 double (或者说对于该情况,不能表示为任何有限长度的二进制小数)
这样,传入 到构造方法的值不会正好等于 0.1(虽然表面上等于该值)。
2. 另一方面,String 构造方法是完全可预知的:写入 new BigDecimal("0.1") 将创建一个 BigDecimal,它正好 等于预期的 0.1。因此,比较而言,通常建议优先使用 String 构造方法
3. 当 double 必须用作 BigDecimal 的源时,请注意,此构造方法提供了一个准确转换;它不提供与以下操作相同的结果:先使用 Double.toString(double) 方法,
然后使用 BigDecimal(String) 构造方法,将 double 转换为 String。要获取该结果,请使用 static valueOf(double) 方法。
参数:
val - 要转换为 BigDecimal 的 double 值。
抛出:
NumberFormatException - 如果 val 为无穷大或 NaN。
”
以上文字摘自 API,API 上解释的很清楚了,这里就不多说了,API 建议优先使用 String 构造方法,那我们就来试一下呗:
public static void main(String[] args){
System.out.println(new BigDecimal("0.4").add(new BigDecimal("0.8"))); // = 1.2 √
System.out.println(new BigDecimal("2").subtract(new BigDecimal("1.1"))); // = 0.9 √
System.out.println(new BigDecimal("0.2").multiply(new BigDecimal("3"))); // = 0.6 √
System.out.println(new BigDecimal("1.2").divide(new BigDecimal("3"))); // = 0.4 √
}
后台打印输出结果:
1.2
0.9
0.6
0.4
OK,这下子终于不出篓子了,所以千万不能随随便便使用 BigDecimal(double) 构造器来创建 BigDecimal 对象,因为该构造器是根据它的参数的精确值来创建实例对象的,
该构造方法的结果还是有一定的不可预知性,用 BigDecimal(String) 此构造器来创建 BigDecimal 实例那就不会有问题了。
以上提到了类 BigDecimal 中的 add 、subtract 、multiply 、divide 方法,在 API 中,你可以看到,这几个方法都各自有自己的一个重载方法,如:
add (BigDecimal augend, MathContext mc) ……
第二个参数 mc 是什么意思呢?先来看一段代码:
public static void main(String[] args){
//计算结果保留两位有效数字
System.out.println(new BigDecimal(Math.PI + "").add(new BigDecimal("0.89842"),new MathContext(2))); //输出 4.0
}
第二个参数 mc 是用来保留计算结果的有效位数的,其他三个方法的重载用法是一样的,这里就不一 一列出来了。
//自定义加法运算
function
addNum (num1, num2) {
var
sq1,sq2,m;
try
{
sq1 = num1.toString().split(
"."
)[1].length;
}
catch
(e) {
sq1 = 0;
}
try
{
sq2 = num2.toString().split(
"."
)[1].length;
}
catch
(e) {
sq2 = 0;
}
m = Math.pow(10,Math.max(sq1, sq2));
return
(num1 * m + num2 * m) / m;
}
alert(addNum(0.1, 0.2));
相关推荐
在iOS开发中,进行精确的计算以及自定义小数位数是常见的需求,尤其是在处理价格、折扣等财务数据时。为了实现这样的功能,开发者需要掌握一些核心的编程技巧和概念。以下将详细介绍如何在iOS中进行精准的浮点数运算...
2. 小数位数(Input Integer):这个参数允许用户自定义保留的小数位数。 输出参数可能有: 1. 结果(Output Real):处理后的新浮点数,只保留了指定的小数位数。 2. 错误标志(Output Bool):表示截取过程中是否...
本文件内容主要讲述呢WB中如何将小数化数据转换成字符串
TRUNC_保留小数位TRUNC_保留小数位TRUNC_保留小数位TRUNC_保留小数位TRUNC_保留小数位TRUNC_保留小数位TRUNC_保留小数位TRUNC_保留小数位TRUNC_保留小数位TRUNC_保留小数位TRUNC_保留小数位TRUNC_保留小数位TRUNC_...
将得到的小数部分相加,得到十进制小数为0.625。 将整数部分和小数部分相结合,二进制小数1100.001转换为十进制小数为12.625。 此文档强调了转换过程中应注意的细节,例如确保二进制表示的精确度(可能需要根据...
Excel 中保留小数点小数为 0 时不显示 0 显示为整数的解决方法 在 Excel 中,当我们设置单元格保留小数时,如保留一位小数,输入整数时,后面也会跟上小数点,如 100.0、20.0,这时我们可以使用条件格式来解决这个...
JS小数加减乘除运算后小数点后产生多位数和计算精度损失的解决方案
项目中遇到小数相似度(业务给过来是小数)需要web界面以百分数的形式表示,并且保留两位小数。 直接上代码。 var nval = 0.12345; var percent = (Math.round(nval*10000))/100+'%'; 说明: 1.Math.round(num)函数...
4. **小数的加减法**:掌握小数加减的基本运算规则,包括对齐小数点,以及处理不同位数的小数相加减的情况。 5. **小数的乘法和除法**:理解小数乘除的计算方法,特别是小数点的位置变化规律。 6. **小数的近似值*...
在C#编程中,处理多位小数的精度问题是一个常见的挑战。这主要涉及到浮点数和定点数的数据类型,以及它们在数学运算中可能出现的精度损失。本文将深入探讨这个问题,并提供解决方案。 首先,让我们理解为何会出现...
为了满足更复杂的用户需求,开发者常常需要自定义RatingBar,以实现整数和小数的递增递减功能。 自定义RatingBar的过程主要涉及到以下几个关键知识点: 1. **自定义View**:在Android中,自定义UI组件通常需要继承...
8. **小数的加减运算**:小数的加减运算需要对齐数位,从右向左逐位相加或相减。如果哪一位相加满十,需要向前一位进一;如果哪一位不够减,需要从前一位退一(10倍)再减。 9. **实际应用问题**:课件中提供了实际...
然后,将三个月的用水量相加后除以3,得到的精确值是12.6吨,但题目要求保留一位小数,因此答案是12.6吨。 第四题是比例问题,询问鲸的体重是大象的多少倍。已知鲸的体重为145吨,大象的体重为4.3吨,通过简单的除...
不同于银行家算法,真正做到四舍五入,符合中国人的使用习惯,并且可自定义保留小数的位数。并且能够对参数值做错误判断。
十进制小数转化为十六进制小数类 /// /// 十进制整数转化为十六进制(带小数) /// /// <param name="value"></param> /// <returns></returns> public string DecToHex(double value) { string result =...
根据所提供的文件信息,文件名为“五年级数学上册三游三峡—小数除法3.4循环小数、有限小数、无限小数同步练习青岛版六三制.pdf”,这份文档的标题表明其为五年级数学上册的教学内容,具体到小数除法的特定章节,即...
在数字信号处理领域,FPGA(Field-Programmable Gate Array)因其可编程性和高速运算能力,被广泛用于实现各种算法,其中包括小数分频。小数分频是数字频率合成中的关键步骤,它允许系统生成非整数倍的输入频率,这...
然而,在处理浮点数(Real类型)数据时,TIA Portal的默认设置并不支持用户自定义小数位数,通常最多保留7位小数进行计算。 "西门子1200实现小数按位截取"这个主题主要关注如何在PLC程序中对Real类型的数值进行特定...
C语言实现小数转分数,包括带循环节的小数
易语言允许用户自定义转换的精度,以满足不同需求。 2. **内部处理**:易语言会将十进制小数先转换为二进制浮点数,然后将二进制浮点数转换为十六进制字符串。这个过程涉及到浮点数的二进制表示,即IEEE 754标准,...