# [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 ~

```