Hey guys,
I have an optimization problem I need to work out for a project I'm working on....
Can anyone suggest or has code to convert Fahrenheit to Celcius optimized for size?
It's actually not for x86 but finding the best method would be a good start.
Thanks :biggrin:
fld/fild,fadd,fmul,fstp/fistp with REAL4 constants?
It's actually RISC PIC16F1784
http://ww1.microchip.com/downloads/en/DeviceDoc/40001637C.pdf
I'm guessing I can only get help here finding a best higher level method or some doc reference
sorry, I didn't notice "not for x86".
hard to solve with these device, even no multiplication available.
It's been a while since I've done this, not at all with RISC. It is for a small range 150-430, I have read that you can get a rough estimate
Subtract 30 then divide by 2....
Not sure how that would work with a small table (assuming that is possible for RISC lol).
only needs to be integer accuracy, I'm guessing give or take 1 degree for a few readings wouldn't matter too much
Actual formula is (F-32)*5/9=C
hi Peter
haven't written any code for that processor, yet
but, it doesn't appear to have multiply or divide
i can write x86 code, and you can convert it to pic16 equiv :biggrin:
first thing, of course, is to subtract out 32 degrees
mov ax,TempF
sub ax,32
what's left is, you have to multiply by 5 and divide by 9
or, multiply by 0.5555555555555
multiplying by 5 is simple with shift and add
mov dx,ax
shl dx,1
shl dx,1
add ax,dx
i can think of 3 ways to get it done (i'm sure there are more)
1) multiply by 5 with shift/add, then divide with shift and add (multiply by 2^n/9)
2) write a general purpose long-division routine - this might be a good solution if it were also to be used elsewhere
3) multiply by 0.555555555555 x 2^n (.5555555555 = 5/9 = 1/1.8 )
the last option is probably going to make the smallest code
16-bit registers, so use 2 of them for a 32-bit result, the high word will be the answer
the low word will be the fraction, which you may want to throw away
so - the constant will be 36409
if you multiply by that value, 32-bit result will be the tempurature in C, x 65536
Quote from: oex on October 03, 2015, 01:54:38 AMIt is for a small range 150-430, I have read that you can get a rough estimate
Subtract 30 then divide by 2....
gives a max. relative error of ca. 10% for that input range
multiply by 36409 - 1000 1110 0011 1001
mov dx,ax ;CX:DX = bit 0
mov cx,0
shl ax,1 ;bit 1
shl ax,1 ;bit 2
shl ax,1 ;bit 3
add dx,ax ;CX:DX = bit 3 + bit 1
shl ax,1 ;bit 4
add dx,ax ;CX:DX = bit 4 + bit 3 + bit 1
and so on
at some point, you'll want a 4th register for AX to overflow into
and, you'll want to
add dx,ax
adc cx,bx
notice, that you can put the number 36409 in a constant and use a loop to multiply :t
you put a single bit in a register
test against the constant - if the constant has that bit set, you add with carry
shift the single bit left and repeat the test and conditional add 16 times
Quote from: qWord on October 03, 2015, 02:09:22 AM
Quote from: oex on October 03, 2015, 01:54:38 AMIt is for a small range 150-430, I have read that you can get a rough estimate
Subtract 30 then divide by 2....
gives a max. relative error of ca. 10% for that input range
Haha sorry QWord, as I say it's been a while, that is a bit too rough, a degree at most either way would be acceptable
Thanks for your solutions Dave, fortunately I don't have to translate this, just trying to help a friend with some optimization, may have a followup or two
here is some x86 code to get you started
it works ok with positive result values
code be modified to handle lower temps (negative), but you don't care
i tested it with a couple input values:
32 -> 0
212 -> 100
;###############################################################################################
INCLUDE \Masm32\Include\Masm32rt.inc
;###############################################################################################
.DATA
wConstant dw 36409
;###############################################################################################
.CODE
;***********************************************************************************************
main PROC
mov ax,32
call F2C
print ustr$(eax),13,10
print chr$(13,10)
inkey
INVOKE ExitProcess,0
main ENDP
;***********************************************************************************************
F2C PROC
mov dx,0 ;DX:AX = TempF
sub ax,32
sbb dx,0 ;DX:AX = TempF - 32
mov cx,0
mov esi,0 ;ESI:CX = result accumulator (only lower 16 bits of ESI used)
mov bx,1 ;shifted test bit
mov edi,16 ;EDI = loop count
jmp short Loop01
Loop00: shl bx,1
shl ax,1
rcl dx,1
Loop01: test bx,wConstant
jz Loop02
add cx,ax
adc esi,edx
Loop02: dec edi
jnz Loop00
xchg eax,esi
and eax,0FFFFh ;result in AX
ret
F2C ENDP
;###############################################################################################
END main
EDIT: oh....
at the end, you could test the high bit of the result fraction to round to the nearest :P
shl cx,1
adc esi,0
xchg eax,esi
and eax,0FFFFh
ret
Here's a hokey way to do it, has these advantages,
- shortest I can think of (as requested by OP)
- uses no mult, div, or other instructions that might not be on RISC like sbb etc
- gives closest integer answer
- easy to understand (mindless in fact)
and these disadvantages,
- could take very long time (up to dozens of nanoseconds)
- Fahrenheit must be >= 32
; Fahrenreit to Centigrade, gives closest integer answer, no mult used, hokey
INCLUDE \Masm32\Include\Masm32rt.inc
FAHR = 37 ; must be >= 32
.code
start:
mov eax, FAHR
sub eax, 32
mov edx, eax
shl eax, 2 ; multiply eax by 4
mov ecx, eax ; start looping at ecx = eax * 4 a little quicker
add eax, edx ; now eax is times 5
@@: mov edx, ecx ; hokey way to divide by 9
shl edx, 3 ; mult edx by 8
add edx, ecx ; now edx is times 9
cmp edx, eax ; see if it's down to the target yet
jle @F
loop @B
@@: sub eax, edx ; round up if target was overshot by 5,6,7, or 8
sub eax, 4
jle @F
inc ecx
@@:
print ustr$(ecx),13,10 ; result in ecx
inkey
ret
end start
[edit] this could be faster, on average, by starting the loop at 223, given the max Fahrenheit of 430; but if speed matters this approach no good anyway
it's a 16-bit processor - lol
Quote from: dedndaveit's a 16-bit processor - lol
Sure, but that doesn't mean the algo should be expressed with 16-bit X86 code .. ! Could just as well give it in psuedocode, or basic. It needs to be translated regardless. Note that the largest number my algo needs is 15480
Peter,
See if this can be translated to the processor you are working with. Its basically one of the old timer techniques for multiply on a processor that does not have a MUL instruction. You need to be able to do bit shifts with this one.
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
OPTION PROLOGUE:NONE
OPTION EPILOGUE:NONE
peasant_multiply proc arg1:DWORD,arg2:DWORD
mov ecx, [esp+4] ; arg1 ; load 1st arg into ECX
mov edx, [esp+8] ; arg2 ; load 2nd arg into EDX
xor eax, eax ; zero EAX
jmp testit ; jump to test if arg2 is ODD or EVEN
lbl0:
add ecx, ecx ; double arg1
shr edx, 1 ; div arg2 by 2
cmp edx, 1 ; compare it to 1
jbe lbl1 ; exit loop if below or equal
testit:
test edx, 00000000000000000000000000000001b
je lbl0 ; jump back if its even
add eax, ecx ; accumulate ECX in EAX if EDX is odd
jmp lbl0
lbl1:
add eax, ecx ; add last ECX to EAX and exit
ret 8
peasant_multiply endp
OPTION PROLOGUE:PrologueDef
OPTION EPILOGUE:EpilogueDef
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
Quote from: rrr314159uses no mult, div, or other instructions that might not be on RISC like sbb etc
Quote from: hutchYou need to be able to do bit shifts with this one.
The reference given above for RISC PIC16F1784 shows it does have a full complement of bit operations: bit-test-and-skip (in one operation), bit shifts, subtract with borrow, rotate thru carry etc