The MASM Forum

General => The Laboratory => Topic started by: oex on October 03, 2015, 01:10:08 AM

Title: Fahrenheit To Celcius
Post by: oex on October 03, 2015, 01:10:08 AM
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:
Title: Re: Fahrenheit To Celcius
Post by: qWord on October 03, 2015, 01:24:11 AM
fld/fild,fadd,fmul,fstp/fistp with REAL4 constants?
Title: Re: Fahrenheit To Celcius
Post by: oex on October 03, 2015, 01:32:20 AM
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
Title: Re: Fahrenheit To Celcius
Post by: qWord on October 03, 2015, 01:47:41 AM
sorry, I didn't notice "not for x86".

hard to solve with these device, even no multiplication available.
Title: Re: Fahrenheit To Celcius
Post by: oex on October 03, 2015, 01:54:38 AM
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
Title: Re: Fahrenheit To Celcius
Post by: dedndave on October 03, 2015, 01:59:47 AM
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
Title: Re: Fahrenheit To Celcius
Post by: 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
Title: Re: Fahrenheit To Celcius
Post by: dedndave on October 03, 2015, 02:09:56 AM
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
Title: Re: Fahrenheit To Celcius
Post by: oex on October 03, 2015, 02:34:28 AM
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
Title: Re: Fahrenheit To Celcius
Post by: dedndave on October 03, 2015, 02:43:21 AM
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
Title: Re: Fahrenheit To Celcius
Post by: rrr314159 on October 03, 2015, 09:11:10 AM
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
Title: Re: Fahrenheit To Celcius
Post by: dedndave on October 03, 2015, 11:04:00 AM
it's a 16-bit processor - lol
Title: Re: Fahrenheit To Celcius
Post by: rrr314159 on October 03, 2015, 12:22:19 PM
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
Title: Re: Fahrenheit To Celcius
Post by: hutch-- on October 03, 2015, 12:51:43 PM
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

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
Title: Re: Fahrenheit To Celcius
Post by: rrr314159 on October 04, 2015, 01:51:59 AM
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