Java:为什么在现实世界中我们应该使用BigDecimal而不是Double? [重复]
|
这个问题已经在这里有了答案:
没有找到相关结果
已邀请:
6 个回复
敦肌
拈吉勉犬姆
。另一方面,小数由表示2-n的位描述,即分数ѭ1number。数字0.75对应于
,得出位序列1100
。 有了这些知识,我们可以制定以下经验法则:任何十进制数字都由一个近似值表示。 让我们通过执行一系列琐碎的乘法来研究这种情况的实际后果。
打印1.0。尽管这确实是正确的,但它可能给我们一种错误的安全感。巧合的是,0.2是Java能够正确表示的少数几个值之一。让我们通过另一个琐碎的算术问题再次挑战Java,将数字0.1加十倍。
根据约瑟夫·D·达西博客中的幻灯片,两个计算的总和分别为
和
。这些结果对于有限的一组数字是正确的。浮点数的精度为8个首位数,而double数的精度为17个首位数。现在,如果预期结果1.0与屏幕上打印的结果之间的概念上的不匹配不足以使您的警钟响起,请注意Mr.的数字。达西的幻灯片似乎与印刷的数字不符!那是另一个陷阱。关于此的更多信息。 在看似最简单的情况下意识到了错误的计算之后,就可以合理地考虑印象可能会出现多快了。让我们将问题简化为仅添加三个数字。
令人震惊的是,不精确性已经在三个附加项中开始发挥作用! 加倍溢出 与Java中的任何其他简单类型一样,双精度数由一组有限的位表示。因此,将一个值加或乘以一个双可以产生令人惊讶的结果。诚然,数字必须很大才能溢出,但是它确实发生了。让我们尝试相乘然后除以一个大数。数学直觉说结果是原始数。在Java中,我们可能会得到不同的结果。
这里的问题是先将big乘以并溢出,然后再将溢出数除。更糟糕的是,不会向程序员发出异常或其他类型的警告。基本上,这使表达式x * y完全不可靠,因为在通常情况下,对于由x,y表示的所有double值均不作任何表示或保证。 大大小小的不是朋友! 劳雷尔和哈代经常在很多事情上意见分歧。同样在计算中,大大小小的都不是朋友。使用固定位数表示数字的结果是,在相同的计算中对非常大和非常小的数字进行运算将无法按预期进行。让我们尝试将小的内容添加到大的内容中。
加法没有效果!这与任何(理智的)加法直觉相矛盾,后者表示给定两个数字d和f为正数,则d + f> d。 小数不能直接比较 到目前为止,我们学到的是,我们必须抛弃在数学课和整数编程中获得的所有直觉。请谨慎使用小数。例如,语句“ 11”实际上是伪装的永无止境的循环!错误是直接将十进制数字相互比较。您应遵循以下准则。 避免两个小数之间的相等性测试。请勿使用
,而应使用
,因为公差可以是一个常量,例如公共静态最终双公差= 0.01 考虑使用运算符<,>作为替代方法,因为它们可以更自然地描述您要表达的内容。例如,我更喜欢表格
比较笨拙
但这两种形式都有其优点,具体取决于情况:在单元测试中,我更倾向于表示
而不是
不仅第一种形式的阅读效果更好,而且通常是您要进行的检查(即d不太大)。 所见即所得-所见即所得 所见即所得是通常在图形用户界面应用程序中使用的表达式。它的意思是“您所看到的就是您所得到的”,用于计算以描述一个系统,在该系统中,编辑期间显示的内容看起来与最终输出非常相似,该最终输出可能是打印的文档,网页等。该短语最初是一个流行的流行短语,由Flip Wilson的扮靓人物“ Geraldine”提出,他常常说“您看到的就是所得到的东西”来原谅她的古怪行为(来自维基百科)。 程序员经常遇到的另一个严重陷阱是,他们认为十进制数是所见即所得。必须意识到,在打印或写入十进制数时,不是要打印/写入的近似值。换一种说法,Java在幕后做了很多近似,并且不断地试图使您不了解它。只有一个问题。您需要了解这些近似值,否则您的代码中可能会遇到各种各样的神秘错误。 但是,只要有一些独创性,我们就可以调查幕后的真实情况。到现在为止,我们知道数字0.1已被近似表示。
我们知道0.1不是0.1,但屏幕上仍打印了0.1。结论:Java是所见即所得! 为了多样化,让我们选择另一个无辜的数字,例如2.3。像0.1一样,2.3是一个近似值。毫不奇怪,当打印数字时,Java隐藏了近似值。
要研究2.3的内部近似值是多少,我们可以将该数字与附近的其他数字进行比较。
因此2.2999999999999997与值2.3一样是2.3!还要注意,由于近似值,枢轴点位于..99997处,而不是..99995处,通常在数学上四舍五入。掌握近似值的另一种方法是调用BigDecimal的服务。
现在,不要为自己的桂冠而休息,以为您可以跳船,只使用BigDecimal。 BigDecimal此处记录了自己的陷阱集合。 没有什么是容易的,而且很少有什么是免费的。而“自然地”浮动和加倍在打印/书写时会产生不同的结果。
根据约瑟夫·D·达西(Joseph D. Darcy)博客的幻灯片,浮点近似具有24个有效位,而双近似具有53个有效位。士气是为了保留值,必须以相同格式读取和写入十进制数字。 被0除 许多开发人员从经验中知道,将数字除以零会突然终止其应用程序。在int上运行时,发现了类似的行为是Java,但非常令人惊讶的是,在double上运行时却没有。除零外,任何数字除以零分别得出∞或-∞。将零除以零会产生特殊的NaN(非数字)值。
将正数与负数相除会产生负数的结果,而将负数与负数相除会产生正数的结果。由于可以用零除,因此根据将数字除以0.0还是-0.0会得到不同的结果。对,是真的! Java为负零!但是不要被愚弄,两个零值相等,如下所示。
无限怪异 在数学世界中,无穷大是我很难理解的概念。例如,当一个无穷大比另一个无穷大时,我从没有获得直觉。当然,Z> N,所有有理数的集合都比自然数的集合无限大,但这大约是我在这方面的直觉极限! 幸运的是,Java中的无穷与数学世界中的无穷差不多。您可以对无穷大的值执行通常的可疑行为(+,-,*,/,但不能将无穷大应用于无穷大。
这里的主要问题是返回NaN值而没有任何警告。因此,如果您愚蠢地调查某个特定的双精度是偶数还是奇数,您真的会陷入困境。也许运行时异常会更合适?
突然,您的变量既不是奇数也不是偶数! NaN比Infinity更奇怪 无限值与double的最大值不同,并且NaN再次与无限值不同。
通常,当双精度数已获取值NaN时,对其进行任何运算都会得到NaN。
结论 小数是近似值,而不是您分配的值。在数学世界中获得的任何直觉都不再适用。期待
和
避免使用==,与一些容差进行比较,或者使用> =或<=运算符 Java是所见即所得!永远不要相信您打印/写入的值是近似值,因此始终以相同格式读取/写入十进制数。 请注意不要溢出双精度数,不要使双精度数变为±Infinity或NaN。无论哪种情况,您的计算结果都可能不符合您的预期。您可能会发现,最好在返回方法值之前始终检查这些值。
郡豪靠暖
桑娠贯涤
哭木算
辽躺
是处理钱财的糟糕选择。