Software - Math - Cube Root

This routine takes the cube root of a 24-bit unsigned number and returns the cube root and the remainder. Note that:

number = root * root * root + remainder

  • Inputs:
    • NUMH = bits 23-16 of the number
    • NUMM = bits 15-8 of the number
    • NUML = bits 7-0 of the number
  • Outputs:
    • ROOT = the cube root of NUM
    • REMH = bits 23-16 of the number
    • REMM = bits 15-8 of the number
    • REML = bits 7-0 of the number

This routine requires 3 bytes of temporary RAM storage: DIVL, DIVM, and DIVH.

;
    LDA #0
    STA ROOT
    STA REML
    STA REMM
    STA REMH
    STA DIVL
    STA DIVM
    STA DIVH
    JSR L1
L1  JSR L2
L2  JSR L3
L3  LDX #3
L4  ASL NUML
    ROL NUMM
    ROL NUMH
    ROL REML
    ROL REMM
    ROL REMH
    DEX
    BNE L4
;
; Note: carry is clear here!
;
    LDA REML
    SBC DIVL
    TAX
    LDA REMM
    SBC DIVM
    TAY
    LDA REMH
    SBC DIVH
    BCC L9
    STA REMH
    STY REMM
    STX REML
    ROL ROOT
    LDX #0
    LDA ROOT
    ASL
    BCC L5
    LDX #2
L5  ASL
    BCC L6
    INX
    CLC
L6  LDY #2
L7  ADC DIVL
    STA DIVL
    TXA
    ADC DIVM
    STA DIVM
    BCC L8
    INC DIVH
L8  ASL DIVL
    ROL DIVM
    ROL DIVH
    LDA ROOT
    LDX #0
    DEY
    BNE L7
    RTS
L9  LDX #2
L10 SEC
    LDA DIVL
    SBC ROOT
    STA DIVL
    BCS L12
    LDA DIVM
    BNE L11
    DEC DIVH
L11 DEC DIVM
L12 ASL ROOT
    DEX
    BNE L10
;
; undo the ASL ROOT from the previous loop
; (new ROOT = old ROOT * 2, not old ROOT * 4)
;
    ROR ROOT
    ASL DIVL
    ROL DIVM
    ROL DIVH
    ASL DIVL
    ROL DIVM
    ROL DIVH
    RTS

How it works:

Calculating a cube root digit-by-digit is similar to calculating a square root digit-by-digit. There are two main differences.

First, for a square root, two digits are shifted in (brought down); for a cube root, three digits are shifted in

Second, for a square root

divisor = 4 * root so far + 1

since:

(2*x+y)*(2*x+y) = 4*x*x + 4*x*y + y*y

For a cube root:

divisor = 12 * root so far * root so far + 6 * root so far + 1

since:

(2*x+y)*(2*x+y)*(2*x+y) = 2*x*x*x + 12*x*x*y + 6*x*y*y + y*y*y

Thus, the algorithm for the cube root is:

cube_root (n)
  dividend = n
  root = 0
  LOOP 8 TIMES
    dividend << 3
    NOTE: discard the lower 24 bits to produce the partial dividend
    partial_dividend = dividend >> 24
    divisor = 12 * root * root + 6 * root + 1
    IF partial_dividend >= divisor
      partial_dividend -= divisor
      root = root * 2 + 1
    ELSE
      root = root * 2
  remainder = partial_dividend
  RETURN (root, remainder)

Rather than calculate the square of the root, the assembly routine calculates a new divisor from the old divisor and the root so far. First, note that the first time through the loop, the divisor will be 1. Second, note that since the carry is clear when the comparison (and subtraction) is performed, the divisor stored in DIV is:

12 * root * root + 6 * root

rather than

12 * root * root + 6 * root + 1

Let r = old root

Thus,

old divisor = 12 * r*r + 6 * r

There are two cases:

Case #1: when the IF clause is true

new root = 2 * old root + 1 = 2 * r + 1

new divisor = 12 * new root * new root + 6 * new root
= 12 * (2 * r + 1) * (2 * r + 1) + 6 * (2 * r + 1)
= 48 * r*r + 48 * r + 12 + 12 * r +6
= 48 * r*r + 60 * r + 18

The new divisor is calculated as follows:

divisor = 2 * (divisor + 4 * new root)
= 2 * (12 * r*r + 6 * r + 4 * (2 * r + 1))
= 2 * (12 * r*r + 6 * r + 8 * r + 4)
= 24 * r*r + 28 * r + 8

divisor = 2 * (divisor + new root)
= 2 * (24 * r*r + 28 * r + 8 + 2 * r + 1)
= 48 * r + 60 * r + 18

Case #2: when the IF clause is false (i.e. when the ELSE clause is true)

new root = 2 * old root = 2 * r

new divisor = 12 * new root * new root + 6 * new root
= 12 * (2 * r) * (2 * r) + 6 * (2 * r)
= 48 * r*r + 12 * r

The new divisor is calculated as follows:

divisor = divisor - old root
= 12 * r*r + 6 * r - r
= 12 * r*r + 5 * r

divisor = divisor - 2 * old root
= 12 * r*r + 5 * r - 2 * r
= 12 * r*r + 3 * r

divisor = divisor * 4
= 48 * r*r + 12 * r

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License