-4

Why the result of multiplication 0.3 and 3 is 0.89999999999999 in Scala?

Kyokook Hwang
  • 2,682
  • 21
  • 36
  • 2
    Downvote. 1) "I don't know why" is not a question. 2) There are, literally, thousands of questions about these kinds of floating point artifacts. Even simply Googling for it spews out tons of reference. – TC1 Feb 07 '13 at 11:26
  • @TC1 OK, you're right. My question is really foolish. Thank you. I'll fix it. – Kyokook Hwang Feb 07 '13 at 11:40

5 Answers5

7

Floating point calculation is reasonably complex subject.

This has to do with the binary representation of a floating point number, that doesn't guarantee every possible number (obviously), to have an exact representation, which can lead to errors in operations, and yes, these errors can propagate.

Here is a link on the subject, although it isn't the simplest thing out there, it can get you a good perspective if you want to understand the subject.

http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

The bottom line problem is that for a given floating point number, a rational, it is possible that the binary representation of that number needs more precision than what's available in the given language.

For instance, if you use 24 bits to store a double, but the binary representation of your floating point value needs 25 bits to be represented accurately, there will be a rounding error.

Edit:

As Péter Török noted in the comment, most known programming languages use the same representations for common data types, float -> 32 bits, double -> 64 bits, so the precision can usually be calculated taking the data type into regard, no matter the language.

pcalcao
  • 15,789
  • 1
  • 44
  • 64
  • *"it is possible that the binary representation of that number needs more precision than what's available in the given language"* - to be precise, this depends on the data type rather than the language used. Most known programming languages support the same floating point data types: `float` (32-bit) and `double` (64-bit). `Double` obviously has higher precision. – Péter Török Feb 07 '13 at 12:33
  • Yes, you are correct, I just didn't want to be too general, since there is no guarantee that the representation used is the same, it's a wildly adopted convention, but with languages popping up now and then, who knows. Still, your comment is correct, I'll edit to incorporate this. Thanks – pcalcao Feb 07 '13 at 12:37
6

That is not only the case in Scala, but in any language/platform, that uses the IEEE standard for floating point numbers.

Example in ruby:

0.1.9.2p320 :001 > 0.3 * 3
 => 0.8999999999999999

or Python:

>>> 0.3 * 3
0.8999999999999999
drexin
  • 24,225
  • 4
  • 67
  • 81
3

This has almost nothing to do with Scala!

The reason is to do with how computers represent floating point numbers. Computers use a binary representation, rather than a decimal one, so numbers such as 0.3 are not represented as that precisely but using an approximation that can be succinctly expressed in base 2. The result of this is that operations performed on these numbers may be slightly out from what you expect the answer to be. For more detail, see Floating Point.

You can get around this if necessary by implementing something like a Fraction class, which uses its own arithmetic to perform these computations exactly.

Impredicative
  • 5,039
  • 1
  • 17
  • 43
2

Use BigDecimal if you want exact values.

BigDecimal(3)*0.3 Will give you 0.9

0

That is a side-effect of the commonly used floating point values representation.

I often encoutenred that problem and make a simple function that "rounds" the number to the nearest bigger-than-desired-precision value. It goes in C as:

int64_t __pow10_arr[18] = { 1l, 10l, 100l, 1000l, 10000l, 100000l,
                            1000000l, 10000000, 100000000l, 1000000000l, 10000000000l, 100000000000l,
                            1000000000000l, 10000000000000l, 100000000000000l, 1000000000000000l, 10000000000000000l, 100000000000000000l };

double roundToNfractions ( double val, uint8_t n )
{
  val *= __pow10_arr[n];
  val += 0.5;
  val = (uint64_t) val;
  val /= __pow10_arr[n];
  return val;
}

Not very efficient but does the trick, because you can safely write printf("%.2d", val); and you always get results like 3.00 instead of 2.99;

Dariusz
  • 21,561
  • 9
  • 74
  • 114