z80:Floating Point

From Learn @ Cemetech
Jump to: navigation, search

Floating Point Tutorial

Although integer math may seem more natural for us when we program the Z80, floating point math that works with fractional numbers is not much more difficult. The only change is in representation and how we program the handling of the data. After reading this, hopefully the reader will not feel limited to integer math and not limited to even the representations here. Don't just use a 'sign bit' if you think it would be more mathematically sound to have a bit for additive inverses and multiplicative inverses. Add complex arithmetic, or try making Rational representations for sometimes many, many more bits of precision and a smaller risk for roundoff errors.

Format

The typical format includes a sign bit (additive inverse, positive/negative) an exponent, and a mantissa. TI, for example, uses 1 bit for sign, 8 bits for the exponent, and 7 bytes storing 12 base 10 digits. If the bytes were 0083 31 41 59 26 53 58 98, it would be read as sign=0, exponent=83h-80h=3, mantissa=3.1415926535898, so the value represented is 3.1415926535898*10^^3^^=3141.5926535898.

Generally, the math has a wider range and better precision if we use a base of power of 2. Depending on the usage, we tend to try to pack all of the data to fill 8-bit multiples. TI uses an extra 8 bits to store the 1 sign bit, but in their application, they also use that byte to store variable type. They actually took advantage of unused bits that had to be there anyways.

Sign Bit

The sign bit is just a 0 or 1 that determines if a number is positive or negative.

Exponent

To decide a good range for the exponent, you will need to pick a base. TI used 10, but generally, 2 might be better or 256 so that we use whole bytes as digits. Essentially, we will use something like 2^^exponent^^*mantissa. It is generally easier to make the middle number in your range as 0. If you are using an 8-bit exponent, 80h=0 for the exponent.

Mantissa

The mantissa is best kept "normalized" by making sure we don't have leading zeroes. If we are using a base of 2, then a mantissa of %0011111100000000 is usually not what we want. Instead, we shift it left twice, which means we decrement the exponent by 2.

Examples

If we decided to have 1 bit for sign, 7 bits for the exponent, and 16 bits for the mantissa, using base 2, then the following would represent 1:

   %01000000   ;sign=0, exponent=64 (treated as 0)
   %1000000000000000 ;mantissa=1.0

And likewise, 2 would be just multiplying by 2 which is the same as increasing the exponent by 1:

   %01000001   ;sign=0, exponent=65 (65-64=1)
   %1000000000000000 ;mantissa=1.0
   1.0*2^1=2

So if we think of the exponent as the location of the decimal, 2 is 10.00000000000000 and 1 is 1.000000000000000. If we want to perform some arithmetic...

Addition and Subtraction

This can be a bit difficult because we have to keep track of the sign of the inputs and the result, but we also have to line up the 'decimal points' (the exponent). In the above examples for the 24-bit floats for 1.0 and 2.0, to line up the decimals, we need to rotate the mantissa of 1 to the right (and likewise, increment the exponent:

   %01000001   ;sign=0, exponent=65 (treated as 1)
   %0100000000000000 ;mantissa=0.5
   0.5*2^1=1

Note that 1 is now represented as a denormalized number, but we can directly add the numbers by adding their mantissas:

   %01000001   ;sign=0, exponent=65 (treated as 1)
   %1100000000000000 ;mantissa=1.5
   1.5*2^1=3

If there was overflow, we would have to rotate the result right to get the upper bit of the result back in the number and then increment the exponent. For example, if we added 2 to this, we would get %0100000000000000 as the 16-bit addition, but the overflow (carry flag) would be a 1, so we would rotate it back in, dropping the last bit (in our case, a 0):

   %01000010   ;sign=0, exponent=66 (treated as 2)
   %1010000000000000 ;mantissa=1.25
   1.25*2^2=5


Adding two negative numbers -a+-b is the same as -(a+b) so it is just like adding two positive numbers and you don't need to change the sign. Adding a positive and negative number will require a subtraction of their absolute values. For example, 2+-1 would require rotating the mantissas as in addition, but then subtract %1000...-%0100... to get %0100... which needs to be renormalised. In this case, it is still a positive result. If there was overflow (on the z80, the c flag is set), then our result is negative, but we also need to perform a 2s compliment on the mantissa (that is, negate it as if it were an integer). So in the case of 1-2, we would get: %01000001 1100000000000000. 2s compliment would return $01000001 0100000000000000 and then we set the sign as negative and renormalise the number, getting the expected -1. In the case where the first number is negative and the second is positive, it would be easiest to swap their order for subtraction instead of making a separate reverse-subtract routine.

Multiplication and Division

Multiplication and division are much easier than addition and subtraction. When multiplying or dividing two numbers, you just need to use XOR logic on the signs and perform basically integer math on the mantissas. If both numbers are negative, their sign bits will be 1 and 1 xor 1=0, which is positive. Likewise, 1 xor 0=1, 0 xor 1=1, 0 xor 0=0. To multiply A and B, add their exponents together, to divide, subtract the exponent of B from A (taking care of underflow/overflow, accordingly which is a simple check of the c flag).

Now you have the sign and exponent of the result, but you still need to get the mantissa. For multiplication, just multiply the two mantissas and take the upper bits for the result, rounding as seen fit. For division, it is a slightly different algorithm than integer division (or so it may seem). Since both numbers are normalized, you don't need to rotate bits into an accumulator. Instead, check if B<=A. If it is, perform B-A as an integer subtraction, and set the upper bit of the result, else leave it reset. Then shift B to the right and repeat the process, setting or resetting the next bits at each iteration. For a 64-bit mantissa, this requires 64 tests. As well, for best accuracy, A and B would need additional 64 trailing zero bits making subtraction 128-bits. Since it is integer subtraction, it is still fairly trivial.

Square Root

For square roots, exponents are divided by 2. If the exponent is odd, add 1 to it to make it even and rotate the mantissa right. From there, the mantissa can be treated as an integer to take the square root of.

Modulo

For many trig applications, you will need to take input mod pi. To do this, check the exponent. If it is less than 1, it is smaller than pi, so you are finished. if it is greater than or equal to 1, perform 3-exponent and this will be the number of iterations required for the algorithm about to be described.

If the mantissa is greater than or equal to pi, subtract pi. Then, shift pi to the right (dividing it by 2) and repeat the needed number of times.

For general modulo, just replace 'pi' with any number and check the exponent for being less than the exponent of the modulus.

24-bit Floating Point

24-bit floats are excellent because the math can be contained in the registers. These currently use 7-bit exponents such that 0=0 and %1111111 = 3Fh = -1

RAM equates

The order in which the RAM values are stored are important:

   f24_exp2:
   	.db 0
   f24_exp1:
   	.db 0
   f24_exp4:
   	.db 0
   f24_exp3:
   	.db 0
   f24_exp6:
   	.db 0
   f24_exp5:
   	.db 0
   f24_man1:
   	.dw 0
   f24_man2:
   	.dw 0
   f24_man3:
   	.dw 0
   f24_man4:
   	.dw 0
   f24_man5:
   	.dw 0
   f24_man6:
   	.dw 0
   tempbyte1:
   	.db 0
   tempword1:
   	.dw 0

Addition/Subtraction

   Float24Add:
   ;BHL+CDE
       ld a,c
       xor 40h
       ld c,a
   	rlc c
       ld a,b
       rla
   	rlc b
       xor 80h
   	sub c
   	jr z,f24shifted
   	jr nc,f24shiftDE
       rra
   	cp -16
   	jr nc,$+6
   	ld a,c \ ex de,hl \ rrca \ ret
   	srl h \ rr l
   	inc a \ jr nz,$-5
   	jr nc,$+3 \ inc hl
   	ld a,b \ and 1 \ ld b,a \ ld a,c \ and $FE \ xor 80h \ or b \ jp f24shifted+1
   f24ShiftDE:
   	rra
   	cp 16
   	jr c,$+5
   	ld a,b \ rrca \ ret
   	srl d \ rr e
   	dec a \ jr nz,$-5
   	jr nc,$+3 \ inc de
   	ld a,c \ and 1 \ ld c,a \ ld a,b \ and $FE \ or c \ ld c,a
   f24Shifted:
   	ld a,b \ rrca \ ld b,a \ ld a,c \ rrca \ ld c,a \ xor b \ ld a,b \ jp m,f24sub
   	add hl,de \ ret nc
   	ld c,0 \ inc c \ inc c \ rr h \ rr l \ jr nc,$+8 \ inc l \ jr nz,$+5 \ inc h
   	jr z,$-12
   	rlca \ add a,c \ jp p,$+10
   	jr c,$+7 \ bit 6,b \ rrca \ jr z,$+4
       rrca \ ret
   SetInf:
   	ld hl,$FFFF
   	rla
   	ld a,h
   	rra
   	ld b,a
   	ret
   f24Sub:
   	sbc hl,de
   	jr nc,normalise24
   	xor 80h
       ld b,a
       xor a \ sub l \ ld l,a
       sbc a,a \ sub h \ ld h,a
       ld a,b
   normalise24:
   	bit 7,h \ ret nz
   	ld a,b \ add a,a
   	cp $82 \ ret z
   	dec a \ dec a
   	add hl,hl
   	rlc b \ rra \ ld b,a
   	jp normalise24
   
   Float24Sub:
   ;BHL-CDE
       ld a,80h
       xor c
       ld c,a
       jp Float24Add

Multiplication

   Float24Mul:
   ;BHL*CDE -> AHL
       ld a,b \ xor c
       and 80h
       push af
       sla b \ sra b
       sla c \ sra c
       ld a,b
       add a,c
       pop bc
       jp m,$+10
       cp 64
       jr nc,SetInf-2
       jp $+7
       cp -64
       jr c,SetInf-2
   ;B has the right sign
       and 7Fh
       or b
       push af
       ld b,h
       ld c,l
       call BC_Times_DE
       bit 7,l
       ld l,h
       ld h,b
       jr z,$+3
       inc hl
       pop af
       ret
   BC_Times_DE:
   ;BHLA is the result
       ld a,b
       or a
       ld hl,0
       ld b,h
   ;1
       add a,a
       jr nc,$+4
       ld h,d
       ld l,e
   ;2
       add hl,hl
       rla
       jr nc,$+4
       add hl,de
       adc a,b
   ;227+10b-7p
       add hl,hl
       rla
       jr nc,$+4
       add hl,de
       adc a,b
   
       add hl,hl
       rla
       jr nc,$+4
       add hl,de
       adc a,b
   
       add hl,hl
       rla
       jr nc,$+4
       add hl,de
       adc a,b
   
       add hl,hl
       rla
       jr nc,$+4
       add hl,de
       adc a,b
   
       add hl,hl
       rla
       jr nc,$+4
       add hl,de
       adc a,b
   
       add hl,hl
       rla
       jr nc,$+4
       add hl,de
       adc a,b
   
   ;===
   ;AHL is the result of B*DE*256
       push hl
       ld h,b
       ld l,b
       ld b,a
       ld a,c
       ld c,h
   ;1
       add a,a
       jr nc,$+4
       ld h,d
       ld l,e
   ;2
       add hl,hl
       rla
       jr nc,$+4
       add hl,de
       adc a,c
   ;227+10b-7p
       add hl,hl
       rla
       jr nc,$+4
       add hl,de
       adc a,c
   
       add hl,hl
       rla
       jr nc,$+4
       add hl,de
       adc a,c
   
       add hl,hl
       rla
       jr nc,$+4
       add hl,de
       adc a,c
   
       add hl,hl
       rla
       jr nc,$+4
       add hl,de
       adc a,c
   
       add hl,hl
       rla
       jr nc,$+4
       add hl,de
       adc a,c
   
       add hl,hl
       rla
       jr nc,$+4
       add hl,de
       adc a,c
   
       pop de
   ;Now BDE*256+AHL
       ld c,a
       ld a,l
       ld l,h
       ld h,c
       add hl,de
       ret nc
       inc b
   ;BHLA is the 32-bit result
       ret

Division

Note that this routine currently does not use 32-bit subtractions, so it can get bad accuracy.

   Float24Div:
   ;BHL/CDE -> AHL
       ld a,b \ xor c
       and 80h
       push af
       sla b \ sra b
       sla c \ sra c
       ld a,b
       sub c
       pop bc
       jp m,$+11
       cp 64
       jp nc,SetInf-2
       jp $+7
       cp -64
       jp c,SetInf-2
   ;B has the right sign
       and 7Fh
       or b
       push af
   
   
       ld bc,0
       or a \ sbc hl,de \ jr c,$+7 \ set 7,b \ jp $+4 \ add hl,de \ srl d \ rr e
       or a \ sbc hl,de \ jr c,$+7 \ set 6,b \ jp $+4 \ add hl,de \ srl d \ rr e
       or a \ sbc hl,de \ jr c,$+7 \ set 5,b \ jp $+4 \ add hl,de \ srl d \ rr e
       or a \ sbc hl,de \ jr c,$+7 \ set 4,b \ jp $+4 \ add hl,de \ srl d \ rr e
       or a \ sbc hl,de \ jr c,$+7 \ set 3,b \ jp $+4 \ add hl,de \ srl d \ rr e
       or a \ sbc hl,de \ jr c,$+7 \ set 2,b \ jp $+4 \ add hl,de \ srl d \ rr e
       or a \ sbc hl,de \ jr c,$+7 \ set 1,b \ jp $+4 \ add hl,de \ srl d \ rr e
       or a \ sbc hl,de \ jr c,$+7 \ set 0,b \ jp $+4 \ add hl,de \ srl d \ rr e
       or a \ sbc hl,de \ jr c,$+7 \ set 7,c \ jp $+4 \ add hl,de \ srl d \ rr e
       or a \ sbc hl,de \ jr c,$+7 \ set 6,c \ jp $+4 \ add hl,de \ srl d \ rr e
       or a \ sbc hl,de \ jr c,$+7 \ set 5,c \ jp $+4 \ add hl,de \ srl d \ rr e
       or a \ sbc hl,de \ jr c,$+7 \ set 4,c \ jp $+4 \ add hl,de \ srl d \ rr e
       or a \ sbc hl,de \ jr c,$+7 \ set 3,c \ jp $+4 \ add hl,de \ srl d \ rr e
       or a \ sbc hl,de \ jr c,$+7 \ set 2,c \ jp $+4 \ add hl,de \ srl d \ rr e
       or a \ sbc hl,de \ jr c,$+7 \ set 1,c \ jp $+4 \ add hl,de \ srl d \ rr e
       or a \ sbc hl,de \ jr c,$+4 \ set 0,c
       ld h,b \ ld l,c
       pop bc
       jp normalise24

log base 2

   Float24Lg:
   ;Inputs: AHL is the 24-bit float
   ;     bit 7,a is the sign
   ;     bits 0~6 are the signed exponent
   ;     HL is the mantissa
   ;Output:
   ;    nc flag set if result would be complex
   ;    c if real result
   ;    BHL is the computed result if real
       or a
       jp p,$+9
       ld a,-1
       ld hl,0
       ret
       add a,a
       sra a
   ;A is now the value that must be added to the final result
       push af
       exx \ ld hl,0 \ exx
   FloatLogLoop:
   ;Step 1 : check if HL >=AAAA
   step1:
       ld bc,$AAAA
       xor a
       sbc hl,bc
       add hl,bc
       jp c,step2
       exx
       ld bc,27200
       add hl,bc
       exx
   ;multiply HL by 3/4
       ld d,h
       ld e,l
       add hl,hl \ rla
       add hl,de \ adc a,0
       rra \ rr h \ rr l
       rra \ rr h \ rr l
       jr nc,step1+3
       inc hl
       jp step1+3
   step2:
       ld bc,$9249
       xor a
       sbc hl,bc
       add hl,bc
       jp c,step3
       exx
       ld bc,12625
       add hl,bc
       exx
   ;multiply HL by 7/8
       ld d,h
       ld e,l
       add hl,hl \ rla
       add hl,hl \ rla
       add hl,hl \ rla
       sbc hl,de \ sbc a,0
       rra \ rr h \ rr l
       rra \ rr h \ rr l
       rra \ rr h \ rr l
       jr nc,step2+3
       inc hl
       jp step2+3
   step3:
       ld bc,$8888
       xor a
       sbc hl,bc
       add hl,bc
       jp c,step4
       exx
       ld bc,6102
       add hl,bc
       exx
   ;multiply HL by 7/8
       ld d,h
       ld e,l
       add hl,hl \ rla
       add hl,hl \ rla
       add hl,hl \ rla
       add hl,hl \ rla
       sbc hl,de \ sbc a,0
       rra \ rr h \ rr l
       rra \ rr h \ rr l
       rra \ rr h \ rr l
       rra \ rr h \ rr l
       jr nc,step3+3
       inc hl
       jp step3+3
   step4:
       ld bc,$8421
       xor a
       sbc hl,bc
       add hl,bc
       jp c,step5
       exx
       ld bc,3002
       add hl,bc
       exx
   ;multiply HL by 31/32
   ;multiply HL by 31
       push hl
       srl h \ rr l \ rra
       srl h \ rr l \ rra
       srl h \ rr l \ rra
       ld d,a
       ld a,h \ ld h,l \ ld l,d
       pop de
       sbc hl,de \ sbc a,0
   ;now divide by 32
   ; 7 shifts right
       add hl,hl \ rla
       add hl,hl \ rla
       add hl,hl \ rla
       bit 7,l
       ld l,h
       ld h,a
       jr z,step4+3
       inc hl
       jp step4+3
   
   step5:
       ld bc,$8208
       xor a
       sbc hl,bc
       add hl,bc
       jp c,step6
       exx
       ld bc,1489
       add hl,bc
       exx
   ;multiply HL by 63/64
   
   ;multiply HL by 63
       push hl
       srl h \ rr l \ rra
       srl h \ rr l \ rra
       ld d,a
       ld a,h \ ld h,l \ ld l,d
       pop de
       sbc hl,de \ sbc a,0
   ;now divide by 64
   ; 7 shifts right
       add hl,hl \ rla
       add hl,hl \ rla
       bit 7,l
       ld l,h
       ld h,a
       jr z,step5+3
       inc hl
       jp step5+3
   
   step6:
       ld bc,$8102
       xor a
       sbc hl,bc
       add hl,bc
       jp c,step7
       exx
       ld bc,742
       add hl,bc
       exx
   ;multiply HL by 127
       ld d,h
       ld e,l
       srl h \ rr l
       ld a,h \ ld h,l \ ld l,0 \ rr l
       sbc hl,de \ sbc a,0
   ;now divide by 128
   ; 7 shifts right
       add hl,hl \ rla
       bit 7,l
       ld l,h
       ld h,a
       jr z,step6+3
       inc hl
       jp step6+3
   
   
   step7:
       ld bc,$8080
       xor a
       sbc hl,bc
       add hl,bc
       jp c,step8
       exx
       ld bc,370
       add hl,bc
       exx
   ;multiply HL by 255
       ld d,h
       ld e,l
       ld a,h \ ld h,l \ ld l,0
       sbc hl,de \ sbc a,0
   ;now divide by 256
       bit 7,l
       ld l,h
       ld h,a
       jr z,step7+3
       inc hl
       jp step7+3
   
   
   step8:
       exx \ ld bc,185 \ exx
       rrc c
       sbc hl,bc
       add hl,bc
       jp c,step9
       exx
       add hl,bc
       exx
   ;multiply HL by 511/512
       xor a
       ld e,h
       ld d,a
       cp L
       jr z,$+3
       inc de
       add hl,hl \ rla
       sbc hl,de \ sbc a,0
       rra \ rr h \ rr l
       jr nc,step8+7
       inc hl
       jp step8+7
   
   step9:
       rrc c
       sbc hl,bc
       add hl,bc
       jp c,step10
       exx
       ld c,92
       add hl,bc
       exx
   ;multiply HL by 1023/1024
       xor a
       ld e,h
       ld d,a
       cp L
       jr z,$+3
       inc de
       add hl,hl \ rla
       add hl,hl \ rla
       sbc hl,de \ sbc a,0
       rra \ rr h \ rr l
       rra \ rr h \ rr l
       jr nc,step9+2
       inc hl
       jp step9+2
   
   step10:
       rrc c
       sbc hl,bc
       add hl,bc
       jp c,step11
       exx
       ld c,46
       add hl,bc
       exx
   ;multiply HL by 1023/1024
       xor a
       ld e,h
       ld d,a
       cp L
       jr z,$+3
       inc de
       add hl,hl \ rla
       add hl,hl \ rla
       add hl,hl \ rla
       sbc hl,de \ sbc a,0
       rra \ rr h \ rr l
       rra \ rr h \ rr l
       rra \ rr h \ rr l
       jr nc,step10+2
       inc hl
       jp step10+2
   
   step11:
       rrc c
       sbc hl,bc
       add hl,bc
       jp c,step12
       exx
       ld c,23
       add hl,bc
       exx
   ;multiply HL by 2047/2048
       xor a
       ld e,h
       ld d,a
       cp L
       jr z,$+3
       inc de
       add hl,hl \ rla
       add hl,hl \ rla
       add hl,hl \ rla
       add hl,hl \ rla
       sbc hl,de \ sbc a,0
       rra \ rr h \ rr l
       rra \ rr h \ rr l
       rra \ rr h \ rr l
       rra \ rr h \ rr l
       jr nc,step11+2
       inc hl
       jp step11+2
   
   step12:
   ;    rrc c
   ;    sbc hl,bc
   ;    add hl,bc
   ;    jp c,step13
   ;    exx
   ;    ld c,12
   ;    add hl,bc
   ;    exx
   
   step13:
   logfin:
   
       pop af
       exx
       ld bc,$7F07
       ld d,a
       ld a,b
       ret z
       ld a,d
       ld e,0
   
       jp p,$+7
       ld c,$87
       neg
       dec c
       add a,a \ jp p,$-2
       ld d,a
       jp Float24Add

Square Root

   Float24Sqrt:
   ;Input:
   ;  AHL is the 24-bit float of which to find the square root
   ;Output:
   ;  BHL is the resulting 24-bit square root
       or a
       jp p,$+9
       ld a,-1
       ld hl,0
       ret
   
       rra
       jr c,$+6
       srl h
       rr l
       add a,a
       add a,a
       sra a
       rra
       ld b,a
   SqrtHL_prec16:
   ;input: HL
   ;Output: DE
   ;247 bytes
   ;1269 t-states worst case
   ;Average is about 1219 t-states
       xor a
       ld d,a
   
       ld c,l
       ld l,h
       ld h,a
   
       add hl,hl
       add hl,hl
       cp h
       jr nc,$+5
       dec h
       ld a,4
   
       add hl,hl
       add hl,hl
       ld e,a
       sub h
       jr nc,$+6
       cpl
       ld h,a
       inc e
       inc e
   
       ld a,e
       add hl,hl
       add hl,hl
       add a,a
       ld e,a
       sub h
       jr nc,$+6
       cpl
       ld h,a
       inc e
       inc e
   
       ld a,e
       add hl,hl
       add hl,hl
       add a,a
       ld e,a
       sub h
       jr nc,$+6
       cpl
       ld h,a
       inc e
       inc e
   
       ld a,e
       ld l,c
   
       add hl,hl
       add hl,hl
       add a,a
       ld e,a
       sub h
       jr nc,$+6
       cpl
       ld h,a
       inc e
       inc e
   
       ld a,e
       add hl,hl
       add hl,hl
       add a,a
       ld e,a
       sub h
       jr nc,$+6
       cpl
       ld h,a
       inc e
       inc e
   
       ld a,e
       add a,a \ ld e,a
       add hl,hl
       add hl,hl
       jr nc,$+6
       sub h \ jp $+6
       sub h
       jr nc,$+6
       inc e \ inc e
       cpl
       ld h,a
   
   
       ld a,l
       ld l,h
       add a,a
       ld h,a
       adc hl,hl
       adc hl,hl
       sll e \ rl d
       sbc hl,de
       jr nc,$+3
       add hl,de
       sbc a,a \ add a,a \ inc a \ add a,e \ ld e,a
   
   ;iteration 9
       add hl,hl \ add hl,hl
       sll e \ rl d
       sbc hl,de
       jr nc,$+3
       add hl,de
       sbc a,a \ add a,a \ inc a \ add a,e \ ld e,a
   
       add hl,hl \ add hl,hl
       sll e \ rl d
       sbc hl,de
       jr nc,$+3
       add hl,de
       sbc a,a \ add a,a \ inc a \ add a,e \ ld e,a
   
       add hl,hl \ add hl,hl
       sll e \ rl d
       sbc hl,de
       jr nc,$+3
       add hl,de
       sbc a,a \ add a,a \ inc a \ add a,e \ ld e,a
   
       add hl,hl \ add hl,hl
       sll e \ rl d
       sbc hl,de
       jr nc,$+3
       add hl,de
       sbc a,a \ add a,a \ inc a \ add a,e \ ld e,a
   
       add hl,hl \ add hl,hl
       sll e \ rl d
       sbc hl,de
       jr nc,$+3
       add hl,de
       sbc a,a \ add a,a \ inc a \ add a,e \ ld e,a
   
       add hl,hl \ add hl,hl
       sll e \ rl d
       sbc hl,de
       jr nc,$+3
       add hl,de
       sbc a,a \ add a,a \ inc a \ add a,e \ ld e,a
   
       sll e \ rl d
       add hl,hl \ add hl,hl    ;up to 17 bits
       jr nc,$+8
           sbc hl,de
           inc e
           jp $+13
       sbc hl,de
       jr nc,$+3
       add hl,de
       sbc a,a \ add a,a \ inc a \ add a,e \ ld e,a
   
       srl d \ rr e \ scf
       sbc hl,de
       ccf
       ex de,hl
       adc hl,hl
       ret

Float To Signed Int

   FloatToSInt:
   ;Input: BHL
   ;Output : DE
   ;Overflow is handled as 0x7FFF or 0xFFFF
       ld de,0
       ld a,b
       and 80h
       or d
       ld d,a
       ld a,b
       add a,a
       ret m
       dec de
       rr d \ rr e
       sub 16
       ret nc
       cpl
       ld d,h \ ld e,l
       ld b,a
       sra d \ rr e
       djnz $-4
       ret

Float to Unsigned Int

   FloatToUInt:
   ;Input: BHL
   ;Output : DE
   ;Overflow is handled as 0xFFFF
       ld de,0
       ld a,b
       add a,a
       ret m
       dec de
       sub 16
       ret nc
       cpl
       ld d,h \ ld e,l
       ld b,a
       srl d \ rr e
       djnz $-4
       ret

Atan2 (CORDIC)

This is getting questionable accuracy, but it is a CORDIC implementation of arctangent using an x and y input:

   Float24Atan2:
   ;Inputs:
   ; BHL=X
   ; CDE=Y
   ;Outputs:
   ; AHL is the angle (64 corresponds to 90 degrees)
   ; CDE can be multiplied by 0.60725293500888 (7F 9B75)to get sqrt(x^2+y^2).
       ld (f24_exp2),bc
       ld (f24_man1),hl
       ld (f24_man2),de
       xor a
       ld h,a
       ld l,a
       ld (f24_man3),hl
       ld (f24_exp3),a
       ld (f24_man4),hl
       ld (f24_exp4),a
       ld hl,Float24_arctantable
       ld (tempword1),hl
   f24_atan2loop:
       ld (tempbyte1),a
       add a,c
       and $7F
       ld c,a
   ;add CDE to f1, store to f5
       ld hl,(f24_man1)
       call Float24Add
       ld (f24_exp5),a
       ld (f24_man5),hl
   
       ld bc,(f24_exp2)
       ld a,(tempbyte1)
       add a,b
       and $7F
       bit 7,b
       jr z,$+4
       or 80h
       ld d,a
       ld b,c
       ld c,d
       ld de,(f24_man1)
       ld hl,(f24_man2)
   
       bit 7,b
       jr nz,$+2+3+3+3+3+1+3
       call Float24Sub
       ld (f24_man2),hl
       ld (f24_exp2),a
       ld hl,(tempword1)
       ld c,(hl)
       jp adjustangle
   
       call Float24Add
       ld (f24_man2),hl
       ld (f24_exp2),a
       ld a,(tempbyte1)
       ld hl,(tempword1)
       ld c,(hl) \ set 7,c
   AdjustAngle:
       inc hl
       ld e,(hl) \ inc hl
       ld d,(hl) \ inc hl
       ld (tempword1),hl
       ld hl,(f24_man4)
       ld a,(f24_exp4)
       ld b,a
       call Float24Add
       ld (f24_man4),hl
       ld (f24_exp4),a
   
       ld hl,(f24_man5)
       ld a,(f24_exp5)
       ld (f24_man1),hl
       ld (f24_exp1),a
   
       ld bc,(f24_exp2)
       ld de,(f24_man2)
       ld a,(tempbyte1)
       dec a
       cp -8
       jp nz,f24_atan2loop
       ld hl,(f24_man4)
       ld a,(f24_exp4)
       ld bc,(f24_exp1)
       ld de,(f24_man1)
       ret
   
   Float24_arctantable:
    .db 5   \ .dw $8000
    .db 4   \ .dw $9720
    .db 3   \ .dw $9FB4
    .db 2   \ .dw $A222
    .db 1   \ .dw $A2C3
    .db 0   \ .dw $A2EC
    .db -1  \ .dw $A2F6
    .db -2  \ .dw $A2F9    ;note that from here, these are essentially the same
    .db -3  \ .dw $A2F9
    .db -4  \ .dw $A2F9
    .db -5  \ .dw $A2FA
    .db -6  \ .dw $A2FA
    .db -7  \ .dw $A2FA
    .db -8  \ .dw $A2FA
    .db -9  \ .dw $A2FA
    .db -10 \ .dw $A2FA

32-bit Floating Point

80-bit Floating Point

Conclusion

For more routines, until this page can be filled in more, see this Floating Point Library