Shop OBEX P1 Docs P2 Docs Learn Events
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)

Comments

  • evanhevanh Posts: 15,126
    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)

  • evanhevanh Posts: 15,126
    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
            add mult1, x    wc
            addx    mult2, #0
            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
    will go through your code (I am a littler slow) apreciate your reply
    Bob (WRD)

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

  • evanhevanh Posts: 15,126

    @"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.

    Is there access to the assembly code spin commands are using?

    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)

  • evanhevanh Posts: 15,126
    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.

  • evanhevanh Posts: 15,126

    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

  • evanhevanh Posts: 15,126

    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
    will go through your code (I am a littler slow) apreciate your reply
    Bob (WRD)

    You're using signed division operators in that code -- does it work if you change to unsigned division? +/

  • Jon
    Not sure how to make that change for MULDIV64 spin method. The debug statement is just showing what the MULDIV64 spin function is doing for descriptive purposes.
    regards
    Bob (WRD)

Sign In or Register to comment.