MULDIV64 appears to have 2% error — Parallax Forums

# MULDIV64 appears to have 2% error

When the following program is run the convert degree 45 is changed to angle32bit then converted back to a degree resulting in the returned value 44 . (45-44)/45*100 which is 2.2% error , Must be doing something wrong in this program:

{{MULDIV64(mult1,mult2,divisor) : quotient
Divide the 64-bit product of 'mult1' and 'mult2' by 'divisor', return quotient (unsigned operation)
allows unsigned integer values to be multiplied then divided then returning qutient value }}
CON
_clkfreq = 200_000_000 'must be greater than 10MHZ for debug
PUB main()| var01,var02,var03,angle32bit,degree,rotation,unitDegree
unitDegree := 360
rotation := \$FFFF_FFFF '2*pi = angle32bit maximum
var01 := \$FFFF_FFFF
var02 := \$FFFF_FFFF
var03 := \$FFFF_FFFF
debug(udec(var01),uhex(var01))
debug(udec(var02))
debug(udec(var03))
debug("var03 = (var01*var02)/var03) = ",udec(var03))
var01 := 45
var02 := rotation
var03 := unitDegree
'var01 must be less than var03 the quotient
debug(udec(var01))
debug(udec(var02))
debug(udec(var03))
angle32bit := MULDIV64(var01,var02,var03)
debug("angle32bit = \$FFFF_FFFF*45/360 = ",uhex(angle32bit),udec(angle32bit))
debug("angle32bit = (var01*var02)/var03) = ",udec(var03))
degree := MULDIV64(angle32bit,unitDegree,rotation)
debug(udec(angle32bit))
debug(udec(rotation))
debug(udec(unitDegree))
debug("(angle32bit*unitDegree)/rotation = ","degree = ",udec(degree))
repeat

Comment would be apreciated
Regards
Bob (WRD)

• edited 2021-07-10 00:45

Off-by-one is classic case of simple binary arithmetic rounding error. Not unexpected. If the range was 0-1 then it would always round down to zero. Up to 100% error. Because it always rounds down, even 0.9999 will still round down to zero. The fix, in hardware, is to always add a half-bit to the result. In software the workaround is to add half the divisor to the dividend before the division.

EDIT: The fix needs added internal to the muldiv64() method. Not hard. Something for Chip to sort out.
In the meantime, changing unitDegree from 360 to 360_000, and var01 to 45_000, might be helpful for you.

• Thanks Evanh will try
Regards
Bob (WRD)

• edited 2021-07-10 01:11

Here's what muldiv64 will be like:

```PUB  muldiv64( mult1, mult2, divisor ) : quotient
org
qmul    mult1, mult2
getqx   mult1
getqy   mult2
setq    mult2
qdiv    mult1, divisor
getqx   quotient
end
return quotient
```

And here's an example fix:

```PUB  muldiv65( mult1, mult2, divisor ) : quotient | x
org
qmul    mult1, mult2
mov x, divisor
shr x, #1
getqx   mult1
getqy   mult2
setq    mult2
qdiv    mult1, divisor
getqx   quotient
end
return quotient
```
• Thanks evanh I was trying :
angle32bit,rotation,unitDegree := 4_294_967_295/8,4_294_967_295,360
var01,var02,var03 := angle32bit,rotation,unitDegree
errorDegree := MULDIV64(angle32bit,360,1)
debug(udec(errorDegree))
errorDegree := errorDegree + (rotation/2)
degree := MULDIV64(errorDegree,1,rotation)
debug(udec(degree))
but this wouldn't work because values seem to error to 0
Bob (WRD)

• evanh
That code seems to work should this sugests the spin code should change or is there other factors to consider?
again Thanks
Bob (WRD)

• @"Bob Drury" said:
That code seems to work should this sugests the spin code should change or is there other factors to consider?

In most cases I think the compensating rounding to nearest is the right solution. Maybe in cases where the remainder is used then an uncompensated division would be needed instead.

Um, not completely sure of the question here. You can write pasm code inside spin methods like that yourself.

• evanh
If MULDIV64(mul1,mul2,divisor) is a spin command it must call a program to run in the byte code interpeter is there someway to see what action this interperter takes, most likely as you sugest some
assembly program is called. If MULDIV64 is in the byte code interperter I would assume this can't be modified being in propeller rom?
Not exceedingly important I have lot to learn but it is interesting.
Regards
Bob (WRD)

• edited 2021-07-11 01:51

Ooooh! Gotcha. Chip has long been refining Spin2 using the Pnut test bed, Spin2 has only just recently settled down. Spin2 is not in ROM with the Prop2. Different to the Prop1.

He hasn't yet but he may well publish the built-in method sources. Some will be direct mapped machine code instructions, some will be Pasm code like I did, whiles others will be plain Spin code or a mix.

EDIT: Completely wrong about the published sources. See below.

• Thanks for reply. This was interesting.
Regards
Bob (WRD)

• I never thought about this. I was writing code for the Si570 frequency synthesizer chip and got "discontinuities" at certain frequencies and never knew why. I will be sure to try the MULDIV65 assembly code snippet. Thanks for the insight.

• My pleasure. It is satisfying to kill those glitches. • @"Bob Drury"
If you download the PNut archive ypu'll find inside the "Spin2_interpreter.spin2" which is the PASM source of the bytecode interpreter.
Unfortunately PNut dasn't compile this file but it have internally the binary blob that is appended to the generated bin that get then transferred into P2 ... so any change to the above file will not make any change in the interpreter ... it is there only for reference.

Propeller tool even doesn't include the Spin2 interpreter source in its distribution so I presume it behave as PNut

• Oh, hehe, all pasm coded.

• @"Bob Drury" said:
Thanks evanh I was trying :
angle32bit,rotation,unitDegree := 4_294_967_295/8,4_294_967_295,360
var01,var02,var03 := angle32bit,rotation,unitDegree
errorDegree := MULDIV64(angle32bit,360,1)
debug(udec(errorDegree))
errorDegree := errorDegree + (rotation/2)
degree := MULDIV64(errorDegree,1,rotation)
debug(udec(degree))
but this wouldn't work because values seem to error to 0