`
truemylife
  • 浏览: 230623 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
文章分类
社区版块
存档分类
最新评论
阅读更多

为什么关注数字精度?

  在大部分场景,我们默认整数或者保留两位小数位,分别对应IntDouble,而没有进一步去了解其精度,因为大部分应用,这样的精度和数据类型是足够应付的,但是在某些科学计数及特殊的商业范畴,可能需要更高精度的数字表达,这就要进一步了解数字的精度。大部分语言都提供两种基本精度类型,一种是float、一种是double ;实际上如果小数位如果是固定的,floatdouble可以想办法转成用intlong来表示,显示给用户的时候再按小数位整除,比如金额一般要求到分就可以了,那我们可以把数据设置成long,显示的时候再除100。无论如何,如果涉及大数字、高精度的运算,可能还是饶不开使用更高精度要求的数据处理,比如:

bigdata*(smalldata*smalldata),可能理想的结果是一个正常值,但因为精度丢失原因,两个smalldata*smalldata可能会变成0,而让整个计算结果相差太大,当然这种场景可能在特定的行业、物理等方面可能会碰到。

 

精度丢失问题

来一段java代码:

 

float a=1f
float b=3f
System.out.println(a/b)
System.out.println(0.33333331f)

结果是

0.33333334
0.3333333
    再来一段网上提供的案例:
public class FloatDoubleTest {
  public static void main(String[] args) {    
    float f = 20014999;    
    double d = f;    
    double d2 = 20014999;    
    System.out.println("f=" + f);    
    System.out.println("d=" + d);    
    System.out.println("d2=" + d2);    
    }    
}
输出结果:
f=2.0015E7
d=2.0015E7
d2=2.0014999E7
 

 

       

       这两个案例都发现单精度float出现了精度丢失问题,而且丢失后的数据也没有按理想中的比如四舍五入给存保留最后一位。我们必须理解精度有数据范围的一面,但更重要的是其精度范围的一面。

Java的单精度和双精度都遵循IEEE 754标准,可进一步去了维基百科大致了解下IEEE 754标准http://zh.wikipedia.org/wiki/IEEE_754

从标准来看,可总结出单双精度和数据范围与精度范围。

1. 数据范围

  floatdouble的范围是由指数的位数来决定的。

  float的指数位有8位,而double的指数位有11位,分布如下:

  float

  1bit(符号位) 8bits(指数位) 23bits(尾数位)

  double

  1bit(符号位) 11bits(指数位) 52bits(尾数位)

  因此,float的指数范围为-127~+128,而double的指数范围为-1023~+1024,并且指数位是按补码的形式来划分的。

  其中负指数决定了浮点数所能表达的绝对值最小的非零数;而正指数决定了浮点数所能表达的绝对值最大的数,也即决定了浮点数的取值范围。

  float的范围为-2^128 ~ +2^128,也即-3.40E+38 ~ +3.40E+38double的范围为-2^1024 ~ +2^1024,也即-1.79E+308 ~ +1.79E+308

2.  数据精度

  floatdouble的精度是由尾数的位数来决定的。浮点数在内存中是按科学计数法来存储的,其整数部分始终是一个隐含着的“1”,由于它是不变的,故不能对精度造成影响。

  float2^23 = 8388608,一共七位,这意味着最多能有7位有效数字,但绝对能保证的为6位,也即float的精度为6~7位有效数字;

  double2^52 = 4503599627370496,一共16位,同理,double的精度为15~16

 

3.范围与精度制约关系

  从上面我们知道了单双精度floatdouble各自的数据范围与精度,以float为例,如果我们控制6-7位的精度在小数点的位置上,会觉的这个是正常的应用场所。但不幸的是,实际上精度只控制高位数6-7位的精度,也就是说如果你的数字整数部分就是个大整数,那就先控制好整数部分的精度,如果小数部分超过6-7位,整数部分的精度也会丢失,更别说小数部分了。举个极端的例子,如下代码:

 

System.out.println(Float.MAX_VALUE);
System.out.println(Float.MAX_VALUE-3000000f);
  

输出结果:

 

3.4028235E38
3.4028235E38
  

   两者是相等的,原因大家应该都明白了,39位整数,300万的精度差已丢失,我想现在回头再来看一开始的两段代码,问题和原因也都明白了。

       这里还有一点,就是丢失精度的时候,并非按四舍五入等更合理的算法,是因为这里的元位是二进制,规则是如果超过尾数位就会直接丢失。可以参考这篇博文:http://singleant.iteye.com/blog/713890

 

JAVABigDecimal

Java引入另外一种大数字类型BigDecimal,通过设置setScale可以设置精度控制,与floatdouble不同,这里的精度专指小数部分的精度。

BigDecimal有多种构造方法,与精度相关最就要的三个构造方法:

BigDecimal(float param)

BigDecimal(double param)

BigDecimal(String param)

不同的参数,在构造的时候会形成默认的精度,比如float,其能控制的精度6-7位,如果参数整数部分超过7位,这时候BigDecimal构造出来的值直接就去掉了小数部分,scale也自然变0,而如果数字较小,构造出来的方法因为其很长的小数位,这时scale也变的很长。double类型的参数与float构造相似,因此看起来用doublefloat构造出来的方法的精度是不确定的,一般情况下采用String构造,比如

new BigDecimal(“111.111001”)

scale:6,小数后6

new BigDecimal(“111.11100”)

new BigDecimal(“111.111”)

scale:3,多余的0不算。

可以再次设置setScale来提高精度,比如

BigDecimal a new BigDecimal(“111.111”)

             a.setScale(6);//提高到6位精度

但不能降低精度,比如设成a.setScale(2),运行时会有异常,因为降低精度必须采用一种近似取值的策略,因此如果降低精度的话,BigDecimal提供了另外一个参数,a.setScale(int newScale,int roundingMode)

另外BigDecimal提供了一些计算的方法,还有一个专门用来比较两数是否相等的方法compareTo

从功能上来说BigDecimal完全可以替代floatdouble来做运算使用,并且能提供精度更大的数据运算。而且BigDecimal的精度专指小数部分,对于入门者来说更容易理解。

遗憾的是BigDecimal在大数据量运算时,效率上会不如直接使用floatdouble;这是从网上引用的测试案例和测试数据:

 网友写道
import java.math.BigDecimal; 

public class BigDecimalEfficiency { 

public static int REPEAT_TIMES = 1000000; 

public static double computeByBigDecimal(double a, double b) { 
BigDecimal result = BigDecimal.valueOf(0); 
BigDecimal decimalA = BigDecimal.valueOf(a); 
BigDecimal decimalB = BigDecimal.valueOf(b); 
for (int i = 0; i < REPEAT_TIMES; i++) { 
result = result.add(decimalA.multiply(decimalB)); 

return result.doubleValue(); 


public static double computeByDouble(double a, double b) { 
double result = 0; 
for (int i = 0; i < REPEAT_TIMES; i++) { 
result += a * b; 

return result; 


public static void main(String[] args) { 
long test = System.nanoTime(); 
long start1 = System.nanoTime(); 
double result1 = computeByBigDecimal(0.120000000034, 11.22); 
long end1 = System.nanoTime(); 
long start2 = System.nanoTime(); 
double result2 = computeByDouble(0.120000000034, 11.22); 
long end2 = System.nanoTime(); 

long timeUsed1 = (end1 - start1); 
long timeUsed2 = (end2 - start2); 
System.out.println("result by BigDecimal:" + result1); 
System.out.println("time used:" + timeUsed1); 
System.out.println("result by Double:" + result2); 
System.out.println("time used:" + timeUsed2); 

System.out.println("timeUsed1/timeUsed2=" + timeUsed1 / timeUsed2); 

输出结果:

result by BigDecimal:1346400.00038148
time used:365847335
result by Double:1346400.000387465
time used:5361855
timeUsed1/timeUsed2=68

相差68倍,不在一个数量级上的差距。因此BigDecimal虽然好用,除非你的需求超过了double的精度,否则还是少用BigDecimal

 

选择思路

  首先java本身不是执行高效率的语言,因此如果集中于数据应算的话,可以考虑比如CFortran等语言是否更加合适。

    如已经不能脱离java,那就要好好分析实际需求,一般情况下double的精度是足够一般商业业务应用的。通常情况下double15-16位,12位整数+(2-3)小数这样的精度是足够的,但也有例外,比如一般发票的单据,金额一般就是小数点后2位,我们整数给他12位,而商品单价有时候会要求很长的小数位,那如果考虑到商品单价也有可能很高,这时候double就可能存在精度丢失问题,这种情况下再选择BigDecimal。总之选择的时候要兼顾实际需求和执行效率。


 

分享到:
评论

相关推荐

    数据采集卡的选型指南

    数据采集系统由信号源、信号调理部分、数据采集板卡、计算机及其软件构成,其中数据采集卡扮演着承上启下的关键角色,其性能直接决定了系统的整体效能。 ### 重要选型考量因素 #### 明确应用需求 在选型前,首先...

    高速数据采集卡选型时的一些关键判别要素

    ### 高速数据采集卡选型的关键判别要素 在高速数据采集卡的选型过程中,考虑到其在工业、科研及各种应用场景中的重要性,合理选择一款满足需求的产品至关重要。以下将详细介绍几个关键判别要素及其重要性: #### 1...

    加速度传感器的原理及其选型方法

    在实际应用中,加速度数据常常需要转化为速度和位移信息,这涉及对传感器输出的加速度信号进行积分。交流响应传感器在积分过程中可能出现问题,尤其是在处理宽脉冲或缓慢变化信号时,由于其固有的RC时间常数限制,...

    机器视觉 系统选型

    3. **数据处理单元**:包含软件及其运行所需的硬件环境,负责对图像数据进行分析和处理。 4. **结果输出**:将处理后的信息以特定的形式输出,如显示屏显示、控制系统反馈等。 #### 三、成像单元的重要性 成像单元...

    高速光耦选型

    - **PC817AC系列**:该系列光耦具有较高的精度和稳定性,常用于精密测量和控制电路中。 - **TLP系列(TLP521、TLP621等)**:这类光耦具备更高的耐压值和更宽的工作温度范围,适用于高压和恶劣环境下的信号隔离。 - ...

    JFVNY晶体、晶振中文选型手册

    在《JFVNY晶体、晶振中文选型手册》中,详细介绍了多种晶体谐振器类型及其特性,为工程师提供全面的选型指南。晶体谐振器的选型需考虑频率稳定性、工作温度范围、尺寸与封装形式等因素。 #### 二、晶体谐振器选型...

    安川伺服 用户手册 伺服的选型与数据表

    安川电机(YASKAWA)发布的《安川伺服 用户手册 伺服的选型与数据表》是针对Σ-Ⅱ系列SGM*H/SGDM型伺服驱动器及其配套伺服电机和伺服单元的技术文档。该手册主要面向需要对Σ-Ⅱ系列伺服驱动器进行选型、安装和使用...

    FANUC 0i系列系统选型手册-驱动分册

    **FANUC 0i系列系统选型手册**是针对FANUC 0i系列数控系统的驱动及其相关部件的选型和参数查询指南。此手册特别适用于FANUC 0i-F系列数控系统的用户,为用户提供了一套完整的驱动选型方案。 #### 二、伺服电机选型 ...

    新宝减速机 选型册.pdf

    本文将依据《新宝减速机选型册》中的内容,详细解析新宝减速机的主要特点、型号分类及其适用范围,旨在为用户提供更加全面、深入的产品认知。 #### 二、新宝减速机概述 新宝减速机是NIDEC-SHIMPO公司旗下的一款高...

    温湿度传感器选型和应用技巧

    本文将详细介绍温湿度传感器的选型原则及其在实际应用中的技巧。 #### 二、温湿度传感器选型要点 1. **选择测量范围** - 在选购温湿度传感器时,首先需要明确实际应用中所需的测量范围。除了一些特殊需求(如...

    丹佛斯 电机选型软件3.0版.zip

    在实际工程应用中,丹佛斯电机选型软件3.0可以帮助设计人员避免因手动计算带来的错误,提高选型精度。同时,通过比较不同电机的能效,可以推动企业实施更绿色的生产策略,符合全球范围内的环保法规要求。 总结来说...

    施耐德接触器选型手册

    施耐德电气的接触器选型手册详细介绍了TeSys®E系列接触器及其选型方法、特性、模块与附件等内容。TeSys®E系列是专为电机启动与保护设计的高性能接触器,适用于各种控制系统。本手册旨在帮助工程师和技术人员根据...

    AB PLC 1756选型手册

    本文将基于“AB PLC 1756选型手册”的标题、描述、标签及部分内容,详细解析ControlLogix 1756系列的特点、配置选项及其适用场景,帮助读者更好地理解该系列产品的优势并进行合理选型。 #### 二、ControlLogix 1756...

    fx综合选型样本

    本文将详细介绍三菱FX系列PLC中的FX1N、FX2N和FX3U三个子系列的选型指南,旨在帮助工程师和技术人员根据实际项目需求选择最合适的PLC型号及其扩展模块。 #### 二、FX系列PLC概述 三菱FX系列PLC具有体积小、功能...

    8051兼容的单片机及其应用选型

    Analog Devices的ADuC系列,如ADuC812、ADuC814、ADuC816和ADuC824,提供了高精度的ADC和DAC,以及额外的模拟功能,如温度传感器和锁相环,适合于需要复杂信号处理和高精度测量的场合。 这些8051兼容的单片机各有...

    完整德州仪器高性能模拟器件在大学生创新设计中的应用及选型指南.pdf

    在探讨德州仪器(Texas Instruments,简称TI)高性能模拟器件在大学生创新设计中的应用及其选型指南之前,有必要先理解TI在半导体行业的领先地位,特别是在模拟产品领域的卓越表现。TI作为全球领先的半导体产品供应...

Global site tag (gtag.js) - Google Analytics