[Oberon] Wrong results of selected REAL multiplications

Hans Klaver hklaver at dds.nl
Mon Oct 16 17:19:06 CEST 2023


Hi Joerg,

You wrote i.a.:

> Here the corrected version. I splitted “round and normalize” into two steps.
> Introduced a new variable z1 for the rounding phase.

Great! Thanks for your swift and accurate action.

I tested your FML code with my original REAL test-values (see the code below; I turned FML into a function).
I can confirm that you:
- pinpointed the Verilog bug accurately (and showed it in your Oberon translation)
- corrected it well in the Oberon code!

> This is in Verilog:
>  (...)

I can't assess the Verilog code. 
But thanks to your Oberon translations of it I do understand it somewhat now ;-)

The actions of Chris, Michael and you are a clear sign that the Oberon community is very knowledgeable and alive & kicking!

Regards,

Hans



MODULE TestFPMul;  (* IN V5 *)

  IMPORT SYSTEM, Texts, Oberon, Out;  (* https://github.com/hansklav/Oberon-07/blob/master/Out.Mod (for Out.Hex) *)

  VAR W: Texts.Writer;


  PROCEDURE Bit(x: INTEGER; hi, lo: INTEGER): INTEGER;
  RETURN ROR(x, lo) MOD LSL(2, hi-lo)
  END Bit;
 

  PROCEDURE FML0* (x, y: REAL): REAL; (* floating point multiply, (with bug!) *)
  (* Translation of Verilog code by Joerg Straube *)
    VAR
      z: REAL;
      S: INTEGER; (* state *)
      P0, P24: INTEGER; (* 48 bit product: 0..23 and 24..47 *)
      sign: BOOLEAN;
      xe, ye: INTEGER; (* 8 bits *)
      e0, e1: INTEGER; (* 9 bits *)
      w0: INTEGER; (* 24 bits *)
      w1, z0: INTEGER; (* 25 bits *)
  BEGIN
    sign := Bit(ORD(x), 31, 31) # Bit(ORD(y), 31, 31);
    xe := Bit(ORD(x), 30, 23);
    ye := Bit(ORD(y), 30, 23);
 (* Out.printf("xe = %d, ye = %d\n", xe, ye, ""); *)
    Out.String("xe = "); Out.Int(xe, 0); Out.String(", ye = "); Out.Int(ye, 0); Out.Ln;
  
    e0 := xe + ye;
    P24 := 0; P0 := 800000H + Bit(ORD(x), 22, 0);
    FOR S := 0 TO 23 DO (* look at all bits in p0 aka x *)
      IF ODD(P0) THEN w0 := 800000H + Bit(ORD(y), 22, 0) ELSE w0 := 0 END;
      w1 := P24 + w0; (* 25 bit result *)
      (* P <= {w1, P[23:1]} *)
      P0 := Bit(P0, 23, 1); (* this is P0 := P0 DIV 2 *)
      IF ODD(w1) THEN P0 := P0 + 800000H END;
      P24 := w1 DIV 2
    END;
 (* Out.printf("product P =%x%x\n", P24, P0, ""); *)
    Out.String("product P = "); Out.Hex(P24); Out.Hex(P0); Out.Ln;
    IF Bit(P24, 23, 23) = 1 THEN
      z0 := P24*2 + Bit(P0, 23, 23) + 1
    ELSE
      z0 := P24*4 + Bit(P0, 23, 22) + 1 (* e1 is not adjusted if rounding sets the highest bit!! *)
    END;
    e1 := e0 - 127 + Bit(P24, 23, 23); (* P[47] *)
 (* Out.printf("z0 = %x, e1 = %d\n", z0, e1, ""); *)
    Out.String("z0 = "); Out.Hex(z0); Out.String(", e1 = "); Out.Int(e1, 0); Out.Ln;    
    IF (xe = 0) OR (ye = 0) THEN z := 0.0 (*0R*)
    ELSIF Bit(e1, 8, 8) = 0 THEN z := SYSTEM.VAL(REAL, Bit(e1, 7, 0)* 800000H + Bit(z0, 23, 1))
    ELSIF Bit(e1, 7, 7) = 0 THEN z := SYSTEM.VAL(REAL, 7F800000H+ Bit(z0, 23, 1))
    ELSE z := 0.0 (*0R*)
    END;
    IF sign THEN z := -z END
  RETURN z
  END FML0;
  

  PROCEDURE FML* (x, y: REAL): REAL;  (* floating point multiply, corrected by Joerg Straube *)
    VAR
      z: REAL;
      S: INTEGER; (* state *)
      P0, P24: INTEGER; (* 48 bit product: 0..23 and 24..47 *)
      sign: BOOLEAN;
      xe, ye: INTEGER; (* 8 bits *)
      e0, e1: INTEGER; (* 9 bits *)
      z0: INTEGER; (* 23 bits *)
      w0: INTEGER; (* 24 bits *)
      w1: INTEGER; (* 25 bits *)
      z1: INTEGER; (* 26 bits *)
  BEGIN
    sign := Bit(ORD(x), 31, 31) # Bit(ORD(y), 31, 31);
    xe := Bit(ORD(x), 30, 23);
    ye := Bit(ORD(y), 30, 23);
 (* Out.printf("xe = %d, ye = %d\n", xe, ye, ""); *)
    Out.String("xe = "); Out.Int(xe, 0); Out.String(", ye = "); Out.Int(ye, 0); Out.Ln;

    e0 := xe + ye;
    P24 := 0; P0 := 800000H + Bit(ORD(x), 22, 0);
    FOR S := 0 TO 23 DO (* look at all bits in p0 aka x *)
      IF ODD(P0) THEN w0 := 800000H + Bit(ORD(y), 22, 0) ELSE w0 := 0 END;
      w1 := P24 + w0; (* 25 bit result *)
      (* P <= {w1, P[23:1]} *)
      P0 := Bit(P0, 23, 1); (* this is P0 := P0 DIV 2 *)
      IF ODD(w1) THEN P0 := P0 + 800000H END;
      P24 := w1 DIV 2
    END;
 (* Out.printf("product P =%x%x\n", P24, P0, ""); *)
    Out.String("product P = "); Out.Hex(P24); Out.Hex(P0); Out.Ln;
    z1 := P24*4 + Bit(P0, 23, 22) + 1 + Bit(P24, 23, 23); (* round *)
    IF Bit(z1, 25, 25) = 1 THEN (* normalize *)
      z0 := Bit(z1, 24, 2)
    ELSE
      z0 := Bit(z1, 23, 1)
    END;
    e1 := e0 - 127 + Bit(z1, 25, 25); (* P[47],  should be z1[25]*)
 (* Out.printf("z0 = %x, e1 = %d\n", z0, e1, ""); *)
    Out.String("z0 = "); Out.Hex(z0); Out.String(", e1 = "); Out.Int(e1, 0); Out.Ln;    
    IF (xe = 0) OR (ye = 0) THEN z := 0.0 (*0R*)
    ELSIF Bit(e1, 8, 8) = 0 THEN z := SYSTEM.VAL(REAL, Bit(e1, 7, 0)* 800000H + z0)
    ELSIF Bit(e1, 7, 7) = 0 THEN z := SYSTEM.VAL(REAL, 7F800000H+ z0)
    ELSE z := 0.0 (*0R*)
    END;
    IF sign THEN z := -z END
  RETURN z
  END FML;


  PROCEDURE WriteRealHex* (VAR W: Texts.Writer; x: REAL);
  (* Write the encoded ('raw') hexadecimal INTEGER value of a REAL.
    Verify on  https://float.exposed  *)
  BEGIN Texts.WriteHex(W, ORD(x)); Texts.Write(W, "H")
  END WriteRealHex;


  PROCEDURE Do0*;  (* to show the bug *)
  BEGIN
    Texts.WriteString(W, "1.0E6 * 1.048575 = ");
    Texts.WriteReal(W, FML0(1.0E6, 1.048575), 16); Texts.WriteLn(W);

    Texts.WriteString(W, "1.0E6 * 1.048576 = ");
    Texts.WriteReal(W, FML0(1.0E6, 1.048576), 16); Texts.WriteLn(W);

    Texts.WriteString(W, "1.0E6 * 1.048576 = ");
    Texts.WriteRealHex(W, FML0(1.0E6, 1.048576)); Texts.WriteLn(W);

    Texts.WriteString(W, "1.0E6 * 1.048577 = ");
    Texts.WriteReal(W, FML0(1.0E6, 1.048577), 16); Texts.WriteLn(W); Texts.WriteLn(W);

    Texts.WriteString(W, "1.0E6 * 2.097152 = ");
    Texts.WriteReal(W, FML0(1.0E6, 2.097152), 16); Texts.WriteLn(W);

    Texts.WriteString(W, "1.0E6 * 4.194304 = ");
    Texts.WriteReal(W, FML0(1.0E6, 4.194304), 16); Texts.WriteLn(W);

    Texts.WriteString(W, "1.0E5 * 1.31072 = ");
    Texts.WriteReal(W, FML0(1.0E5, 1.31072), 16); Texts.WriteLn(W);

    Texts.WriteString(W, "1.0E4 * 3.2768 = ");
    Texts.WriteReal(W, FML0(1.0E4, 3.2768), 16); Texts.WriteLn(W);

    Texts.WriteString(W, "1.0E4 * 1.6384 = ");
    Texts.WriteReal(W, FML0(1.0E4, 1.6384), 16); Texts.WriteLn(W);

    Texts.WriteString(W, "1.0E3 * 8.192 = ");
    Texts.WriteReal(W, FML0(1.0E3, 8.192), 16); Texts.WriteLn(W);

    Texts.WriteString(W, "1.0E3 * 4.096 = ");
    Texts.WriteReal(W, FML0(1.0E3, 4.096), 16); Texts.WriteLn(W);

    Texts.WriteString(W, "1.0E3 * 2.048 = ");
    Texts.WriteReal(W, FML0(1.0E3, 2.048), 16); Texts.WriteLn(W);

    Texts.WriteString(W, "1.0E3 * 1.024 = ");
    Texts.WriteReal(W, FML0(1.0E3, 1.024), 16); Texts.WriteLn(W);

    Texts.WriteString(W, "1.0E2 * 2.56 = ");
    Texts.WriteReal(W, FML0(1.0E2, 2.56), 16); Texts.WriteLn(W);

    Texts.WriteString(W, "100.0 * 2.56 = ");
    Texts.WriteReal(W, FML0(100.0, 2.56), 16); Texts.WriteLn(W);

    Texts.WriteString(W, "100.0 * 2.56 = ");
    Texts.WriteRealHex(W, FML0(100.0, 2.56)); Texts.WriteLn(W);

    Texts.WriteString(W, "1000.0 * 2.56 = ");
    Texts.WriteReal(W, FML0(1000.0, 2.56), 16); Texts.WriteLn(W);

    Texts.WriteString(W, "1.0E2 * 1.28 = ");
    Texts.WriteReal(W, FML0(1.0E2, 1.28), 16); Texts.WriteLn(W);

    Texts.WriteString(W, "1.0E1 * 6.4 = ");
    Texts.WriteReal(W, FML0(1.0E1, 6.4), 16); Texts.WriteLn(W);

    Texts.WriteString(W, "1.0E1 * 3.2 = ");
    Texts.WriteReal(W, FML0(1.0E1, 3.2), 16); Texts.WriteLn(W);

    Texts.Append(Oberon.Log, W.buf)                          
  END Do0;


  PROCEDURE Do*;  (* To show the corrected FML() *)
  BEGIN
    Texts.WriteString(W, "1.0E6 * 1.048575 = ");
    Texts.WriteReal(W, FML(1.0E6, 1.048575), 16); Texts.WriteLn(W);

    Texts.WriteString(W, "1.0E6 * 1.048576 = ");
    Texts.WriteReal(W, FML(1.0E6, 1.048576), 16); Texts.WriteLn(W);

    Texts.WriteString(W, "1.0E6 * 1.048576 = ");
    Texts.WriteRealHex(W, FML(1.0E6, 1.048576)); Texts.WriteLn(W);

    Texts.WriteString(W, "1.0E6 * 1.048577 = ");
    Texts.WriteReal(W, FML(1.0E6, 1.048577), 16); Texts.WriteLn(W); Texts.WriteLn(W);

    Texts.WriteString(W, "1.0E6 * 2.097152 = ");
    Texts.WriteReal(W, FML(1.0E6, 2.097152), 16); Texts.WriteLn(W);

    Texts.WriteString(W, "1.0E6 * 4.194304 = ");
    Texts.WriteReal(W, FML(1.0E6, 4.194304), 16); Texts.WriteLn(W);

    Texts.WriteString(W, "1.0E5 * 1.31072 = ");
    Texts.WriteReal(W, FML(1.0E5, 1.31072), 16); Texts.WriteLn(W);

    Texts.WriteString(W, "1.0E4 * 3.2768 = ");
    Texts.WriteReal(W, FML(1.0E4, 3.2768), 16); Texts.WriteLn(W);

    Texts.WriteString(W, "1.0E4 * 1.6384 = ");
    Texts.WriteReal(W, FML(1.0E4, 1.6384), 16); Texts.WriteLn(W);

    Texts.WriteString(W, "1.0E3 * 8.192 = ");
    Texts.WriteReal(W, FML(1.0E3, 8.192), 16); Texts.WriteLn(W);

    Texts.WriteString(W, "1.0E3 * 4.096 = ");
    Texts.WriteReal(W, FML(1.0E3, 4.096), 16); Texts.WriteLn(W);

    Texts.WriteString(W, "1.0E3 * 2.048 = ");
    Texts.WriteReal(W, FML(1.0E3, 2.048), 16); Texts.WriteLn(W);

    Texts.WriteString(W, "1.0E3 * 1.024 = ");
    Texts.WriteReal(W, FML(1.0E3, 1.024), 16); Texts.WriteLn(W);

    Texts.WriteString(W, "1.0E2 * 2.56 = ");
    Texts.WriteReal(W, FML(1.0E2, 2.56), 16); Texts.WriteLn(W);

    Texts.WriteString(W, "100.0 * 2.56 = ");
    Texts.WriteReal(W, FML(100.0, 2.56), 16); Texts.WriteLn(W);

    Texts.WriteString(W, "100.0 * 2.56 = ");
    Texts.WriteRealHex(W, FML(100.0, 2.56)); Texts.WriteLn(W);

    Texts.WriteString(W, "1000.0 * 2.56 = ");
    Texts.WriteReal(W, FML(1000.0, 2.56), 16); Texts.WriteLn(W);

    Texts.WriteString(W, "1.0E2 * 1.28 = ");
    Texts.WriteReal(W, FML(1.0E2, 1.28), 16); Texts.WriteLn(W);

    Texts.WriteString(W, "1.0E1 * 6.4 = ");
    Texts.WriteReal(W, FML(1.0E1, 6.4), 16); Texts.WriteLn(W);

    Texts.WriteString(W, "1.0E1 * 3.2 = ");
    Texts.WriteReal(W, FML(1.0E1, 3.2), 16); Texts.WriteLn(W);

    Texts.Append(Oberon.Log, W.buf)                          
  END Do;

BEGIN Texts.OpenWriter(W)
END TestFPMul.

TestFPMul.Do0  (with bug)
TestFPMul.Do   (corrected)

System.Free TestFPMul ~



More information about the Oberon mailing list