Java Floating-Point Number Intricacies
Thomas Wang, March 2000last update September 2000
Abstract
The semantic of Java floating point number generally follows the IEEE 754 Binary Floating-Point Arithmetic standard. The IEEE 754 standard provides for the definitions of floating-point infinities, negative infinities, negative zero, and NaN (not a number). In the context of Java, these entities are often confusing to typical programmers.
In this article, we will discuss the results of computing with these special floating-point entities. We will also point out some common Java floating-point number pit falls.
Introduction
The Java programming language provides two built-in classes for representing floating-point numbers: float, and double. The "float" class takes 4 bytes of storage, and have 23 binary digits of precision. The "double" class takes 8 bytes of storage, and have 52 binary digits of precision.
3.0d is a double precision floating-point number constant. 3.0, or alternatively 3.0f is a single precision floating-point number constant.
float f = 3.0f;
double d = 3.0d;
The Java core library also provides two wrapped classes java.lang.Float, and java.lang.Double. These two classes allow floating-point objects to be stored in Java collection objects, such as hash tables. These two classes also provides parsing, and conversion helper methods.
Float ff = new Float(1.0f); // creates a Java "Float" object
Double dd = new Double(2.0d); // creates a Java "Double" object
Special Floating-Point Number Entities
Not A Number
"NaN" stands for "not a number". "Nan" is produced if a floating point operation has some input parameters that cause the operation to produce some undefined result. For example, 0.0 divided by 0.0 is arithmetically undefined. Taking the square root of a negative number is also undefined.
0.0 / 0.0 -> NaN
Math.sqrt(-2.0) -> NaN
Operations involving NaN
Double.NaN + Double.NaN -> NaN
Float.NaN + 2.0 -> NaN
Float.NaN * 3.0 -> NaN
(0.0 / 0.0) * Float.POSITIVE_INFINITY -> NaN
Math.abs(0.0 / 0.0) -> NaN
(int) (Double.NaN) -> 0
All boolean operations involving "NaN" results in a false value.
Double.NaN > 1.0 -> false
Double.NaN < 1.0 -> false
Double.NaN == 1.0 -> false
Float.NaN < -3.0 -> false
Float.NaN > Float.POSITIVE_INFINITY -> false
Float.NaN < Float.POSITIVE_INFINITY -> false
(0.0 / 0.0) == (0.0 / 0.0) -> false
Infinity
"Infinity" is produced if a floating point operation creates such a large floating-point number that it cannot be represented normally. "Infinity" is a special value that represent the concept of positive infinity.
1.0 / 0.0 -> Infinity
Operations Involving Infinities
Double.POSITIVE_INFINITY == Double.POSITIVE_INFINITY -> true
Float.POSITIVE_INFINITY == Float.POSITIVE_INFINITY -> true
(1.0 / 0.0) + 2.0 -> Infinity
(1.0 / 0.0) * 0.5 -> Infinity
(1.0 / 0.0) * 0.0 -> NaN
(1.0 / 0.0) + (1.0 / 0.0) -> Infinity
(1.0 / 0.0) * (1.0 / 0.0) -> Infinity
(1.0 / 0.0) - (1.0 / 0.0) -> NaN
(1.0 / 0.0) / (1.0 / 0.0) -> NaN
(int) (1.0 / 0.0) -> 2147483647
Negative Infinity
"-Infinity" is produced if a floating point operation creates such an extremely negative floating-point number that it cannot be represented normally. "-Infinity" is a special value that represent the concept of negative infinity.
-1.0 / 0.0 -> -Infinity
Operations Involving Negative Infinity
- Double.POSITIVE_INFINITY == Doublle.NEGATIVE_INFINITY -> true
Float.NEGATIVE_INFINITY < 1.0 -> true
(-1.0 / 0.0 ) + 2.0 -> -Infinity
(-1.0 / 0.0) == (-1.0 / 0.0) -> true
(-1.0 / 0.0) - (-1.0 / 0.0) -> NaN
(-1.0 / 0.0) * 0.5 -> -Infinity
(-1.0 / 0.0) * 0.0 -> NaN
Math.abs(-1.0 / 0.0) -> Infinity
(int) (-1.0 / 0.0) -> -2147483648
Negative Zero
"-0.0" is produced when a floating-point operation results in a negative floating-point number so close to 0 that cannot be represented normally.
-2.0 / Float.POSITIVE_INFINITY -> -0.0
"-0.0" is numerically identical to "0.0". However, some operations involving "-0.0" are different than the same operation with "0.0".
(-0.0) == 0.0 -> true
2.0 / (0.0) -> Infinity
2.0 / (-0.0) -> -Infinity
Operations Involving Negative Zero
0.0 > (-0.0) -> false
(-0.0) * (-0.0) -> 0.0
3.0 + (-0.0) -> 3.0
4.0 * (-0.0) -> -0.0
0.0 - 0.0 -> 0.0
(-0.0) - (-0.0) -> 0.0
(-0.0) + (-0.0) -> -0.0
(-0.0) + 0.0 -> 0.0
0.0 + (-0.0) -> 0.0
(-0.0) - 0.0 -> -0.0
- (-0.0) -> 0.0
- (0.0) -> -0.0
0.0 / (-0.0) -> NaN
Comparing Java "Float" Objects
Comparing two Java "Float" objects can have different semantics than comparing two Java "float" values. Recall the "float" class is a Java primitive class, while java.lang.Float is a subclass of "Object".
A "NaN" value is not equal to itself. However, a "NaN" Java "Float" object is equal to itself. The semantic is defined this way, because otherwise "NaN" Java "Float" objects cannot be retrieved from a hash table.
(new Float(0.0 / 0.0)).equals(new Float(0.0 / 0.0)) -> true
For the class java.lang.Float, objects are ordered from lowest to highest: -Infinity, negative numbers, -0.0, 0.0, positive numbers, Infinity, NaN. "java.lang.Double" objects are identically ordered.
(new Float(0.0)).equals(new Float(-0.0)) -> false
(new Float(-1.0 / 0.0)).compareTo(new Float(-3.0)) -> -1
(new Float(-3.0)).compareTo(new Float(-0.0)) -> -1
(new Float(-0.0)).compareTo(new Float(0.0)) -> -1
(new Float(0.0)).compareTo(new Float(3.0)) -> -1
(new Float(3.0)).compareTo(new Float(1.0 / 0.0)) -> -1
(new Float(1.0 / 0.0)).compareTo(new Float(0.0 / 0.0)) -> -1
Some Actual Pitfalls
Wrong Way to Implement abs()
This is the wrong way:
(f >= 0.0) ? f : - f
What if 'f' is -0.0? Since 0.0 is equal to -0.0, the above expression would return the incorrect value of -0.0, versus the correct value of 0.0.
The correct expression is:
(f <= 0.0) ? 0.0 - f : f
Be very careful when you are optimizing a floating point expression. For example, if you optimized the above expression to:
(f <= 0.0) ? - f : f
Then you introduced a bug that causes abs(0.0) to return -0.0.
Danger of Floating-Point Number Optimization
We have already seen a case where (0.0 - f) is not equal to (- f).
! (a < b) does not imply that (a >= b).
! (1.0 < NaN) -> true
(1.0 >= NaN) -> false
One page 308 of the book "The Java Language Specification", it talks in more detail of the danger of optimizing floating-point expressions.
Assuming A Floating-Point Value Is Equal To Itself
Well, a "NaN" value is not equal to itself. For similar reason, assuming a number subtracting itself must equal to 0.0 is wrong. "NaN" - "NaN" still yields "NaN". "Infinity" - "Infinity" yields "NaN", although two Infinities are equal to each other.
Not Checking for NaN
public employee(float monthly_salary)
{
if (monthly_salary < 0.0)
throw IllegalArgumentException("illegal monthly salary");
this.yearly_salary = 12.0 * monthly_salary;
}
The above piece of code would not catch the mistake of using a "NaN" as an input argument. The code below would catch it.
public employee(float monthly_salary)
{
if (monthly_salary >= 0.0)
{
this.yearly_salary = 12.0 * monthly_salary;
}
else throw IllegalArgumentException("illegal monthly salary");
}
Looking at the fixed code, what should we do if the input argument is "Infinity"? Depending on the API's specification, we can do a number of different things. The point is that any time we are dealing with floating-point numbers, we should think about the cases dealing with "NaN", "Infinity", "-Infinity", and "-0.0".
References
James Gosling, Bill Joy, Guy Steele, "The Java Language Specification", Addison Wesley, (1996)
IEEE, "ANSI/IEEE Std 754-1985, An American National Standard IEEE Standard for Binary Floating-Point Arithmetic", (1985)
分享到:
相关推荐
stylelint-config-concentric-order 此配置根据验证 CSS 属性的顺序。 除了您自己的规则(或来自可共享配置的规则)之外,强烈建议您使用它,因为它只与排序有关。 该配置适用于stylelint --fix和以自动对您的 CSS ...
将其导入到您的文件中import 'package:concentric_transition/concentric_transition.dart'; 特征 同心PageView 同心飞剪机 同心PageRoute 用法 使用ConcentricPageView小部件 import 'package:concentric_...
【市场多元化战略】是企业在市场竞争中采取的一种策略,旨在通过扩展到不同的行业或领域来分散风险、扩大市场份额和实现持续增长。这种战略的核心是企业利用自身的技术、资源和能力,进入与现有业务相关或完全不相关...
3 7 ms 7 ms 7 ms x130.cd9e68.sj.concentric.net [205.158.104.130] 4 7 ms 7 ms 8 ms ge9-0.dcr2.dc-fremont-ca.us.xo.net [205.158.60.169] 5 7 ms 7 ms 7 ms ge2-0.dcr1.dc-fremont-ca.us.xo.net [65.106.2.205]...
matlab跳过代码直接运行适应性风险偏好 从以下位置安装:并按照自述文件中的...此示例在同心环世界中演化了一个64千机器人群的行为,可以轻松替代其他世界或行为。基本用法包括: 为包含所有要演化的变量的行为编写脚
#### 一、常用前缀 **1. micro-:极其小** - **定义与含义**:前缀“micro-”来源于希腊语,意为“极其小”,用于表示极小或微小的事物。 - **示例**: - **computer** → **microcomputer**:微型计算机 - **...
- Concentric Circles: 同心圆 - Cone: 圆锥 - Congruent: 全等 - Consecutive Integers: 连续整数 - Coordinate: 坐标 - Cost: 成本 - Counterclockwise: 逆时针 - Cube: 立方体 - Cylinder: 圆柱 - ...
Cytoscape.js 是一个强大的JavaScript库,专为在Web浏览器中创建交互式网络图谱而设计。这个库被广泛应用于数据可视化、生物信息学、社交网络分析以及其他领域,允许开发者构建复杂的图形界面来展示节点和边的关系。...
Patchwork 地面分割原理是基于 concentric zone 模型和 Region-wise 地面拟合的ground segmentation 方法,该方法可以 robust 地处理under-segmentation 问题,并且可以在 40Hz 以上的频率下运行。该方法首先将点云...
CCTag库 检测由同心圆组成的CCTag标记。 在CPU和GPU中均实现。 该库是该文件的实现: Lilian Calvet,Pierre Gurdjos,Carsten Griwodz,Simone Gasparini。 在高度挑战性的条件下对圆形基准的检测和精确定位。...
G6提供了多种布局算法,包括Force、Fruchterman、Circular、Radial、Dagre、Concentric、Grid、Tree等。开发者可根据具体需求选择合适的布局,并在图初始化后通过`graph.changeLayout`方法更换布局。 ```javascript...
《Patchwork:基于同心区域的区域级地面分割与地表概率估计》是一篇发表在2021年10月IEEE ROBOTICS AND AUTOMATION LETTERS上的论文,作者包括Hyungtae Lim、Minho Oh和Hyun Myung。该论文主要探讨了地面分割问题,...
ASTM B228 是一种标准规范,旨在规定 concentric-lay-stranded 铜包钢导线的生产和检测要求。该标准涵盖了铜包钢导线的材料、结构、性能和检测等多个方面。 1. 范围 ASTM B228 规定了 concentric-lay-stranded 铜...
运行: ./bin/concentric-clock_dbg Windows(在Win7 x64上测试) 将放在文件夹.\lib\ [可选]将SFML .dll文件放在.\bin目录中,以便更轻松地执行二进制文件,因为lib是动态链接的。 如果使用的是mingw32,请运行...
ASTM B232是一份关于 concentric-lay-stranded 铝导体的标准规范,该规范涵盖了铝导体的设计、制造、测试和应用等方面。 1. 标准规范的作用域 ASTM B232规范涵盖了 concentric-lay-stranded 铝导体的设计、制造和...
concentric_wheel
ConCentric事件计划软件是一款专为会议和活动策划者设计的开源工具,旨在优化和简化整个活动策划过程。开源软件的特性使得用户可以免费获取、使用,并可以根据自己的需求进行定制和改进,这为用户提供了极大的灵活性...
抽象精品ppt模板concentric_wheel147
1. **等径直通(Equal Coupling)**:用于连接两个相同直径的管道,提供一个连续的流体通道。 2. **异径直通(Reducing Coupling)**:连接不同直径的管道,允许流体在不同尺寸之间过渡。 3. **牙直通(Female ...