Tuesday, April 15, 2008

Arrrggghhhh .....

Arrghhh!!! Try this:


Smalltalk
Transcript show: (50.0 - 7.23) asString

returns 42.77


C/C++
#include 

int main(int argc, char ** argv) {
double a = 50.0 - 7.23;
printf("%lf", a);
}

returns 42.770000


Java
public class Test {
public static void main(String[] args) {
double a = 50.0 - 7.23;
System.out.println(a);
}
}

returns 42.769999999999996

9 comments:

  1. You don't even need the #asString. Transcript>>#show: does that automatically. At least, it does in Squeak.

    ReplyDelete
  2. Note that the calculation stated will not be carried out with the precision implied by seeing the numbers written in base 10 notation. Is this perhaps an issue of dissimilar printing policies?

    ReplyDelete
  3. Take time an read of http://docs.sun.com/source/806-3568/ncg_goldberg.html

    Your example is just showing floating point arithmetic problems (feature), and by coincidence the C/C++ example did not showed that behavior, at least the Java example behave the same on every platform and does not depends on the C compiler and C runtime used

    I know the Smalltalk way looks more polished, but I like the Java way to use Decimal arithmetic only when I need them (decimal arithmetic is slower). I only hope Java had a way to use BigDecimal instances with simple operator syntax instead of methods, look at the way new EcmaScript 4 will treat decimals, that is elegant in my opinion

    http://dev.opera.com/articles/view/why-i-love-ecmascript-4-real-decimals/

    By the way I am a Smalltalk developer (learned Object Orientation with it) so this is not anything against Smalltalk. only that I had many times the need to move code that requires high performance to native code (Smalltalk primitives) because the use of pure Decimal objects, at least at that time with VisualAge Smalltalk

    ReplyDelete
  4. Remember this: Float are special kind of fraction with denominator being an exact power of 2 and numerator limited to 53 bits in double precision...
    And thus Float will do inexact operations except in a limited number of cases...

    So all you see is the rounding of this fraction to a certain number of digits in base 10.
    There is no contradiction there.
    I bet java did put enough digits by default such that the number can be re-interpreted unchanged.

    You might want to test this:
    50 - 7.23 = 42.77
    In squeak 3.10 were correct parsing of Float is implemented, the answer is false, and:
    50 - 7.23 = 42.769999999999996 -> true.

    Unfortunately, unlike recent versions of Squeak, most Smalltalk don't interpret decimal floating point representation accurately. They fail to answer the nearest Floating point number due to cumulated round off errors caused by naive algorithm.

    ReplyDelete
  5. After reading this post, I tried running the same code on my Scheme system, and I got the same result as Java:

    > (- 50.0 7.23)
    42.769999999999996

    Later on, I found out that there's a way to get the 'right' answer:

    > (exact->inexact (- #e50.0 #e7.23))
    42.77

    Also, starting with Java 1.5, you can use

    System.out.printf (String format, Object... args)

    ReplyDelete
  6. Have you tried looking at the actual bytes of all the floats to see if they're REALLY the same, as opposed to just printing the same?

    ReplyDelete
  7. In java note that running the following will give: 42.77
    float a = 50.0F - 7.23F;

    ReplyDelete
  8. 0.1 asFraction -> (3602879701896397/36028797018963968)
    0.1 asApproximateFraction -> (1/10)

    I discover the float problem recently, and so I discovered scaled decimals:
    0.1s1 asFraction -> (1/10)

    My test was in squeak (3.9, 3.10)...
    1 - 0.1 -0.1 -0.1 = 0.7 -> false

    ReplyDelete
  9. The aarghhh effect was caused by the different output I got:

    My program was comparing calculations
    and I had a LONG search in logs to find out that Java printed it like that. I expected it to print it as 42.77 as I knew from other languages/tools.

    ReplyDelete