002. java.lang.NumberFormatException: Infinite or NaN,怎么破?

news2024/10/5 13:33:22

在这里插入图片描述
你好,我是YourBatman:当我老了,也写代码;不为别的,只为爱好。

📚前言

如果你工作超5年,100%遇到过这个异常:java.lang.NumberFormatException: Infinite or NaN
在这里插入图片描述

  • Infinite中文释义:极大的、无法衡量的、无穷尽的;
  • NaN:Not a Number,不是一个数,它是计算机科学中数据类型的一种,代表不可表示的值,常用于浮点数计算中,于1985年纳入浮点数标准IEEE 754。

在 Java 中只有浮点类型(Float&Double)实现了IEEE 754标准

它还有些变种异常:阅完本文就知道这些异常本质上其实是一回事了

  • java.lang.NumberFormatException: For input string: NaN
  • java.sql.SQLException: 'NaN' is not a valid numeric or approximate numeric value

✍正文

java.lang.NumberFormatException: Infinite or NaN异常并不算常见(毕竟开发中浮点数远远没有整数使用场景多),但也绝不罕见。so,知道为何会出现此异常,以及如何解决它是每个开发者必知必会的知识点。

🌈异常哪里抛出来的?

(假设你看不到异常栈)从抛出的异常中可以提取到两个关键信息供以我们查找异常源头:

  1. 异常类型:java.lang.NumberFormatException
  2. 异常detail msg:Infinite or NaN

首先当然是利用Java语言强类型的优势,看看哪些地方引用到了java.lang.NumberFormatExceptionNumberFormatException
在这里插入图片描述
OMG,在641个地方出现过,看到这个数字该当场死心了:这条信息基本就是无效信息。

无奈再根据关键字Infinite or NaN搜索试试:
在这里插入图片描述
太幸运了,有且仅有一处代码里存在。看看是哪里:
在这里插入图片描述
破案了: java.lang.NumberFormatException: Infinite or NaN异常有且仅在构造BigDecimal实例的时候才有可能抛出。

🌈抛出此异常的原因

既然抛出此异常的源码都找到了,并且还只有一处,回答此问题就非常容易了:

public BigDecimal(double val, MathContext mc) {
    if (Double.isInfinite(val) || Double.isNaN(val))
        throw new NumberFormatException("Infinite or NaN");
       
    ... // 省略其它代码
}

逻辑简单,将Double的两个方法isInfinite()isNaN()一看便知:

public final class Double extends Number implements Comparable<Double> {
	
	// 常量
    public static final double POSITIVE_INFINITY = 1.0 / 0.0;
    public static final double NEGATIVE_INFINITY = -1.0 / 0.0;
    public static final double NaN = 0.0d / 0.0;

	public static boolean isInfinite(double v) {
	    return (v == POSITIVE_INFINITY) || (v == NEGATIVE_INFINITY);
	}
	public static boolean isNaN(double v) {
	    return (v != v);
	}
	
}

一个个来。

🚀isInfinite(double v)

public static final double POSITIVE_INFINITY = 1.0 / 0.0;
public static final double NEGATIVE_INFINITY = -1.0 / 0.0;

public static boolean isInfinite(double v) {
    return (v == POSITIVE_INFINITY) || (v == NEGATIVE_INFINITY);
}

将v和两个常量比较而已,逻辑不可谓不简单。那么关键点来了:什么情况下一个double类型的值会和POSITIVE_INFINITY/NEGATIVE_INFINITY常量相等呢?

其实看Double类对这两个常量的定义,就明白了(参考👆🏻常量定义代码)。为了更清晰的对号入座,笔者这里再来几个举一反三的case:

@Test
public void fun2() {
    // 等于Double.POSITIVE_INFINITY的场景
    System.out.println(1.0 / 0 == Double.POSITIVE_INFINITY); // true
    System.out.println(2.0 / 0 == Double.POSITIVE_INFINITY); // true
    System.out.println(1 / 0.0 == Double.POSITIVE_INFINITY); // true
    System.out.println(2 / 0.0 == Double.POSITIVE_INFINITY); // true
    System.out.println(new Double(Double.POSITIVE_INFINITY) == Double.POSITIVE_INFINITY); // true

    // 等于Double.NEGATIVE_INFINITY的场景
    System.out.println(-1.0 / 0 == Double.NEGATIVE_INFINITY); // true
    System.out.println(-2.0 / 0 == Double.NEGATIVE_INFINITY); // true
    System.out.println(-1 / 0.0 == Double.NEGATIVE_INFINITY); // true
    System.out.println(-2 / 0.0 == Double.NEGATIVE_INFINITY); // true
    System.out.println(new Double(Double.NEGATIVE_INFINITY) == Double.NEGATIVE_INFINITY); // true

    // 需特别注意的特殊case:
    System.out.println(1.0 / -0 == Double.POSITIVE_INFINITY); // -0和0没有区别,所以结果是POSITIVE(true)
    System.out.println(1.0 / -0.0 == Double.NEGATIVE_INFINITY); // -0.0和0.0是有区别的,所以结果是POSITIVE(false)
}

总结一下:浮点数除法运算,分母为0且分子不为0,结果就是POSITIVE_INFINITY/NEGATIVE_INFINITY。

Tips:它哥两分别称作正无穷大负无穷大

🚀isNaN(double v)

public static final double NaN = 0.0d / 0.0;

public static boolean isNaN(double v) {
	return (v != v);
}

what?自己还能不等于自己?bug吧~

来看看:

@Test
public void fun3() {
	// double d = 0.0d / 0; // 结果一样

    System.out.println(d == Double.NaN);
    System.out.println(Double.isNaN(d));
}

运行后的输出结果为:

false
false -> d==d这个是falsetrue

惊不惊喜,意不意外:还真存在自己不等于自己的情况呢。

总结一下:浮点数除法计算,分母为0且分子为0,结果就是NaN。并且:每次计算的NaN都永不相等。

Tips:NaN代表不是数字,因此“不是数字”和“不是数字”不相等,从逻辑上好像也说得通嘛

🌈针对此异常的补充说明

围绕POSITIVE_INFINITY、NEGATIVE_INFINITY、NaN三个常量进行一些补充说明吧。

🚀直接打印输出什么?

@Test
public void fun4() {
    System.out.println(Double.POSITIVE_INFINITY);
    System.out.println(Double.NEGATIVE_INFINITY);
    System.out.println(Double.NaN);
}

运行程序,输出:

Infinity
-Infinity
NaN

总结一下:Double对象打印输出(toString或者序列化),不一定永远是数字,也有可能是字符串。

🚀 是否可以参与运算和比较?

虽然是常量,但毕竟也是数字类型嘛,那就看看运算和比较喽:

运算

@Test
public void fun7() {
    System.out.println("正无穷大参与运算:" + (Double.POSITIVE_INFINITY + 1)); // Infinity
    System.out.println("正无穷大参与运算:" + (Double.POSITIVE_INFINITY - 1)); // Infinity
    System.out.println("负无穷大参与运算:" + (Double.NEGATIVE_INFINITY * 1)); // -Infinity
    System.out.println("负无穷大参与运算:" + (Double.NEGATIVE_INFINITY / 1)); // -Infinity
    System.out.println("负无穷大参与运算:" + (Double.NEGATIVE_INFINITY / 0)); // -Infinity

    System.out.println("NaN参与运算:" + (Double.NaN + 1)); // NaN
    System.out.println("NaN参与运算:" + (Double.NaN - 1)); // NaN
    System.out.println("NaN参与运算:" + (Double.NaN * 1)); // NaN
    System.out.println("NaN参与运算:" + (Double.NaN / 1)); // NaN
    System.out.println("NaN参与运算:" + (Double.NaN / 0)); // NaN

    // 特殊场景
    System.out.println(Double.POSITIVE_INFINITY - Double.POSITIVE_INFINITY); // NaN
    System.out.println(Double.NEGATIVE_INFINITY - Double.NEGATIVE_INFINITY); // NaN
    System.out.println(Double.POSITIVE_INFINITY + Double.NEGATIVE_INFINITY); // NaN

    System.out.println("负无穷大参与运算:" + (Double.POSITIVE_INFINITY / -0.0)); // -Infinity
    System.out.println("负无穷大参与运算:" + (Double.NEGATIVE_INFINITY / -0.0)); // Infinity
}

总结一下:正/负无穷大和任何数值(包括除以0)做运算结果都是本身,和Infinite or NaN运算结果为NaN;NaN进行任何运算的结果都是NaN。

特例:正/负无穷大若除以-0的话,结果互调

比较

@Test
public void fun6() {
    System.out.println("正无穷大 > 任何数吗? -> " + (Double.POSITIVE_INFINITY > Double.MAX_VALUE)); // true
    System.out.println("正无穷大 > 任何数吗? -> " + (Double.POSITIVE_INFINITY > Long.MAX_VALUE)); // true
    System.out.println("负无穷大 < 任何数吗? -> " + (Double.POSITIVE_INFINITY > Double.MIN_VALUE)); // true
    System.out.println("负无穷大 < 任何数吗? -> " + (Double.POSITIVE_INFINITY > Long.MIN_VALUE)); // true

    System.out.println("NaN参与比较:" + (Double.NaN == Double.NaN)); // false
    System.out.println("NaN参与比较:" + (Double.NaN > Double.NaN)); // false
    System.out.println("NaN参与比较:" + (Double.NaN < Double.NaN)); // false
    System.out.println("NaN参与比较:" + (Double.NaN < 1)); // false
    System.out.println("NaN参与比较:" + (Double.NaN < -1)); // false

    System.out.println("NaN参与比较:" + (Double.NaN != -1)); // true
    System.out.println("NaN参与比较:" + (Double.NaN != Double.NaN)); // true
}

总结一下:正无穷大比任何数值都大;负无穷大比任何数值都小;NaN参与!=比较永远是true(包括和自己比),除此之外都为false。

🚀 Float里的这三个常量和Double一样吗?

弱弱问一句:2023年了在实际业务开发中,不会真有人使用Float吧?吧?吧?

灵魂拷问:如果你使用了Float,收益是什么?是否真的值得?

Float类里也存在这三个常量和判断的方法:

public final class Float extends Number implements Comparable<Float> {
	
	// 常量
    public static final float POSITIVE_INFINITY = 1.0f / 0.0f;
    public static final float NEGATIVE_INFINITY = -1.0f / 0.0f;
    public static final float NaN = 0.0f / 0.0f;

	public static boolean isInfinite(float v) {
	    return (v == POSITIVE_INFINITY) || (v == NEGATIVE_INFINITY);
	}
	public static boolean isNaN(float v) {
	    return (v != v);
	}
	
}

和Double可谓一毛一样嘛。看下这个:

@Test
public void fun5() {
    System.out.println(Double.POSITIVE_INFINITY == Float.POSITIVE_INFINITY);
    System.out.println(Double.NEGATIVE_INFINITY == Float.NEGATIVE_INFINITY);
    System.out.println(Double.NaN == Float.NaN);
}

运行程序,输出:

true
true
false

结论无需多言,自行体会做到心中有数哈。

🚀 其它语言的表现

以弱类型语言JavaScript为例:
在这里插入图片描述
表现和Java一样。毕竟NaN早已被纳入IEEE 754规范了,不出意外每种编程语言的表现都是一致的。

Tips:JavaScript中的isFinite()方法是正向思维的,和Java里isInfinite()是“反”着来的哦

🌈遇到此异常怎么破?

解决问题的难度永远在根因定位上,至于遇到此异常怎么破嘛,略!!!

考虑到代码的健壮性,实际场景中是可以对这些异常做预处理的:使用Double.isNaN()Double.isInfinite()等方法来做分支逻辑

🍞总结

在Java中,浮点数0并非一个准确值,而是一个无限接近0的数。为此才闹出这么多令人费解的“幺蛾子”,这是由计算机底层原理决定的,记住就好,无解。

计算机的运算基于数学,但貌似也有些“不同于”数学理论。这不,NaN这玩意就是这么神奇的存在。

推荐阅读

  • 001. 为啥用IDEA反编译没有擦除泛型?

在这里插入图片描述
本专栏源代码库:https://github.com/yourbatman/yourbatman-999-question

  • 个人博客:https://yourbatman.cn
  • 程序员网盘:https://wangpan.yourbatman.cn
  • 女娲工程:https://start.yourbatman.cn
  • 更多专栏:https://yourbatman.cn/columns |或| 公号后台回复“专栏列表”获取全部小而美的原创技术专栏

我是YourBatman,一个俗人,贪财好色。历经过延期毕业、卖保险、送外卖的大龄程序员,《梦幻西游》骨灰玩家;龙珠迷、火影迷。前大厂资深技术专家,现资深领域建模专家、Java架构师;高质量代码、DDD面向对象设计布道师;Spring开源贡献者,CSDN博客之星年度Top 10,出版书籍《Spring奇淫巧技》&《领域建模之面向对象程序设计》进行时。wx:yourbatman-u

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/588775.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用MockJS进行前端开发中的数据模拟

在前端开发中&#xff0c;有时我们需要在没有后端接口的情况下进行前端页面的开发和测试。这时&#xff0c;我们可以使用MockJS来模拟数据&#xff0c;以便进行开发和调试。MockJS是一个用于生成随机数据和拦截Ajax请求的JavaScript库&#xff0c;它能够帮助我们快速搭建起一个…

InVEST模型

学习目标&#xff1a; 1)采用InVEST模型&#xff0c;掌握产水&#xff08;包括水源涵养&#xff09;、碳存储&#xff08;包括固碳&#xff09;、土壤保持、水质&#xff08;氮磷&#xff09;、生境质量和热岛缓解等生态系统服务评估方法&#xff0c;开展人类活动影响、重大工…

MATLAB EXPO 2023会议记录|基于STM32的MATLAB电机控制方案

算法导出工作流视频&#xff1a;(99 封私信 / 82 条消息) Simulink 算法导出工作流 —— stm32电机控制示例(v2) - 知乎 (zhihu.com) Algorithm-Export Workflows for Custom Hardware 示例&#xff1a; Algorithm-Export Workflows for Custom Hardware - MATLAB & Simuli…

JavaScript拖拽API,ondragstart、ondragover、ondragenter、ondrop,使用详细(JavaScript常用原生拖拽API)

简述&#xff1a;JavaScript的拖拽api相必大家都不陌生&#xff0c;今天来分享下元素在拖动时触发的事件&#xff0c;顺便做下记录。 一、ondragstart事件 ondragstart事件在拖动元素时触发&#xff0c;通常用于设置拖动时的数据类型和数据。可以通过event.dataTransfer.setDa…

开发环境搭建-stm32CubeIDE进行标准库开发

stm32CubeIDE介绍 https://www.stmcu.com.cn/ecosystem/Cube/STM32CubeIDE stm32CubeIDE下载 点击上面的链接&#xff0c;登录即可下载。 搭建demo工程 新建工作空间 创建一个工程 选择芯片-STM32F103C8T6 点击“Next” 点击“Finish ” 添加标准库到项目工程文件目录下 配…

SM国密算法(二)-- OpenSSL库中分离算法

一、OpenSSL简介&#xff1a; OpenSSL 是用于传输层安全性 (TLS) 和安全套接字层 (SSL) 协议的一个强大、商业级和功能齐全的工具包。它也是一个通用的密码学库&#xff0c;包含有RSA、SM4、DES、AES等诸多加密算法。 OpenSSL GitHub地址 二、移植过程 1. 文件目录 下载在…

除蚂蚁文件数据恢复大师之外,还有哪些相似的软件?

数据丢失是一件常见的事情&#xff0c;许多人都会遇到这样的问题。为了解决这个问题&#xff0c;出现了许多数据恢复软件&#xff0c;其中包含蚂蚁文件数据恢复大师。但是&#xff0c;除了蚂蚁文件数据恢复大师之外&#xff0c;还有哪些类似的软件呢&#xff1f;本文为您整理了…

笔试强训总结3

作者&#xff1a;爱塔居 专栏&#xff1a;笔试强训 作者简介&#xff1a;大三学生&#xff0c;希望能同大家一起进步&#xff01; 1.以下代码运行输出的是 public class Person{ private String name "Person"; int age0; } public class Child extends Person{ p…

selenium clear()无效的解决办法

做自动化时&#xff0c;在往输入框中send_keys前往往需要先清空一下这个输入框里的内容&#xff0c;避免输入框原本有内容或默认值&#xff0c;导致最终输入的结果不是预期的内容。 清空内容我们一般会用clear()方法 import time from selenium import webdriverdriver webd…

如何使用Python操作Excel文件?看这篇博客就够了!

前言 如何使用Python操作Excel文件&#xff1f;看这篇博客就够了&#xff01; 在工作中&#xff0c;我们经常需要处理和分析数据。而Excel作为一种广泛使用的数据分析工具&#xff0c;被很多人所熟知。但是&#xff0c;对于一些非技术背景的用户来说&#xff0c;如何操作Exce…

自学网络安全,一般人我劝你还是算了吧

学前感言: 我为什么会这样说&#xff0c;要一般人自学网络安全就算了&#xff0c;因为我不是一般人 1.这是一条坚持的道路,三分钟的热情可以放弃往下看了. 2.多练多想,不要离开了教程什么都不会了.最好看完教程自己独立完成技术方面的开发 .3.有时多 google,baidu,我们往往…

自学网络安全(黑客)?一般人我劝你还是算了吧!

一、自学网络安全学习的误区和陷阱 1.不要试图以编程为基础的学习开始学习 我在之前的回答中&#xff0c;我都一再强调不要以编程为基础再开始学习网络安全&#xff0c;一般来说&#xff0c;学习编程不但学习周期长&#xff0c;而且实际向安全过渡后可用到的关键知识并不多 …

IMX6ULL裸机篇之IIC协议

一. IIC实验简介 I2C 是最常用的通信接口&#xff0c;众多的传感器都会提供 I2C 接口来和主控相连。 比如摄像头、 加速度计、触摸屏等。 I.MX6U-ALPHA开发板 使用 I2C1 接口连接了一个距离传感器 AP3216C &#xff0c;本章我们就来学习如何使用 I.MX6U 的 I2C 接口…

“开启科技之门,每日工作充满力量” —— 全国科技者工作日

作者主页&#xff1a;爱笑的男孩。的博客_CSDN博客-深度学习,活动,python领域博主爱笑的男孩。擅长深度学习,活动,python,等方面的知识,爱笑的男孩。关注算法,python,计算机视觉,图像处理,深度学习,pytorch,神经网络,opencv领域.https://blog.csdn.net/Code_and516?typeblog个…

科技创新—人工智能应用技术的出现

科技创新—人工智能应用技术的出现 人工智能&#xff08;Artificial Intelligence&#xff0c;AI&#xff09;研究目的是通过探索智慧的实质&#xff0c;扩展人类智能——促使智能主体会听&#xff08;语音识别、机器翻译等&#xff09;、会看&#xff08;图像识别、文字识别等…

深入解析CPU性能火焰图生成的内部原理

在进行CPU性能优化的时候&#xff0c;我们经常先需要分析出来我们的应用程序中的CPU资源在哪些函数中使用的比较多&#xff0c;这样才能高效地优化。一个非常好的分析工具就是《性能之巅》作者 Brendan Gregg 发明的火焰图。 我们今天就来介绍下火焰图的使用方法&#xff0c;以…

这三款识别图片手写文字的软件超好用

在过去&#xff0c;我们需要通过扫描或者人工录入的方式将手写文字数字化&#xff0c;这样操作不仅繁琐而且容易出错。而随着人工智能技术的发展&#xff0c;我们现在可以通过图片识别技术将手写文字自动识别为数字形态&#xff0c;从而实现自动化的信息处理和提取。这项技术可…

【C语言】经典题目(二)

C站的小伙伴们&#xff0c;大家好呀^^! 这一篇文章是C语言之经典题目&#xff0c;快来跟我一起进入C语言的世界吧&#xff01;&#x1f49e; C语言其他刷题篇在这里哦&#xff1a; 【C】语言经典题目&#xff08;一&#xff09; 【C语言】字符串—刷题篇 【C语言】经典题目二 求…

java.time包使用指南

目录 前言一、时区与时间1. 世界标准时&#xff1a;UTC、GMT、UT2. 地区时&#xff1a;Asia/Shanghai、UTC83. 时区&#xff1a;ZoneId、TimeZone4. 时间偏移量&#xff1a;ZoneOffset5. 时区简称&#xff1a;CTT、PRC 二、主要时间类1. 重要时间接口&#xff1a;Temporal2. 时…

Uni-app学习从0到1开发一个app——(1)初步了解各种小程序开发框架

文章目录 0 引入1、小程序常用框架1.1、 mpvue1.2、 mpvue1.3、 Tina.js1.4、 WePY1.5 微信官方1.6 TouchUI WX 2、uin-app3、引用 0 引入 uin-app官网地址&#xff1a;https://uniapp.dcloud.net.cn/ 最近对于小程序莫名的感兴趣起来&#xff0c;索性就从uni-app开始吧 1、小…