java中浮点数的比较(double, float)

本文深入探讨了Java中浮点数(float和double)的存储机制,包括符号位、指数位及尾数位的具体作用,并解释了为何直接比较浮点数可能会导致错误结果。

最近在项目中碰到了一个业务逻辑计算,代码如下(示例代码)

double val1 = ...;

double val2 = ...,

double dif = ...,

if (Math.abs(val1 - val2-dif) == 0){

  //do things

}

 

结果发现有一组数据:61.5,60.4,1.1无法达到正确的结果.有经验的开发人员一眼就可以发现问题所在,也知道应该采用如下的方式修改代码(产品模式下要进行代码的抽取和封装):

double exp = 10E-10;

if (Math.abs(val1 - val2-dif)>-1*exp && Math.abs(val1 - val2-dif)<exp){

 //do things

}

 

那为什么上面代码中"Math.abs(val1 - val2-dif) == 0"的值为什么会是false呢?这就引申到java的一个基础问题,即java中浮点数的存储机制.

 

Java 中的浮点数分为单精度和双精度数,也就是floatdouble.

 

float在内存中跟int一样,占4个字节,32 bit.

 

1bit表示符号,0表示正数,1表示负数,这个很好理解,不用多管.

 

2-9bit表示指数,一共8(可以表示0-255),这里的底数是2,为了同时表示正数和负数,这里要减去127的偏移量.这样的话范围就是(-127128),

另外全0和全1作为特殊处理,所以直接表示-126127.

 

剩下的23位表示小数部分,这里23位表示了24位的数字,因为有一个默认的前导1(只有二进制才有这个特性).

 

     最后结果是:(-1)^(sign) * 1.f * 2^(exponent)

     这里:sign是符号位,f23bit的小数部分,exponent是指数部分,最后表示范围是(因为正负数是对称的,这里只关心正数)

    2^(-126) ~~ 2(1-2^(-24)) * 2^127

    这个还不是float的取值范围,因为标准中还规定了非规格化表示法,另外还有一些特殊规定.

   

非规格化表示:

    当指数部分全0而且小数部分不全0时表示的是非规格化的浮点数,因为这里默认没有前导1,而是0.

    取值位0.f * 2^(-126),表示范围位 2^(-149)~~ (1-2^(-23)) * 2^(-126) 这里没有考虑符号.这里为什么是-126而不是-127? 如果是-127的话,那么最大表示为

2^(-127)-2^(-149),很显然2^(-127) ~~2^(-126) 就没法表示了.

 

 

其他特殊表示

    1.当指数部分和小数部分全为0,表示0,+0-0之分(符号位决定),0x00000000表示正0,0x80000000表示负0.

    2.指数部分全1,小数部分全0,表示无穷大,有正无穷和负无穷,0x7f800000表示正无穷,0xff800000表示负无穷.

    3.指数部分全1,小数部分不全0,表示NaN,分为QNaNSNaN,Java中都是NaN.

 

结论:

    可以看出浮点数的取值范围是:2^(-149)~~(2-2^(-23))*2^127,也就是Float.MIN_VALUEFloat.MAX_VALUE.

 

References:

http://blog.youkuaiyun.com/treeroot/archive/2004/09/05/95071.aspx

http://hi.baidu.com/520miner/blog/item/698266ed9ee000d7b31cb1aa.html

<think>好的,用户想了解Java中如何对浮点数float进行向上取整。首先,我需要回忆一下Java中处理浮点数取整的方法。 首先,Math类中有几个相关的方法,比如Math.ceil、Math.floor和Math.round。其中,Math.ceil的作用是向上取整,返回大于等于参数的最小double值。但用户提到的是float类型,所以可能需要强制转换。例如,float num = 3.3f; 使用Math.ceil(num)会得到4.0,但类型是double,需要转成float的话可以强转。 然后,用户可能需要知道不同方法的区别。比如,Math.floor是向下取整,Math.round是四舍五入。这时候应该举例说明,比如3.3向上取整是4,3.7向上取整也是4,而-3.3向上取整则是-3,因为-3比-3.3大,但满足大于等于的条件。 另外,要注意float转成double时的精度问题。虽然float的精度较低,但Math.ceil处理时应该不会有太大问题,不过最好提醒用户注意可能的精度丢失,尤其是在处理特别大或特别小的数值时。 可能还需要提到其他方法,比如类型转换直接截断,但这不是向上取整,而是向下取整。比如(int)3.7会得到3,所以这不适用。因此,Math.ceil是正确的方法。 最后,应该给出一个完整的例子,展示如何声明float变量,使用Math.ceil并转换结果,同时处理正负数的情况。这样用户能清楚整个流程,包括可能的类型转换步骤。</think>在Java中对浮点数float进行向上取整,可以使用`Math.ceil()`方法,但需要注意类型转换问题。以下是具体实现步骤: ### 方法说明 1. `Math.ceil(double a)`函数: - 作用:返回≥参数的最小整数值(双精度浮点数形式) - 特点:严格向上取整,无论小数部分多小 - 输入/输出类型:double类型 ### 示例代码 ```java float original = 3.3f; // 需要先转为double类型参数 double ceilResult = Math.ceil(original); // 得到4.0 // 将结果转回float(可选) float result = (float) ceilResult; // 得到4.0f ``` ### 注意事项 1. 类型转换流程: ``` floatdouble(自动转换)→ ceil运算 → 强制转回float(可选) ``` 2. 特殊情况处理: ```java System.out.println(Math.ceil(3.0f)); // 3.0(整数保持不变) System.out.println(Math.ceil(-2.3f)); // -2.0(负数的向上取整方向) System.out.println(Math.ceil(Float.NaN)); // NaN ``` ### 替代方案对比 | 方法 | 功能 | 3.1输入 | 3.9输入 | -2.3输入 | |---------------------|-------------|-------|-------|---------| | `Math.ceil()` | 向上取整 | 4.0 | 4.0 | -2.0 | | `Math.floor()` | 向下取整 | 3.0 | 3.0 | -3.0 | | `Math.round()` | 四舍五入 | 3 | 4 | -2 | | `(int)`强制类型转换 | 截断小数部分 | 3 | 3 | -2 | ### 完整示例 ```java public class CeilExample { public static void main(String[] args) { float[] testCases = {3.1f, 3.9f, -2.3f, 5.0f}; for (float num : testCases) { double ceil = Math.ceil(num); System.out.println(num + " 向上取整 → " + ceil); } } } ``` ### 输出结果 ``` 3.1 向上取整 → 4.0 3.9 向上取整 → 4.0 -2.3 向上取整 → -2.0 5.0 向上取整 → 5.0 ``` 对于需要精确计算的场景(如财务计算),建议使用`BigDecimal`类进行处理,但常规的数值计算使用`Math.ceil()`即可满足大多数向上取整需求。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值