5

In Kotlin 123.456 is a valid Double value, however, 123.456F.toDouble() results in 123.45600128173828 - presumably just the way precision is handled between the two.

I want to be able to convert freely between the two, specifically for cases like this:

123.456F -> 123.456 // Float to Double

123.456 -> 123.456F // Double to Float

How can I convert a float to a double in cases like this, and maintain precision?

Matthew Layton
  • 39,871
  • 52
  • 185
  • 313
  • 1
    "Maintaining precision" is almost nonsensical when it comes to floating-point numbers. 123.456F is _actually_ represented as a number closer to 123.45600128173828. (As usual, if you want to do something like this, you should _really, really_ be using `BigDecimal`.) – Louis Wasserman May 14 '19 at 15:24

2 Answers2

8

It's a big ugly, but you could convert your Float to a String and back out to a Double:

val myDouble: Double = 123.456f.toString().toDouble()
// 123.456d

You could always encapsulate this in an extension function:

fun Float.toExactDouble(): Double = 
    this.toString().toDouble()

val myDouble = 123.456f.toExactDouble()
Todd
  • 30,472
  • 11
  • 81
  • 89
4

In Kotlin 123.456 is a valid Double value

Actually, that's not quite true.  There's a Double value very close to 123.456, but it's not exactly 123.456.  What you're seeing is the consequences of that.

So you can't maintain precision, because you don't have that precision to start with!

Short answer:

If you need exact values, don't use floating-point!

(In particular: Never store money values in floating-point! See for example this question.)

The best alternative is usually BigDecimal which can store and calculate decimal fractions to an arbitrary precision. They're less efficient, but Kotlin's operator overloading makes them painless to use (unlike Java!).

Or if you're not going to be doing any calculations, you could store them as Strings.

Or if you'll only need a certain number of decimal places, you could scale them all up to Ints (or Longs).

Technical explanation:

Floats and Doubles use binary floating-point; they store an integer, and an integer power of 2 to multiple or divide it by.  (For example, 3/4 would be stored as 3*2⁻².)  This means they can store a wide range of binary fractions exactly.

However, just as you can't store 1/3 as a decimal fraction (it's 0.3333333333…, but any finite number of digits will only be an approximation), so you can't store 1/10 as a binary fraction (it's 0.000110011001100…).  This means that a binary floating-point number can't store most decimal numbers exactly.

Instead, they store the nearest possible value to the number you want.  And the routines which convert them to a String will try to undo that difference, by rounding appropriately.  But that doesn't always give the result you expect.

Floating-point numbers are great when you need a huge range of values (e.g. in scientific and technical use), but don't care about storing them exactly.

gidds
  • 16,558
  • 2
  • 19
  • 26