decimaltal, och tal som är större än 255 ?
Re: decimaltal, och tal som är större än 255 ?
Nu börjar ju diskussionen äntligen handla om själva ursprungsproblemet (om nu inte tempgivaren bara var ett exempel, men ett väldigt bra nybörjarexempel i så fall)
lite heltalsmatematik: (nu talar vi decimalt om inget annat sägs)
vi skiter i minusgrader just nu. (det blir överkurs)
anta att givaren ger ut 0 vid 0 grader och 256 vid 100 grader (så bra är det förstås aldrig i verkligheten)
du läser in en byte: talet blir 66
du vet att 256=100 grader. alltså måste temperaturen vara (100 / 256 ) * 66 dvs 25,78 grader (givaren är nämligen perfekt linjär).
du vill ha en decimal. och temperaturen som ska visas kan vara allt från 0.0 till 99,9 grader. det skulle kunna motsvaras av två bytes tal (16 bitar ) som då kan anta värden från 0 till 65535.
låt 10000 motsvara 100 grader. Det tal du måste få fram blir då 2578.
För att få fram detta tal måste du multiplicera 66 med en konstant , nämligen 10000/256 dvs 39.0625
multiplikation med 16-bitars tal kommer du antagligen att behöva i framtiden, så istället för att analysera talet 39.0625 binärt för att komma på en smart genväg i just det här fallet, så lånar vi en 16-bitars multiplikationsrutin i ASM från nätet.
multiplikationen definieras som A=B*C där A, B och C består av två 8-bitars register vardera som innehåller 16-bitars tal.
sätt B = det inlästa värdet.
C = konstanten, men eftersom den inte är ett heltal så multiplicerar vi båda talen med 256 genom att shifta talen 8 bitar åt vänster ( dvs * 256)
Då får vi
A*256*256 = B*256 * C*256
konstanten C*256 blir 10000 och inlästa värdet *256 är detsamma som att lägga värdet i den höga byten av talet och fylla på med nollor i den låga.
så multiplicerar vi. svaret blir 32-bitars!
och det blir stort: 66*256 * 10000 = 1.6896*10^8
men talet ska delas med 256*256 för att få fram svaret.
det görs enkelt med att vi kapar bort de 16 sista siffrorna i svaret .
då blir det 2578 kvar.
detta är temperaturen i hundradels grader celcius!
omvandla talet till ASCII (annars kan du ändå aldrig kulla läsa det på en LCD) med en lämplig rutin du snor från nätet. du får "02578"
skriv ut siffra 2 och 3, skriv ut en punkt, skriv ut siffra 4 (och eventuellt 5 om du vill ha med hundradelar). skriv sedan ut "°C" och din display visar 25.7°C eller 25.78°C
sen kommer överkursen:
A) om 0 från givaren inte är noll grader utan -40 grader!
B) du vill avrunda resultatet rätt
C) varför är det helt meningslöst att visa hundradelar i det här fallet?
lite heltalsmatematik: (nu talar vi decimalt om inget annat sägs)
vi skiter i minusgrader just nu. (det blir överkurs)
anta att givaren ger ut 0 vid 0 grader och 256 vid 100 grader (så bra är det förstås aldrig i verkligheten)
du läser in en byte: talet blir 66
du vet att 256=100 grader. alltså måste temperaturen vara (100 / 256 ) * 66 dvs 25,78 grader (givaren är nämligen perfekt linjär).
du vill ha en decimal. och temperaturen som ska visas kan vara allt från 0.0 till 99,9 grader. det skulle kunna motsvaras av två bytes tal (16 bitar ) som då kan anta värden från 0 till 65535.
låt 10000 motsvara 100 grader. Det tal du måste få fram blir då 2578.
För att få fram detta tal måste du multiplicera 66 med en konstant , nämligen 10000/256 dvs 39.0625
multiplikation med 16-bitars tal kommer du antagligen att behöva i framtiden, så istället för att analysera talet 39.0625 binärt för att komma på en smart genväg i just det här fallet, så lånar vi en 16-bitars multiplikationsrutin i ASM från nätet.
multiplikationen definieras som A=B*C där A, B och C består av två 8-bitars register vardera som innehåller 16-bitars tal.
sätt B = det inlästa värdet.
C = konstanten, men eftersom den inte är ett heltal så multiplicerar vi båda talen med 256 genom att shifta talen 8 bitar åt vänster ( dvs * 256)
Då får vi
A*256*256 = B*256 * C*256
konstanten C*256 blir 10000 och inlästa värdet *256 är detsamma som att lägga värdet i den höga byten av talet och fylla på med nollor i den låga.
så multiplicerar vi. svaret blir 32-bitars!
och det blir stort: 66*256 * 10000 = 1.6896*10^8
men talet ska delas med 256*256 för att få fram svaret.
det görs enkelt med att vi kapar bort de 16 sista siffrorna i svaret .
då blir det 2578 kvar.
detta är temperaturen i hundradels grader celcius!
omvandla talet till ASCII (annars kan du ändå aldrig kulla läsa det på en LCD) med en lämplig rutin du snor från nätet. du får "02578"
skriv ut siffra 2 och 3, skriv ut en punkt, skriv ut siffra 4 (och eventuellt 5 om du vill ha med hundradelar). skriv sedan ut "°C" och din display visar 25.7°C eller 25.78°C
sen kommer överkursen:
A) om 0 från givaren inte är noll grader utan -40 grader!
B) du vill avrunda resultatet rätt
C) varför är det helt meningslöst att visa hundradelar i det här fallet?
Re: decimaltal, och tal som är större än 255 ?
litet tillägg:
exempel på addition av två sextonbitars tal i en mikroprocessor:
(jag kan bara AVR assembler, så det får bli det språket i mitt exempel)
för att addera två sextonbitars tal behövs fyra 8-bitars register.
dessa register kan vi kalla AL,AH,BL och BH.
AL är den "låga" byten i tal A. AH är den höga byten.
AH har då värdet 256*innehållet, och AL har värdet 1*innehållet
A består alltså av 16 bitar uppdelat i hög och låg.
samma sak med B - BL och BH.
vi ska addera så att A=A+B
exempel 4001+100 = 4101
A = 4001 (decimalt) blir t.ex. 0FA1 (hex) och 0000 1111 1010 0001 (bin)
AL = 10100001 (=161 dec)
AH = 00001111 (= 15 dec)
B = 100 (dec ) = 0064 (hex) = 0000 0000 0110 0100 (bin)
BL = 01100100 (100 dec)
BH = 00000000 (0 dec)
programmet ser ut så här:
ADD AL,BL ; addera låga bytes med varandra, "overflow" ger etta i carryflaggan C
ADC AH,BH ; addera höga bytes med varandra och lägg till carryflaggans värde.
AL+BL:
10100001
+ 01100100
= 100000101
AL får värdet 00000101 ( 5 dec) och carryflaggan =1
AH+BH+C:
00001111
+00000000
+ 1
=00010000 ( = 16 dec)
carry = 0 den här gången vilket visar att svaret fick plats inom de 16-bitarna.
nu blev AH och AL tillsammans 0001 0000 0000 0101 (bin) = 1005 (hex) = 4101 (dec)
det är också lätt att räkna decimalt i huvudet med de fyra 8-bitars talen:
A = 4001 => AH = 161, AL = 15
B = 100 => BH = 0, BL = 100
AL=AL+BL ... AL = 161+100 = 261. Men 261 är mer än 255. AL blir då (261-256) = 5, med en etta i minnet.
AH=AH+BH+C... AH = 15+0+1 = 16
svaret i 16 bitar blir AH*256+AL = 16*256+5 = 4101
exempel på addition av två sextonbitars tal i en mikroprocessor:
(jag kan bara AVR assembler, så det får bli det språket i mitt exempel)
för att addera två sextonbitars tal behövs fyra 8-bitars register.
dessa register kan vi kalla AL,AH,BL och BH.
AL är den "låga" byten i tal A. AH är den höga byten.
AH har då värdet 256*innehållet, och AL har värdet 1*innehållet
A består alltså av 16 bitar uppdelat i hög och låg.
samma sak med B - BL och BH.
vi ska addera så att A=A+B
exempel 4001+100 = 4101
A = 4001 (decimalt) blir t.ex. 0FA1 (hex) och 0000 1111 1010 0001 (bin)
AL = 10100001 (=161 dec)
AH = 00001111 (= 15 dec)
B = 100 (dec ) = 0064 (hex) = 0000 0000 0110 0100 (bin)
BL = 01100100 (100 dec)
BH = 00000000 (0 dec)
programmet ser ut så här:
ADD AL,BL ; addera låga bytes med varandra, "overflow" ger etta i carryflaggan C
ADC AH,BH ; addera höga bytes med varandra och lägg till carryflaggans värde.
AL+BL:
10100001
+ 01100100
= 100000101
AL får värdet 00000101 ( 5 dec) och carryflaggan =1
AH+BH+C:
00001111
+00000000
+ 1
=00010000 ( = 16 dec)
carry = 0 den här gången vilket visar att svaret fick plats inom de 16-bitarna.
nu blev AH och AL tillsammans 0001 0000 0000 0101 (bin) = 1005 (hex) = 4101 (dec)
det är också lätt att räkna decimalt i huvudet med de fyra 8-bitars talen:
A = 4001 => AH = 161, AL = 15
B = 100 => BH = 0, BL = 100
AL=AL+BL ... AL = 161+100 = 261. Men 261 är mer än 255. AL blir då (261-256) = 5, med en etta i minnet.
AH=AH+BH+C... AH = 15+0+1 = 16
svaret i 16 bitar blir AH*256+AL = 16*256+5 = 4101
-
- Inlägg: 822
- Blev medlem: 23 mars 2009, 19:04:00
- Ort: Ystad
Re: decimaltal, och tal som är större än 255 ?
Jag känner i alla fall inte nu för att göra en tempmätare *S*. Tror jag fattade det sista exemplet ganska bra. Och det var lite som jag tänkte att värdet i sig är ett heltal. Men man tänder ett kommatecken på displayen.
Om jag fattat rätt.
Enda ggn picken igentligen kan jobba med ett decimaltal så är det i a/d eller d/a då den ändra värdet en enhet om t,ex tempen höjs 0.15864 grader. Men de siffrorna behöver jag ju inte använda. Det sköter A/D delen åt mig. All annan decimaltal får jag själv räkna ut på ett eller annat sätt och visa .
Skall jag t,ex bara göra en frost/halkvarnare . En diod som lyser, så kan man skita i vad för värde pic-en har. Bara man vet att jag skall börja varna då värdet är lägre än t,ex 010001010 som då kanske är 2 grader Cellsius.
Skall man jobba med stegmotor så kanske man skall kika på vad det finns för hårdvara. Man kan i stället för att gånga ett tal med 1,4 så ser man till att 2 kuggjul har olika antal tänder. 100 och 140. Så slipper man en beräkning i pic-en. Det sparar tid. Kan man bygga en maskin med 100 pulser per varv och ett varv rör den sig 1 mm så kan man ju lätt få en nogrannhet på 0.01. i alla fall teoretiskt *S*
Om jag fattat rätt.
Enda ggn picken igentligen kan jobba med ett decimaltal så är det i a/d eller d/a då den ändra värdet en enhet om t,ex tempen höjs 0.15864 grader. Men de siffrorna behöver jag ju inte använda. Det sköter A/D delen åt mig. All annan decimaltal får jag själv räkna ut på ett eller annat sätt och visa .
Skall jag t,ex bara göra en frost/halkvarnare . En diod som lyser, så kan man skita i vad för värde pic-en har. Bara man vet att jag skall börja varna då värdet är lägre än t,ex 010001010 som då kanske är 2 grader Cellsius.
Skall man jobba med stegmotor så kanske man skall kika på vad det finns för hårdvara. Man kan i stället för att gånga ett tal med 1,4 så ser man till att 2 kuggjul har olika antal tänder. 100 och 140. Så slipper man en beräkning i pic-en. Det sparar tid. Kan man bygga en maskin med 100 pulser per varv och ett varv rör den sig 1 mm så kan man ju lätt få en nogrannhet på 0.01. i alla fall teoretiskt *S*
Re: decimaltal, och tal som är större än 255 ?
Nja... det där med att bygga kugghjulen speciellt för att slippa räkna i picen - kanske kan fungera ibland, men oftast är det ju inte så bekvämt eller praktiskt att omvandla den fysiska miljön för att man vill slippa en multiplikation, så man får nog helt enkelt lära sig räkna med sina heltal.
B = A * 1.4
räkna ut B=A*14 genom att
B=A
A*=2
B+=A
A=A*2
B+=A
B*=2
eller AVR-assembler (8 bitars tal):
mov b,a
lsl a
add b,a
lsl a
add b,a
lsl b
det tar exakt 6 klockpulser , eller 1,5µS vid 4 mhz. 16-bitars tal tar dubbelt så lång tid.
plocka bort en decimal i svaret så får du svaret på A * 1.4
B = A * 1.4
räkna ut B=A*14 genom att
B=A
A*=2
B+=A
A=A*2
B+=A
B*=2
eller AVR-assembler (8 bitars tal):
mov b,a
lsl a
add b,a
lsl a
add b,a
lsl b
det tar exakt 6 klockpulser , eller 1,5µS vid 4 mhz. 16-bitars tal tar dubbelt så lång tid.
plocka bort en decimal i svaret så får du svaret på A * 1.4
-
- Inlägg: 822
- Blev medlem: 23 mars 2009, 19:04:00
- Ort: Ystad
Re: decimaltal, och tal som är större än 255 ?
Man måste lära sig det bara..... Man kan inte alltid gå genvägar som blir omvägar. man får andra problem med att dona med kuggar. Antingen på kostnad av snabbhet eller på styrka.
Letar efter en bra och billig pulsmotor för experiment. Tar gärna emot förslag .
Letar efter en bra och billig pulsmotor för experiment. Tar gärna emot förslag .
Re: decimaltal, och tal som är större än 255 ?
Ibland kan det vara vetigt att justera "verkligheten" för att underlätta
hanteringen/beräkningarna i processorn. T.ex om man ska mäta signalen
från en givare som ger en analog signal, så kan man kanske justera
signalen med en spänningsdelare, eller så. Det beror ju mycket på hur
det ser ut från fall till fall. Eller t.ex om man ska mäta ett antal pulser
från en givare (t.ex för att mäta bränsleflöde eller liknande) så kan man
anpassa tiden som man mäter för att få ett "bekvämt" värde direkt, istället
för att bara slentrianmässigt mäta/räkna under t.ex 1.00 sek. Det är ju gansla
enkelt att justera en timer för valfri period och de följande beräkningarna kan
*ibland* förrenklas kraftigt av det.
Som vanligt så är hela lösningen beroende på vilka ingående delar man
har från fall till fall.
Sen, när det gäller att justera (multiplicera/dividera) ett värde med en *konstant*
så finns det en kodgenerator på http://www.piclist.com som genererar den effektivaste
koden för en given konstant och ett angivet fel i beräkningen.
http://www.piclist.com/techref/piclist/ ... divmul.htm
T.ex för att multiplicera med 1.4 och med 0.5% fel så ger den nedanstående kod.
Med 1% fel så blir den något kortare ("Add input / 128 to accumulator" försvinner).
hanteringen/beräkningarna i processorn. T.ex om man ska mäta signalen
från en givare som ger en analog signal, så kan man kanske justera
signalen med en spänningsdelare, eller så. Det beror ju mycket på hur
det ser ut från fall till fall. Eller t.ex om man ska mäta ett antal pulser
från en givare (t.ex för att mäta bränsleflöde eller liknande) så kan man
anpassa tiden som man mäter för att få ett "bekvämt" värde direkt, istället
för att bara slentrianmässigt mäta/räkna under t.ex 1.00 sek. Det är ju gansla
enkelt att justera en timer för valfri period och de följande beräkningarna kan
*ibland* förrenklas kraftigt av det.
Som vanligt så är hela lösningen beroende på vilka ingående delar man
har från fall till fall.
Sen, när det gäller att justera (multiplicera/dividera) ett värde med en *konstant*
så finns det en kodgenerator på http://www.piclist.com som genererar den effektivaste
koden för en given konstant och ett angivet fel i beräkningen.
http://www.piclist.com/techref/piclist/ ... divmul.htm
T.ex för att multiplicera med 1.4 och med 0.5% fel så ger den nedanstående kod.
Med 1% fel så blir den något kortare ("Add input / 128 to accumulator" försvinner).
Kod: Markera allt
; ACC = ACC * 1.4
; Temp = TEMP
; ACC size = 8 bits
; Error = 0.5 %
; Bytes order = little endian
; Round = no
; ALGORITHM:
; Clear accumulator
; Add input * 1 to accumulator
; Add input / 4 to accumulator
; Add input / 8 to accumulator
; Add input / 64 to accumulator
; Add input / 128 to accumulator
; Move accumulator to result
;
; Approximated constant: 1.39844, Error: 0.111607 %
; Input: ACC0, 8 bits
; Output: ACC0 .. ACC1, 9 bits
; Code size: 18 instructions
cblock
ACC0
ACC1
endc
;copy accumulator to temporary
movf ACC0, w
;shift accumulator right 1 times
clrc
rrf ACC0, f
;add temporary to accumulator
addwf ACC0, f
;shift accumulator right 3 times
rrf ACC0, f
clrc
rrf ACC0, f
clrc
rrf ACC0, f
;add temporary to accumulator
addwf ACC0, f
;shift accumulator right 1 times
rrf ACC0, f
;add temporary to accumulator
addwf ACC0, f
;shift accumulator right 2 times
rrf ACC0, f
clrc
rrf ACC0, f
;add temporary to accumulator
addwf ACC0, f
clrf ACC1
rlf ACC1, f
-
- Inlägg: 822
- Blev medlem: 23 mars 2009, 19:04:00
- Ort: Ystad
Re: decimaltal, och tal som är större än 255 ?
Det var ju en smart kodgenerator. Men man kanske lurar sig själv om man tror att den alltid ger den mest effektiva lösningen. Sodjans exempel är ju perfekt i det här fallet. Att multiplicera med 1.4 (dec) ser ju lätt ut, men binärt så är 1.4 = 1.01100110011001100110011001100110011001100110011... och det blir jobbigt. Jag tog genvägen att multiplicera "konstanten" med 10 (för att få tiondelar istället för ental i svaret) och kan då bekvämt multiplcera med 14 (dec) vilket blir 1110 (bin) vilket man som sagt kan göra på 6 klockpulser - och får därmed ett exakt resultat.
Re: decimaltal, och tal som är större än 255 ?
Jag har applikationer där värden kan ställas i %. När man sedan ska räkna ut det "binära värde", alltså vilket ta den procent motsvarar är det enkelt:
Resultat = (Konstant * Procent) / 100;
Oftast räknas detta i steg då vissa kompilers kan ställa till det lite:
Resultat = Konstant * Procent;
Resultat = Resultat / 100;
På det vis kan man hålla sig till heltal och fortfarande få bra noggrannhet.
Resultat = (Konstant * Procent) / 100;
Oftast räknas detta i steg då vissa kompilers kan ställa till det lite:
Resultat = Konstant * Procent;
Resultat = Resultat / 100;
På det vis kan man hålla sig till heltal och fortfarande få bra noggrannhet.
Re: decimaltal, och tal som är större än 255 ?
...och för att få så bra noggrannhet som möjligt med ett begränsat antal bitar så skalar man upp talet så mycket det går innan man multiplicerar/dividerar... t.ex. om du har 16-bitar och du har ett tal mellan 0 och 1023 och du ska ha ut procent, t.ex 85% så multiplicera först talet med 64 så har du fler bitar att räkna med och därmed mindre avrundningsfel etc.. (maxgränsen för unsigned 16-bitars tal är ju 65535 och 64*1023 = 65427)
Re: decimaltal, och tal som är större än 255 ?
> Att multiplicera med 1.4 (dec) ser ju lätt ut, men binärt så är 1.4 =
> 1.01100110011001100110011001100110011001100110011...
Jag vet inte om du kollade den automatgenererade koden.
Den gör exakt det du beskriver, men "bryter" när man passerar
gränsen för den noggranhet som man anger på sidan. Alltså,
"1.0110011" är samma sak som
Varje "1" i den binära representationen av 1.4 motsvaras av en av raderna ovan.
Ganska enkelt igentligen. Om man anger att man har 32 bitars tal, och en mycket lägre
fel-procent, så ser det ut som nedan. Vilket alltså motsvaras av "1.01100110011001100110011"
från din uträkning av 1.4 binärt. Man tar alltså med så många binära decimaler (som är "1") som
behövs för att uppnå den angivna/önskade fel-procenten, fler är bara onödigt och tillför
inget till resultatet.
> 1.01100110011001100110011001100110011001100110011...
Jag vet inte om du kollade den automatgenererade koden.
Den gör exakt det du beskriver, men "bryter" när man passerar
gränsen för den noggranhet som man anger på sidan. Alltså,
"1.0110011" är samma sak som
Kod: Markera allt
; Add input * 1 to accumulator
; Add input / 4 to accumulator
; Add input / 8 to accumulator
; Add input / 64 to accumulator
; Add input / 128 to accumulator
Ganska enkelt igentligen. Om man anger att man har 32 bitars tal, och en mycket lägre
fel-procent, så ser det ut som nedan. Vilket alltså motsvaras av "1.01100110011001100110011"
från din uträkning av 1.4 binärt. Man tar alltså med så många binära decimaler (som är "1") som
behövs för att uppnå den angivna/önskade fel-procenten, fler är bara onödigt och tillför
inget till resultatet.
Kod: Markera allt
; ACC = ACC * 1.4
; Temp = TEMP
; ACC size = 32 bits
; Error = 1e-005 %
; Bytes order = little endian
; Round = no
; ALGORITHM:
; Clear accumulator
; Add input * 1 to accumulator
; Add input / 4 to accumulator
; Add input / 8 to accumulator
; Add input / 64 to accumulator
; Add input / 128 to accumulator
; Add input / 1024 to accumulator
; Add input / 2048 to accumulator
; Add input / 16384 to accumulator
; Add input / 32768 to accumulator
; Add input / 262144 to accumulator
; Add input / 524288 to accumulator
; Add input / 4194304 to accumulator
; Add input / 8388608 to accumulator
; Move accumulator to result
;
; Approximated constant: 1.4, Error: 1.70299e-006 %
; Input: ACC0 .. ACC3, 32 bits
; Output: ACC0 .. ACC4, 33 bits
; Code size: 281 instructions
...
...
Re: decimaltal, och tal som är större än 255 ?
Jag får nog bokmärka den länken... kan vara bra att ha när man inte vill lägga tid på "tankenötter".
- Swech
- EF Sponsor
- Inlägg: 4750
- Blev medlem: 6 november 2006, 21:43:35
- Ort: Munkedal, Sverige (Sweden)
- Kontakt:
Re: decimaltal, och tal som är större än 255 ?
Rutinen i fråga är ju egentligen en klassisk multiplikation där man strippar bort
alla ingående "0" i sin multiplikator.
För att underlätta vid underhåll av programkod så föredrar iallafall jag personligen
att inte bygga kod som förutsätter en hårdkodad variabel/konstant.
Lägg hellre till en komplett multiplikation.
Men smaken är naturligtvis blandad.
Swech
alla ingående "0" i sin multiplikator.
För att underlätta vid underhåll av programkod så föredrar iallafall jag personligen
att inte bygga kod som förutsätter en hårdkodad variabel/konstant.
Lägg hellre till en komplett multiplikation.
Men smaken är naturligtvis blandad.
Swech
Re: decimaltal, och tal som är större än 255 ?
> Rutinen i fråga är ju egentligen en klassisk multiplikation där man strippar bort
alla ingående "0" i sin multiplikator.
Visst är det det ! Som jag sa, ganska enkelt igentligen...
Tanken är naturligtsvis att man just har ett fall där det handlar om en fast konstant.
Och i så fall blir den nog snabbare än en generell MUL rutin med motsvarande
antal bitar/precision. Men som det är sagt flera gånger tidigare i tråden, det finns
igentligen inget allomfattande generellt "rätt" svar, det beror så väldigt mycket
på den aktuella applikationen (och kanske inte direkt så mycket på "smak"...).
alla ingående "0" i sin multiplikator.
Visst är det det ! Som jag sa, ganska enkelt igentligen...

Tanken är naturligtsvis att man just har ett fall där det handlar om en fast konstant.
Och i så fall blir den nog snabbare än en generell MUL rutin med motsvarande
antal bitar/precision. Men som det är sagt flera gånger tidigare i tråden, det finns
igentligen inget allomfattande generellt "rätt" svar, det beror så väldigt mycket
på den aktuella applikationen (och kanske inte direkt så mycket på "smak"...).
