javalispcommon-lisparithmetic-expressionss-expression

Why do these two equivalent expressions in java and lisp have different outcomes?


The expression in lisp is:

(* (+ a b) (- a (/ 1 b)))

The equivalent expression (I think) in Java is:

(a + b) * (a - (1/b));

But when I input the same a and b float values into both, they evaluate differently. Why is that?


Solution

  • If you are using the same float types (singles or doubles for both), then what you are seeing is probably the different behaviour of the thing that prints the floats. Chances are the numbers are actually the same. The default CL printer is (or should be) rather careful to print enough information that the float can be read back in and be the same to the one it printed. So given

    (defun f (a b)
      (* (+ a b) (- a (/ 1 b))))
    

    Then we can test the read-print consistency with

    (defun tsrp (x)
      (with-standard-io-syntax
        (eql (read-from-string (write-to-string x))
             x)))
    

    And now in one implementation (the first example is using single precision the second double, which is how CL should read these with default reader settings):

    > (f 1.001 1.0003)
    0.0026016448
    
    > (tsrp (f 1.001 1.0003))
    t
    
    > (f 1.001d0 1.0003d0)
    0.002601509937018715D0
    
    > (tsrp (f 1.001d0 1.0003d0))
    t
    

    And in another:

    ? (f 1.001 1.0003)
    0.0026016447
    ? (tsrp (f 1.001 1.0003))
    t
    ? (f 1.001d0 1.0003d0)
    0.002601509937018715D0
    ? (tsrp (f 1.001d0 1.0003d0))
    t
    

    Note that the two implementations print different looking results in the single-precision case. But you can confirm that these two representations are the same single-float:

    > (= 0.0026016448 0.0026016447)
    t
    

    and

    ? (= 0.0026016448 0.0026016447)
    t
    

    I don't know how to get Java to print a float so it can be read back in like this: I think it might be the %s format control. If that's right, and you make sure your float types are the same, then you should get either the same representation, or a representation which differs only in digits which don't actually matter (which you can check, with equivalent code to the above).