Menu

#4738 is(equal(x,y)) is not symmetric for mixed float/bfloat

None
open
nobody
5
2026-05-20
2026-05-19
No
for fpprec:1 thru 20 do if equal(float(1/3),bfloat(1/3)) then print(fpprec);
2, 5, 8, 11, 14

for fpprec:1 thru 20 do if equal(bfloat(1/3),float(1/3)) then print(fpprec);
1, 3, 4, 6, 7, 9, 10, 12, 13, 15

This is because equal(a,b) works by calculating a-b, which is not the same as b-a.

There are a several cleaner definitions which would make equal reflexive:

  • equal only if they are exactly equal, i.e. rationalize(a)=rationalize(b)
  • equal only if they are equal to the precision of the less-precise one
  • equal only if both a-b and b-a are zero.
    I'm not sure whether these are equivalent.

I would advocate for the most "intuitive" definition, namely the first: they are equal if they represent exactly the same number.

Discussion

  • Stavros Macrakis

    • labels: --> equal, bfloat
     
  • Robert Dodier

    Robert Dodier - 2026-05-20

    OK by me to implement rationalize(y) = rationalize(x).

     
    • Stavros Macrakis

      To be clear, I mean that as a specification. To implement, we can use integer-decode-float and ash to align the binary representations, like this (thank you, Claude):

      (defun float=bigfloat (f bf)
        "Return T if CL double-float F and Maxima bigfloat BF are exactly equal.
         Uses INTEGER-DECODE-FLOAT and ASH to compare as integers without
         any floating-point arithmetic."
        (multiple-value-bind (f-sig f-exp f-sign)
            (integer-decode-float f)
          (let* ((signed-sig (* f-sign f-sig))
                 (bf-mantissa (cadr bf))
                 (bf-exp      (caddr bf))
                 (fpprec      (caddar bf))
                 ;; Shift both to a common exponent.
                 ;; float value  = signed-sig * 2^f-exp
                 ;; bigfloat value = bf-mantissa * 2^(bf-exp - fpprec)
                 ;; Shift the one with the larger exponent left to match the other.
                 (f-scaled-exp  (+ f-exp fpprec))   ; exponent of float side after scaling
                 (common-exp    bf-exp)              ; use bf-exp as the baseline
                 (f-int  (ash signed-sig  (- f-scaled-exp common-exp)))
                 (bf-int (ash bf-mantissa 0)))       ; no shift needed on bf side
            (= f-int bf-int))))
      
       

Log in to post a comment.

MongoDB Logo MongoDB