如果要求精确的答案,请避免使用float和double

ESP32+MAX7219二进制时钟设计与嵌入式时间映射实践 二进制时钟是嵌入式系统中数据表示与物理呈现深度耦合的典型范例,其核心在于将时间语义(年月日时分秒)解构为比特位,并通过LED点阵的空间权重布局实现直观识别。该设计依托RTC模块提供时间基准,依赖SPI协议完成微控制器与专用驱动芯片(如MAX7219)的可靠通信,涉及寄存器级配置、位图坐标变换、软硬件时序对齐等关键原理。技术价值体现在低资源占用下的高可读性人机交互,适用于教学演示、极客项目及嵌入式UI原型开发。本文聚焦ESP32平台在MicroPython环境下,结合MAX7219驱动8×8点阵实现高可靠性二 阅读详情

1. 概述

float和double类型的主要设计目的是为了科学计算和工程计算。它们执行二进制浮点运算,这是为了在广域数值范围上提供较为精确的快速近似计算而精心设计的。然而,它们没有提供完全精确的结果,所以不应该被用于要求精确结果的场合。float和double类型对于货币计算尤为不合适,因为要让一个float或者double精确地表示0.1(或者10的任何负数次方值)是不可能的,比如System.out.println(2.0-1.1)将会打印0.899999999999999,而不是你所希望的0.9,这种舍入错误产生的原因是浮点数实际上是用二进制系统实现的,而分数1/10在二进制系统中没有精确的表示,其道理就如同在十进制系统中无法精确表示1/3一样;再比如0.5在二进制系统中有精确表示,而0.55在二进制系统中没有精确表示。

许多数都是无法在有限的n内完全精确的表示出来的,我们只能利用更大的n值来更精确的表示这个数,但更大的n也只是更加逼近精确值而无法得到精确值。例如

  1. public static void main(String[] args) {
  2.     System.out.println("100.5 - 100                 = " + (100.5 - 100));
  3.     System.out.println("100.5F - 100F               = " + (100.5F - 100F));
  4.     System.out.println("100.55 - 100.00             = " + (100.55 - 100.00));
  5.     System.out.println("100.55F - 100.00F           = " + (100.55F - 100.00F));
  6.     System.out.println("new Double(100.55 - 100.00) = " + new Double(100.55 - 100.00));
  7.     System.out.println("new Float(100.55 - 100.00)  = " + new Float(100.55 - 100.00));
  8.     System.out.println("(double)(100.55 - 100.00)   = " + (double)(100.55 - 100.00));
  9.     System.out.println("(float)(100.55 - 100.00)    = " + (float)(100.55 - 100.00));
  10.     
  11.     System.out.println("2.0 - 1.1               = " + (2.0 - 1.1));
  12.     System.out.println("2.0F - 1.1F             = " + (2.0F - 1.1F));
  13.     System.out.println("new Double(2.0 - 1.1)   = " + new Double(2.0 - 1.1));
  14.     System.out.println("new Float(2.0 - 1.1)    = " + new Float(2.0 - 1.1));
  15.     System.out.println("(double)(2.0 - 1.1)     = " + (double)(2.0 - 1.1));
  16.     System.out.println("(float)(2.0 - 1.1)      = " + (float)(2.0 - 1.1));
  17.     System.out.println("1.0 - 0.9               = " + (1.0 - 0.9));
  18.     System.out.println("1.0F - 0.9F             = " + (1.0F - 0.9F));
  19.     System.out.println("new Double(1.0 - 0.9)   = " + new Double(1.0 - 0.9));
  20.     System.out.println("new Float(1.0 - 0.9)    = " + new Float(1.0 - 0.9));
  21.     System.out.println("(double)(1.0 - 0.9)     = " + (double)(1.0 - 0.9));
  22.     System.out.println("(float)(1.0 - 0.9)      = " + (float)(1.0 - 0.9));
  23.     /*
  24.      * 输出结果:
  25.      * 100.5 - 100                 = 0.5
  26.      * 100.5F - 100F               = 0.5
  27.      * 100.55 - 100.00             = 0.5499999999999972
  28.      * 100.55F - 100.00F           = 0.55000305
  29.      * new Double(100.55 - 100.00) = 0.5499999999999972
  30.      * new Float(100.55 - 100.00)  = 0.55 (将一个Double转换为Float自然会有精度丢失, 所以舍入为0.55)
  31.      * (double)(100.55 - 100.00)   = 0.5499999999999972
  32.      * (float)(100.55 - 100.00)    = 0.55 (将一个double转换为float自然会有精度丢失, 所以舍入为0.55)
  33.      * 
  34.      * 2.0 - 1.1                = 0.8999999999999999
  35.      * 2.0F - 1.1F              = 0.9 (这里用Float可以得到正确值, 我不知道原因, 你去问cpu吧. 只要记住这是一个巧合就可以了)
  36.      * new Double(2.0 - 1.1)    = 0.8999999999999999
  37.      * new Float(2.0 - 1.1)     = 0.9 (将一个Double转换为Float自然会有精度丢失, 所以舍入为0.9)
  38.      * (double)(2.0 - 1.1)      = 0.8999999999999999
  39.      * (float)(2.0 - 1.1)       = 0.9 (将一个double转换为float自然会有精度丢失, 所以舍入为0.9)
  40.      * 
  41.      * 1.0 - 0.9                = 0.09999999999999998
  42.      * 1.0F - 0.9F              = 0.100000024
  43.      * new Double(1.0 - 0.9)    = 0.09999999999999998
  44.      * new Float(1.0 - 0.9)     = 0.1 (将一个Double转换为Float自然会有精度丢失, 所以舍入为0.1)
  45.      * (double)(1.0 - 0.9)      = 0.09999999999999998
  46.      * (float)(1.0 - 0.9)       = 0.1 (将一个double转换为float自然会有精度丢失, 所以舍入为0.1)
  47.      * */
  48. }

2. 为什么浮点数会丢失精度(浮点数没办法用十进制来精确表示)

(1)十进制与二进制转换

要归咎于CPU表示浮点数的方法:2.4的二进制表示并非就是精确的2.4,而是最为接近2.3999999999999999;原因在于浮点数由两部分组成:指数和尾数,浮点数的值实际上是由cpu的某个数学公式计算得到的,所以您所遇到的精度损失会在任何操作系统和编程环境中遇到。

(2)类型失配

您可能混合了浮点数和双精度浮点数类型。请确定您在进行数学运算的时候所有的数据类型全部相同。注意:float类型的变量只有7位的精度,而double类型的变量有15位的精度。

3. 解决方法

(1)java.lang.Math中的public static int round(float a)和public static long round(double a)不能解决问题; java.lang.Object --> java.text.Format --> java.text.NumberFormat --> java.text.DecimalFormat 也不能解决问题
(2)使用BigDecimal、int或long(将小数转换为整数再进行计算,例如:将以美元为单位的货币计算改为以美分为单位的货币计算)。java.lang.Object --> java.lang.Number -- Direct Known Subclasses: BigDecimal, Byte, Double, Float, Integer, Long, Short

BigDecimal常用构造方法:

public BigDecimal(double val) --> Translates a double into a BigDecimal which is the exact decimal representation of the double's binary floating-point value.

public BigDecimal(String val) --> Translates the string representation of a BigDecimal into a BigDecimal

通常情况下,上面的那一个使用起来要方便一些。我们可能想都不想就用上了,会有什么问题呢?等到出了问题的时候,才发现上面哪个够造方法的详细说明中有这么一段。

The results of this constructor can be somewhat unpredictable. One might assume that writing new BigDecimal(0.1) in Java creates a BigDecimal which is exactly equal to 0.1 (an unscaled value of 1, with a scale of 1), but it is actually equal to 0.1000000000000000055511151231257827021181583404541015625. This is because 0.1 cannot be represented exactly as a double (or, for that matter, as a binary fraction of any finite length). Thus, the value that is being passed in to the constructor is not exactly equal to 0.1, appearances notwithstanding.

The String constructor, on the other hand, is perfectly predictable: writing new BigDecimal("0.1") creates a BigDecimal which is exactly equal to 0.1, as one would expect. Therefore, it is generally recommended that the String constructor be used in preference to this one.

When a double must be used as a source for a BigDecimal, note that this constructor provides an exact conversion; it does not give the same result as converting the double to a String using the Double.toString(double) method and then using the BigDecimal(String) constructor. To get that result, use the static valueOf(double) method.

原来我们如果需要精确计算,非要用String来够造BigDecimal不可!在《Effective Java》一书中的例子也是用String来够造BigDecimal的,但是书上却没有强调这一点,这也许是一个小小的失误吧。

使用BigDecimal加法运算步骤:(1)将两个浮点数转为String,然后构造成BigDecimal;(2)在其中一个上调用add()方法,传入另一个作为参数,然后把运算的结果(BigDecimal)再转换为浮点数。这样比较繁琐,我们可以自己写一个工具类Arith来简化操作:

  1. package edu.hust.test;
  2. import java.math.BigDecimal;
  3. /**
  4.  * 由于Java的简单类型不能够精确的对浮点数进行运算,这个工具类提供精确的浮点数运算,包括加减乘除和四舍五入。
  5.  * 所以提供以下静态方法:
  6.  * public static double add(double v1,double v2)
  7.  * public static double sub(double v1,double v2)
  8.  * public static double mul(double v1,double v2)
  9.  * public static double div(double v1,double v2)
  10.  * public static double div(double v1,double v2,int scale)
  11.  * public static double round(double v,int scale)
  12.  * 
  13.  */
  14. public class Arith {
  15.     //默认除法运算精度
  16.     private static final int DEF_DIV_SCALE = 10;
  17.     //这个类不能实例化
  18.     private Arith(){}
  19.     /**
  20.      * 提供精确的加法运算。
  21.      * @param v1 被加数
  22.      * @param v2 加数
  23.      * @return 两个参数的和
  24.      */
  25.     public static double add(double v1,double v2){
  26.         BigDecimal b1 = new BigDecimal(Double.toString(v1));
  27.         BigDecimal b2 = new BigDecimal(Double.toString(v2));
  28.         return b1.add(b2).doubleValue();
  29.     }
  30.     /**
  31.      * 提供精确的减法运算。
  32.      * @param v1 被减数
  33.      * @param v2 减数
  34.      * @return 两个参数的差
  35.      */
  36.     public static double sub(double v1,double v2){
  37.         BigDecimal b1 = new BigDecimal(Double.toString(v1));
  38.         BigDecimal b2 = new BigDecimal(Double.toString(v2));
  39.         return b1.subtract(b2).doubleValue();
  40.     } 
  41.     /**
  42.      * 提供精确的乘法运算。
  43.      * @param v1 被乘数
  44.      * @param v2 乘数
  45.      * @return 两个参数的积
  46.      */
  47.     public static double mul(double v1,double v2){
  48.         BigDecimal b1 = new BigDecimal(Double.toString(v1));
  49.         BigDecimal b2 = new BigDecimal(Double.toString(v2));
  50.         return b1.multiply(b2).doubleValue();
  51.     }
  52.     /**
  53.      * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到小数点以后10位,以后的数字四舍五入。
  54.      * @param v1 被除数
  55.      * @param v2 除数
  56.      * @return 两个参数的商
  57.      */
  58.     public static double div(double v1,double v2){
  59.         return div(v1,v2,DEF_DIV_SCALE);
  60.     }
  61.     /**
  62.      * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指定精度,以后的数字四舍五入。
  63.      * @param v1 被除数
  64.      * @param v2 除数
  65.      * @param scale 表示表示需要精确到小数点以后几位。
  66.      * @return 两个参数的商
  67.      */
  68.     public static double div(double v1,double v2,int scale){
  69.         if(scale<0){
  70.             throw new IllegalArgumentException("The scale must be a positive integer or zero");
  71.         }
  72.         BigDecimal b1 = new BigDecimal(Double.toString(v1));
  73.         BigDecimal b2 = new BigDecimal(Double.toString(v2));
  74.         return b1.divide(b2,scale,BigDecimal.ROUND_HALF_UP).doubleValue();
  75.     }
  76.     /**
  77.      * 提供精确的小数位四舍五入处理。
  78.      * @param v 需要四舍五入的数字
  79.      * @param scale 小数点后保留几位
  80.      * @return 四舍五入后的结果
  81.      */
  82.     public static double round(double v,int scale){
  83.         if(scale<0){
  84.             throw new IllegalArgumentException("The scale must be a positive integer or zero");
  85.         }
  86.         BigDecimal b = new BigDecimal(Double.toString(v));
  87.         BigDecimal one = new BigDecimal("1");
  88.         return b.divide(one,scale,BigDecimal.ROUND_HALF_UP).doubleValue();
  89.     }
  90. }

使用BigDecimal缺点:要创建对象并占用内存;其floatValue()方法有时会精度丢失而造成计算错误(所以一般小数计算尽量使用double)。

最后补充一点:也正因为此,浮点数不能用于循环判断,否则容易造成死循环。

华为云计算——FusionCompute创建虚拟机 阅读详情

相关推荐

gesp c++ 五级知识点

gesp c++ 五级知识点

MMD.然大大 2009

为什么浮点数不能精确表示?

我们知道,在编写程序时,两个浮点数(float或double)不能直接进行大小比较。 当然,我们也都知道,不能直接进行大小比较的原因是因为浮点数在计算机内部不能精确的表示。可是,为什么在计算机内部浮点数不能够精确地表示呢?这还得从IEEE 754标准说起。

1万+

BK3437蓝牙5.2芯片实战:手把手教你设计超低功耗智能家居遥控器(附原理图)

本文详细介绍了BK3437蓝牙5.2芯片在超低功耗智能家居遥控器设计中的应用。通过分析芯片的核心优势、硬件设计要点和低功耗固件策略,手把手教你实现日均功耗低于5μA的遥控器原型,并附有原理图设计详解和常见问题解决方案。

tgb34567890的博客 620

精度计算

doulbe操作 double i = 2.00 - 1.10 结果:0.899999999999999 问题:并不是所有的小数都可以用二进制浮点数精确表示。 System.out.printf(" %.2f%n " 2.00-1.10) 浮点数不适合货币运算,因为不能产生精确的结果。 精度:表示一个有限长度的二进制小数。 解决问题: 1.使用整数类型,并以某为单位进行计算。 (200 - 110) 把2.00和1.10都扩大为分; 2.使用BigDecimal 但要是有B...

1659

RK3588交叉编译实战:从环境搭建到程序部署

本文详细介绍了在X86主机上为RK3588开发板搭建交叉编译环境的完整流程。从获取ARM工具链、配置sysroot,到实战编译Hello World和Qt应用程序,并提供了部署方法和常见问题解决方案。通过交叉编译,开发者可以在高性能PC上高效开发,并在ARM架构的RK3588上运行程序,大幅提升嵌入式开发效率。

weixin_29191669的博客 274

ISP Tuning实战指南:从基础到高级的色彩与亮度优化

本文详细介绍了ISP Tuning的基础概念与高级调优技术,包括黑电平校正(BLC)、自动白平衡(AWB)、色彩校正矩阵(CCM)和Gamma曲线调整等核心算法。通过实战案例和调试技巧,帮助工程师提升摄像头成像质量,解决高光过曝、暗部噪点等常见问题,适用于智能门铃、车载摄像头等多种场景。

weixin_30879169的博客 108

matlab报错随笔

#报错信息# 错误使用 reshape 要执行 RESHAPE,请勿更改元素数目。 #解决方法 reshape(A,size),A的大小和size不相等,检查大小

CGG的博客 9242

动量因子在A股市场的有效性分析:量化实证研究

验证动量因子在A股市场的有效性及时间周期性分析不同时间窗口(短期/中期/长期)对动量效应的影响量化交易成本、市场流动性对动量策略收益的侵蚀程度比较A股与成熟市场(如美股)动量效应的差异特征理论框架:行为金融学解释动量效应的产生机制技术实现:Python全流程代码实现动量因子计算与回测实证分析:统计检验、风险收益指标、敏感性分析应用拓展:结合市场微观结构的策略优化方案动量因子(Momentum Factor)

2501_92132293的博客 2481

精确计算时,不要使用float或double

照例,还是来一段EffectiveJava的内容。内容大家应该已经都熟悉,不过还是发现很多新人完全不清楚,所以拿来共享一下。一、病例: System.out.println(1.03 - 0.42); // 0.6100000000000001 System.out.println(1.00 - 9 * 0.10); // 0.09999999999999998 又比如,每个单价0...

weixin_30734435的博客 258

青少年C/C++软件编程(四级)等级考试真题及答案解析目录

3. 带通配符的字符串匹配。1. 怪盗基德的滑翔翼。3.最长公共子上升序列。4.宠物小精灵之收服。

长春高老师编程 3033

(2-2-2)位置控制算法:常用的位置控制算法(2)

例如下面是一个使用滑模控制算法实现机器人位置控制的例子,在该例子中,假设机器人在一维空间中移动,目标是使机器人的位置迅速、鲁棒地达到设定的目标位置。自适应控制算法通过实时地调整控制器参数,可以适应系统的变化,从而提高系统的鲁棒性和性能。模型预测控制(Model Predictive Control,MPC)是一种先进的控制策略,通常应用于动态系统,以实现对系统状态的优化控制。(6)迭代优化:随着时间推移,MPC迭代执行上述步骤,持续优化控制输入,以适应系统动态和外部扰动,实现对系统状态的稳健控制。

码农三叔 1608

天天记录 - Java 精确计算避免使用float和double

一  问题描述     float和double类型不能用于精确计算,其主要目的是为了科学计算和工程计算,它们执行二进制浮点原酸,目的是为了广泛的数值范围上提供较为精确的快速近似计算而精心设计的。但是如果设计钱币之类的计算需要很精确,所以这种情况不能使用float和double,因为要让其精确表示0.1 或者 10的任何负数次方值是不可能的。 二 眼见为实,举例证明: //

Android 4541

Jenkins部署前后端分离项目最全图文教程(手把手教学)

最近Jenkins部署的热度比较高,于是就想出一篇关于我是如何使用Jenkins部署前后端分离项目的,本篇记录了我是如何一步一步去配置、搭建和部署的完整流程,每一行代码都进行了实测和讲解,通过图文的教学写成博客分享给大家!

黄团团的个人博客 4780

【笔记60】如果需要精确的答案,请避免使用float和double

float和double主要为了科学计算和工程计算而设计,执行二进制浮点运算,这是为了在广泛的数值范围上提供较为精确的快速近似计算而精心设计的。然而,它们没有提供完全精确的结果,所以不适合用于需要精确结果的场合,尤其是货币计算。 //假设有1.03元,花掉0.42元后 System.out.println(1.03 - .42);//0.6100000000000001 使用舍入可以解决上面...

u013773608的博客 237

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

如果需要精确的答案,请避免使用float和double

float和double主要为了科学计算和工程计算而设计,执行二进制浮点运算,这是为了在广泛的数值范围上提供较为精确的快速近似计算而精心设计的。然而,它们没有提供完全精确的结果,所以不适合用于需要精确结果的场合,尤其是货币计算。

北辰 572

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

第60项:如果需要精确的答案,请避免使用float和double

  float和double类型主要是为了科学计算和工程计算而设计的。它们执行二进制浮点运算(binary floating-point arithmetic),这是为了在广泛的数值范围上提供较为精确的快速近似计算而精心设计的。然而,它们并没有提供完全精确的结果,所以不应该被用于需要精确结果的场合。float和double类型尤其不适合用于货币计算 ,因为要让一个float或者double精确地表...

Coloured_Glaze的博客 401

第48条:如果需要精确的答案,请避免使用float和double

float和double类型主要是为了科学计算和工程计算而设计的。他们执行二进制浮点运算,这是为了在广泛的数值范围上提供较为精确的快速近似计算而精心设计的。然而,它们并没有提供完全精确的结果,所以不应该被用于需要精确结果的场合。float和double类型尤其不是和用于货币计算,因为要让一个float或者double精确地标识0.1(或者10的任何其他负数次方值)是不能的。 举例说明代码如下: ...

月月大王的博客 473

建议:如果需要精确的答案,请避免使用float和double。

float和double类型尤其不适合用于货币计算,因为要让一个float或者double精确的表示0.1(或者10的任何其他负数次方值)是不可能的。 示例代码: double funds = 1.00; int itemsBought = 0; for (double price = .10; funds >= pric...

孤芳不自赏 2973

跟我学(Effective Java 2)第48条:如果需要精确的答案,请避免使用float和double

第48条:如果需要精确的答案,请避免使用float和double float和double主要为了科学计算和工程计算而设计,执行二进制浮点运算,这是为了在广泛的数值范围上提供较为精确的快速近似计算而精心设计的。然而,它们没有提供完全精确的结果,所以不适合用于需要精确结果的场合,尤其是货币计算。 System.out.println(1.03 - .42); //0.6100000000000001...

Dullon_jiang的博客 252

一文读懂《Effective Java》第48条:如果需要精确答案,请避免使用float和double

点击上方蓝字关注我们float 和 double 类型主要是为了科学计算和工程计算而设计的。他们执行二进制浮点运算,为了在广泛的数值范围上提供较为精准的快速近似计算而精心设计。但是,两者...

Diligence is the mother of success. 205

《effective java》48—如果需要精确的答案,请避免使用float和double

float和double类型并不适合用于货币计算,因为要让一个float或者double精确地表示0.1是不可能的。比如:可以看到计算结果并不正确。解决这个问题的正确办法是使用BigDecimal、int或者long进行货币计算。举个例子:你口袋里有1块钱,货架上有价格从1毛、2毛、3毛一直到1块钱的糖果。你打算从标价为1毛的糖果开始买,每种买一颗,一直到不能支付货架上的下一种货物为止,那么可以买...

伍六七的博客 753

31-如果要求精确的答案,请避免使用float和double

在以下场合,不能使用浮点数运算。1、货币     可以使用分为单位.2、精确计算      BigDecimal 可以自己控制舍入,基本没有位数限制。但是慢。当9位以下使用int,18位以下使用long.  去年年底就有一个case,是计算正负概率,然后做归一化。 计算概率 有很多诸如  p= n/s 的表达式,归一化后,分子分母约分,最后剩下一个连乘的分子

woandzk的专栏 401
上一篇: struts2: Form表单有多个submit的提交问题
下一篇: JDK和JRE的区别
ForWayfarer
博客等级 码龄20年 60粉丝 96原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值