groostav
03/27/2023, 7:25 PMval firstDbl: Double = 0.1
val firstStr: String = "0.1"
val firstBigStr = BigDecimal("0.1")
val firstBigDbl = BigDecimal(0.1)
has some funny equality implications:
firstDbl == firstStr.toDouble() //true
firstBigStr == firstBigDbl //false
firstBigStr.toDouble() == firstBigDbl.toDouble() // false, 0.1 != 0.0...555
firstBigDb.toString() // 0.1000000000000000055511151231257827021181583404541015625
Can somebody help me understand where this error came from? Should i be calling a different BigDecimal constructor?ephemient
03/27/2023, 7:29 PM0.1 in codegroostav
03/27/2023, 7:29 PMDouble.toString() knew how to get it back?groostav
03/27/2023, 7:29 PMephemient
03/27/2023, 7:30 PM.toString() finds the shortest string representation that produces the same value, not the exact representationephemient
03/27/2023, 7:30 PM0.1000000000000000055511151231257827021181583404541015625 == 0.1groostav
03/27/2023, 7:30 PMgroostav
03/27/2023, 7:31 PM0x3FB999999999999Agroostav
03/27/2023, 7:31 PMgroostav
03/27/2023, 7:31 PMephemient
03/27/2023, 7:31 PMtoString() produces the latter, BigDecimal captures all of the formerephemient
03/27/2023, 7:31 PMgroostav
03/27/2023, 7:37 PMdouble fields to store some input from a user. Its pretty straight forward. I was aware that the first time you do an operation on that value you've incurred a loss, but I had thought that any value that was denotable within the precision limits of IEEE 754 was, how should I say, recoverable as its denotation within those bounds.
IE, if i wrote 0.1 in a text field (or indeed in intelliJ as a constant), the I would get the bits 0x3FB999999999999A, and, like an ASCII coding of A to 0x41 , i could convert back and forth between them as I saw fit.ephemient
03/27/2023, 7:38 PM0.1 is not actually 0.1 as you think of it, as that cannot be exactly representedgroostav
03/27/2023, 7:39 PMgroostav
03/27/2023, 7:40 PMratio primative in kotlin, i wonder if thats already an issueephemient
03/27/2023, 7:41 PMephemient
03/27/2023, 7:42 PMgroostav
03/27/2023, 7:42 PM0.1r, but im digressinggroostav
03/27/2023, 7:42 PMgroostav
03/27/2023, 7:43 PMval myDouble = doubleParsedFromDenotableUserText(text)
how safe is it to write val bigDecimal = BigDecimal(myDouble.toString()),
I guess my question is how much of a perfect dual operation is text.toDouble() and double.toString()? when will the two not produce a result that agrees?ephemient
03/27/2023, 7:44 PMdouble.toString().toDouble() is supposed to round-trip (for finite values, on JVM - not JS), but string.toDouble().toString() is not, as there are many possible string representations for the same floating-point valuegroostav
03/27/2023, 7:48 PMgroostav
03/27/2023, 7:48 PM0.1?ephemient
03/27/2023, 7:49 PMgroostav
03/27/2023, 7:50 PM0.1 == 0.100000000000000005 && 0.1 == 0.0.100000000000000005999 is trueephemient
03/27/2023, 7:50 PM"0.099999999999999999".toDouble() == "0.1".toDouble()groostav
03/27/2023, 7:50 PMephemient
03/27/2023, 7:51 PMDouble which will .toString() to "0.1"?groostav
03/27/2023, 7:51 PMgroostav
03/27/2023, 7:51 PMephemient
03/27/2023, 7:51 PMephemient
03/27/2023, 7:53 PMgroostav
03/27/2023, 7:58 PMdouble, and back again, losslessly?groostav
03/27/2023, 8:01 PMdouble value (IE, a double value we knew was parsed from a Denotable), we could get that denotable representation backephemient
03/27/2023, 8:01 PMephemient
03/27/2023, 8:02 PMgroostav
03/27/2023, 8:02 PMephemient
03/27/2023, 8:04 PMgroostav
03/27/2023, 8:04 PMgroostav
03/27/2023, 8:04 PMgroostav
03/27/2023, 8:04 PMgroostav
03/27/2023, 8:04 PMephemient
03/27/2023, 8:07 PMDouble.toString docs): 1.0E23.toString() == "9.999999999999999E22"groostav
03/27/2023, 8:07 PMgroostav
03/27/2023, 8:10 PMBigDecimal on my model istead of a double, but im trying to understand the scope of the failures I can see with the use of double. It seems like, perhalps a reasonable intermediate fix is to do a double.toString().toDouble as a kind of validation on user input, and then... do something if input != input.toDouble().toString()groostav
03/27/2023, 8:10 PMgroostav
03/27/2023, 8:11 PMgroostav
03/27/2023, 8:12 PMephemient
03/27/2023, 8:12 PM0.0 == -0.0
(0.0).toString() != (-0.0).toString()groostav
03/27/2023, 8:12 PMgroostav
03/27/2023, 8:12 PMgroostav
03/27/2023, 8:14 PMgroostav
03/27/2023, 8:15 PM-0.0 is denotable differently than 0.0, so the fact that it gives you different representation doesnt violate the rulegroostav
03/27/2023, 8:15 PM-0.0.toString() == "-0.0" its finegroostav
03/27/2023, 8:15 PMephemient
03/27/2023, 8:16 PMrequire(input == input.toDouble().toString()) may force you to enter certain values differently on different JVMs - aside from the bug above, the representation is supposed to be minimal but not necessarily uniqueephemient
03/27/2023, 8:18 PMgroostav
03/27/2023, 8:21 PMtoDouble.toString() where appropriategroostav
03/27/2023, 8:21 PMDoubleConverter or DecimalFormat thats a bit more strict about thisgroostav
03/27/2023, 8:21 PMephemient
03/27/2023, 8:25 PMephemient
03/27/2023, 8:27 PM