用Kotlin固定浮点运算

本文探讨了Java中浮点运算的不准确性,如何使用BigDecimal作为解决方案,以及Kotlin如何通过扩展方法和运算符重载提供更简洁、易读的浮点运算方式。
摘要由CSDN通过智能技术生成

这周,我终于抽出时间来与Sonar一起分析我们的代码库。 特别是让我意识到了很多有关浮点运算的问题。

有趣的Java浮点运算法则

那些在学术背景下学习过Java的人可能还记得关于FP算术的一些东西。 然后,如果您从未使用过它们,您可能会忘记它们。 这是一个有趣的非常简单的示例,事实证明是:

doublea=5.8d;
doubleb=5.6d;
doublesub=a-b;
assertThat(sub).isEqualTo(0.2d);

与常识相反,此代码段引发一个AssertionErrorsub 等于0.2而是0.20000000000000018

BigDecimal作为拐杖

当然,没有一种值得称呼的语言能使这种说法站得住脚。 BigDecimal是Java的答案:

BigDecimal类提供了用于算术,缩放操作,舍入,比较,哈希和格式转换的操作。

让我们使用BigDecimal更新以上代码段:

BigDecimala=newBigDecimal(5.8d);
BigDecimalb=newBigDecimal(5.6d);
BigDecimalsub=a.subtract(b);
assertThat(sub).isEqualTo(newBigDecimal(0.2d));

然后再次运行测试......糟糕,它仍然失败:

java.lang.AssertionError:
Expecting:
 <0.20000000000000017763568394002504646778106689453125>
to be equal to:
 <0.200000000000000011102230246251565404236316680908203125>
but was not.

使用构造函数不会改变任何东西,而必须使用静态valueOf()方法。

BigDecimala=BigDecimal.valueOf(5.8d);
BigDecimalb=BigDecimal.valueOf(5.6d);
BigDecimalsub=a.subtract(b);
assertThat(sub).isEqualTo(BigDecimal.valueOf(0.2d));

终于奏效了,但是作为很多仪式的代价...

Kotlin救援

仅仅将代码移植到Kotlin只会稍微提高可读性:

vala=BigDecimal.valueOf(5.8)
valb=BigDecimal.valueOf(5.6)
valsub=a.subtract(b)
assertThat(sub).isEqualTo(BigDecimal.valueOf(0.2))

请注意,在Kotlin中,默认情况下浮点数是双精度的。

为了使API更加流畅,从而使代码更具可读性,可以应用两个有价值的Kotlin功能。

第一个是扩展方法(我已经在以前的文章中展示了它们在改进SLF4J日志记录中的用法。让我们在这里使用它轻松地从Double创建BigDecimal对象:

funDouble.toBigDecimal():BigDecimal=BigDecimal.valueOf(this)

vala=5.8.toBigDecimal()// Now a is a BigDecimal

第二个功能-与方法扩展一起,是运算符重载。 Kotlin位于Java(无法进行运算符重载)和Scala(可以重载每个运算符)之间(我想知道为什么现在还没有表情符号库):仅某些运算符可以重载,包括算术运算符- +-*/

可以很容易地覆盖它们,如下所示:

operatorfunBigDecimal.plus(a:BigDecimal)=this.add(a)
operatorfunBigDecimal.minus(a:BigDecimal)=this.subtract(a)
operatorfunBigDecimal.times(a:BigDecimal)=this.multiply(a)
operatorfunBigDecimal.div(a:BigDecimal)=this.divide(a)

valsub=a-b

注意,这已经在Kotlin的stdlib中得到了解决

原始代码段现在可以这样写:

vala=5.8.toBigDecimal()
valb=5.6.toBigDecimal()
assertThat(a-b).isEqualTo(0.2.toBigDecimal())

断言线可能可以进一步改善。 为了使isEqualTo()方法接受Double参数,可能的解决方案还包括AssertJ自定义断言或扩展方法。

结论

使用Kotlin扩展方法,可以使任何复杂的API或库更易于阅读。 你在等什么?

翻译自: https://blog.frankel.ch/fixing-floating-point-arithmetics-with-kotlin/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值