Java中浮点数的基础知识

偶然查看Math.round的JDK

1  public static int round(float a) {
2         if (a != 0x1.fffffep-2f) // greatest float value less than 0.5
3             return (int)floor(a + 0.5f);
4         else
5             return 0;
6     }

注释说0x1.fffffep-2f是最接近0.5的float类型的小数,咦,科学计数法用e表示指数我是知道的,但是这个p是什么鬼。可能有的读者还会问,为什么这个数时最接近0.5的数,这个数到底是多少呢?所以现在的问题有两个:

1、p代表什么。

2、0x1.fffffep-2f 用十进制表示到底是多少。

先公布答案,P在16进制表示的浮点数中代替e作为科学计数法指数部分的标志,1.fffffep-2f中的e是十六进制中的14;第二个问题很简单,也很复杂。说它简单是因为只需要几行代码就可以知道该值是多少。

System.out.println( 0x1.fffffep-2f);
BigDecimal bigDecimal=new BigDecimal(0x1.fffffep-2f);
System.out.println(bigDecimal.toPlainString());

/*输出
0.49999997
0.4999999701976776123046875

说它复杂是理解它为什么是最近0.5的float数。

1、P究竟是什么

原来为了和十六进制中的e进行区分,在java中用16进制表示的浮点数,我们用P代替e作为指数的标志。所以该常数代表0x1.fffffe * 2^(-2),f(F)是float后缀,不写代表是double类型。

A floating-point literal has the following parts: a whole-number part, a decimal or hexadecimal point (represented by an ASCII period character), a fractional part, an exponent, and a type suffix. A floating point number may be written either as a decimal value or as a hexadecimal value. For decimal literals, the exponent, if present, is indicated by the ASCII letter e or E followed by an optionally signed integer. For hexadecimal literals, the exponent is always required and is indicated by the ASCII letter p or P followed by an optionally signed integer.

参考链接:https://stackoverflow.com/questions/8603232/p-in-constant-declaration/8603263#8603263(中文版Java语言规范3.10.2)

2、为什么是最接近0.5的数

任意一个二进制浮点数V可以表示成下面的形式:

 

  (1)(-1)^s表示符号位,当s=0,V为正数;当s=1,V为负数。

  (2)M表示尾数,范围是[1,2)(规格化)或者是[0,1)(非规范化)。

  (3)2^E表示阶码。

IEEE 754规定,对于32位的浮点数,最高的1位是符号位s,接着的8位是指数E,剩下的23位为有效数字M。e代表指数部分的无符号数ek-1ek-2...e0,f代表尾数部分的无符号数表示,0.fn-1...f1f0。

对于exp非零的数即规格化数而言,指数部分的取值范围E=e-127,是[-126,127],尾数部分是[1,2-2^(-23)](M=1+f)。

要得到最接近0.5的数,我们不能用1*2(-1),因为是完全相等。退而求其次,我们用2-2^(-23)*2(-2),一个非常接近2的数(3.9999998)除以4来表示0.5无疑是最正确的选择。

看到这里细心的读者可能会问,为什么不用一个非常接近4的数去除以8来到答案呢?无论十六进制浮点数写成0x1.fffffep2这种形式,还是写成0x3.3fffff这种尾数大于2的,Java内部都会通过调整阶码自动将尾数部分控制在[1,2)之间。所以无论使用3.99999(近似表示,下同)除以8还是7.9999999除以16,最后都要表示为一个尾数在[1,2)范围内,乘以一个阶码。换句话说,无论是用3.99999除以8还是7.9999999除以16最终的结果都是一样的。为了看起来比较直观,避免尾数转换之后精度丢失的麻烦,我们直接将尾数固定在[1,2)去确定阶码无疑是最正确的选择。

0x1.fffffep-2f二进制表示是 0 11111100 1111 1111 1111 1111 1111 111。

我们可以猜想对于double类型最接近0.5的数而言,应该同样是尾数全是1,阶码为-2。即为0x1.fffffffffffffp-2。

 public static long round(double a) {
        if (a != 0x1.fffffffffffffp-2) // greatest double value less than 0.5
            return (long)floor(a + 0.5d);
        else
            return 0;
    }

查看JDK,果不其然,说明我的分析是没有问题的。

(以上基础知识来自《深入理解计算机系统》2.4.2)

时间: 2025-01-01 10:23:53

Java中浮点数的基础知识的相关文章

Java中String的基础知识

Java中String的基础知识 ==与equal的区别 基本数据类型,指的是java中的八种基本数据结构(byte,short,char,int,long,float,double,boolean),一般的比较是使用的 ==,比较的是他们的值. 复合数据类型(类) ==比较的是两个对象的引用,可以理解为在内存中的地址,除非是同一个new出来的对象,他们的 ==为true,否则,都为false. equal是object中的方法.object中的实现如下,内部还是使用==实现,也就是说,如果一个

Java中的NIO基础知识

上一篇介绍了五种NIO模型,本篇将介绍Java中的NIO类库,为学习netty做好铺垫 Java NIO 由3个核心组成,分别是Channels,Buffers,Selectors.本文主要介绍着三个部分. Channel 所有的I/O都从一个Channel开始.通道与流不同,通道是双向的,流是单向的. 即可以从通道中读取数据,也可以写数据到通道里 . 读的话,是从通道读取数据到缓冲区,写的话是从缓冲区写入数据到通道. 四种通道: FileChannel.从文件中读写数据 DatagramCha

[基础] Java目录(摘自Java核心技术·卷1 基础知识)

Java核心技术·卷1 基础知识(原书第9版) 第1章 Java程序设计概述 1.1 Java程序设计平台 1.2 Java"白皮书"的关键术语 1.2.1 简单性 1.2.2 面向对象 1.2.3 网络技能 1.2.4 健壮性 1.2.5 安全性 1.2.6 体系结构中立 1.2.7 可移植性 1.2.8 解释型 1.2.9 高性能 1.2.10 多线程 1.2.11 动态性 1.3 Java applet与Internet 1.4 Java发展简史 1.5 关于Java的常见误解

Java多线程完整版基础知识

Java多线程完整版基础知识 (翟开顺由厚到薄系列) 1.前言 线程是现代操作系统中一个很重要的概念,多线程功能很强大,java语言对线程提供了很好的支持,我们可以使用java提供的thread类很容易的创建多个线程.线程很不难,我对之前学习过的基础,在这做了一个整理,本文主要参考的是Java研究组织出版的j2se进阶和张孝祥-java就业培训教材这两本书 2.概述 2.1线程是什么 主要是线程与进程的区别,这里不再阐述,自行网上搜索 为什么使用线程:操作系统切换多个线程要比调度进程在速度上快很

(001)springboot中测试的基础知识以及接口和Controller的测试

(一)springboot中测试的基础知识 (1)添加starter-test依赖,范围指定为test,只在执行测试时生效 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> 完整po

《java核心技术 卷1 基础知识》二

<Java核心技术 卷1 基础知识>第4-5章 在Java中没有类就无法做任何事情 new操作符的返回值是一个引用 在Java中,如果使用一个未初始化的指针,运行系统会产生一个运行时错误. Date 用来表示时间点 LocalDate 日历表示法 同时不推荐使用Date类来处理日历 推荐使用LocalDate来处理日历 每一个拥有名字的类都会被编译生成对应的class文件 所有的Java对象都是在堆中构造的,构造器总是随着new操作符一起使用 在Java中,所有的方法都必须在类的内部定义,但并

《Java核心技术 卷1 基础知识》三

<Java核心技术 卷1 基础知识> 第六章 接口和内部类 接口不是类,而是对类的的一组需求描述. 接口不能包含实例域--接口没有实例 可以将接口看作没有实例域的抽象类 要将类声明为实现某个接口,需要使用关键字implements. 类实现一个接口的具体步骤为: 1)使用implements关键字进行声明要实现的接口 2)对接口中的所有方法进行定义 接口中的所有方法默认为public 但在实现接口时需要声明为public 这里介绍了Comparable接口,该接口只有一个方法,compareT

《Java核心技术 卷1 基础知识》七

<Java核心技术 卷1 基础知识> 第10章 图形程序设计 在Java1.0刚出现时,就包含了一个基本GUI程序设计的类库,即抽象窗口工具箱(Abstract Window Toolkit,AWT) 基本AWT库采用将处理用户界面元素的任务委派给每个目标平台(如windows.Macintosh等)的本地GUI工具箱的方式, 由本地工具箱负责用户界面元素的创建和动作 但由于在不同的平台上,操作行为有一些微妙的差别 因此,AWT也由"一次编写,随处使用"变为"一次

ASP.NET中的C#基础知识

ASP.NET中的C#基础知识 说明:asp.net作为一种开发框架现在已经广为应用,其开发的基础除了前端的html.css.JavaScript等后端最重要的语言支持还是C#,下面将主要用到的基础知识做一个总结,方面后面的学习. 一.C#是一种面向对象的变成语言,主要用于开发可以在.net平台上运行的应用程序.是一种强类型语言,一次每个变量都必须具有声明类型.C#中有两种数据类型:值类型和引用类型.(其中值类型用于存储值,引用类型用于存储实际数据的引用). 1.值类型 值类型表示实际的数据,存