C programmering: konstanter i float --> int

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
Användarvisningsbild
arvidb
Inlägg: 4537
Blev medlem: 8 maj 2004, 12:56:24
Ort: Stockholm

Re: C programmering: konstanter i float --> int

Inlägg av arvidb »

SvenW skrev:*snip*
Typdeffar är bra om typen används på många ställen, och i anslunting till typdeffen kan man kommentera närmare hur man tänker, så man inte behöver lägga samma information i kommentarer på många ställen.

ex:
typedef int16_t AdcSteps; // One ADCstep is 1 mV
...
const AdcSteps v_ref = 4096;
...
Själv tycker jag generellt riktigt illa om "typdeffar". Det gör att det blir betydligt svårare att hitta buggar eftersom man inte vet vad som finns bakom "AdcSteps". Rymmer variabeln 8, 16 eller 32 bitar? Är den signed eller unsigned? Man får inte heller någon extra typsäkerhet (tyvärr!) eftersom kompilatorn konverterar heltalsvariabler bakom ryggen på en.

Mycket bättre då att använda den faktiska typen direkt.

På rak arm kan jag komma på ett enda tillfälle där typedef kan vara OK:

1) För att förtydliga variabeltypen:

Kod: Markera allt


#include <stdlib.h>
#include <stdio.h>

typedef int	int16;

#define COMPILE_TIME_ASSERT(pred)		\
	switch (0) { case 0: case (pred): default: ; }

int main(void)
{
	/*
	 * Make sure the data types are of the correct size.
	 * These checks will complain like this if the predicate is
	 * false:
	 * 	error: duplicate case value
	 */
	COMPILE_TIME_ASSERT(sizeof(int16) == 2);

	printf("Hello, world!\n");

	return EXIT_SUCCESS;
}
(se http://www.jaggersoft.com/pubs/CVu11_3.html för info om vad COMPILE_TIME_ASSERT() gör).

Ett tillfälle som inte är bra är om man inte är riktigt säker på vilken datatyp man kommer att vilja ha i slutändan, och vill kunna ändra "lätt", genom att bara ändra sin typedef. Detta är som gjort för att skapa svårupptäckta buggar i programmet - man har ju skrivit koden med utgångspunkt att man har en variabel av en viss typ. Ändrar man sen typ så ber man om buggar som dyker upp förr eller senare.

Findecanor skrev:Flyttal skrivs i decimal, men lagras i binärt, dvs. basen 2. Visst får du precision, men det är inte alltid som det du skrivit är exakt det som lagras i variabeln. T.ex. om du skriver "0.1" så får du egentligen "0.10001".
... och om du försöker lagra 0.1 i 8.8 fixed format så blir det 0.1015625. Du får inte sämre precision med flyttal än med fixed point av samma storlek.
Findecanor skrev:Jag tror inte heller att det är vanligt att microcontrollers har något flyttalsenhet, vilket gör att de måste emuleras, vilket inte har någon bra prestanda och därför bör undvikas i tidskritisk kod.
Dock, om man gör som TS föreslår och enbart använder flyttal för att beräkna konstanter som går att beräkna vid kompileringstillfället, och castar dem till heltal när de är "klara", så sköter kompilatorn om all flyttalsmatte och det blir bara heltalsmatte kvar för processorn. Jag tycker att det verkar som en bra strategi!

Vill man sedan fortsätta räkna med högre precision än heltal under körning så kan man ju multiplicera konstanterna med en potens av två och använda fixed point.


TS:

gcc verkar ha stöd för parametern -mno-soft-float för åtminstone någon arkitektur. Vet inte om det funkar för dig.

Konstanter som anges med decimalkomma och utan efterföljande f är för övrigt inte av typen float ("1.0f"), utan av typen double ("1.0"). Vilket väl bara är bra så länge kompilatorn tar hand om matten.
blueint
Inlägg: 23238
Blev medlem: 4 juli 2006, 19:26:11
Kontakt:

Re: C programmering: konstanter i float --> int

Inlägg av blueint »

Följande definition kanske skulle vara användbar?

#define decimaltal(a,b) ((a)*1000+(b))

#define maxspanning decimaltal(15,2)

printf("Kvot %.2f\n", mätvärde() / maxspanning );

I detta fall utgår man från att alla värden är 1000 ggr större.

Sedan finns det ju en en del modiferare som kan sättas efter tal som kan ställa till det. De är oftast inkompatibla med varann med riktigt luriga buggar som följd.
Användarvisningsbild
jesse
Inlägg: 9240
Blev medlem: 10 september 2007, 12:03:55
Ort: Alingsås

Re: C programmering: konstanter i float --> int

Inlägg av jesse »

Findecanor skrev:Använd fixed point! Då får du prestanda, precision och kompilatorn kan uttrycken kan optimeras ner till konstanter.
Jag föredrar att använda fixed point med en faktor som är 2**(heltal). De kan oftast optimeras väl av kompilatorn.
Hur skriver du det då? Det finns väl ingen färdig typ som heter "fixed point"... Det hade varit bra med en standard i så fall, i form av ett bibliotek att ladda ner.

Mina heltal som jag använder mig av (ovan) är ju egentligen lite korkade, eftersom de inte är 2-potenser... Jag anger 1.00 volt som 100, alltså i hundradelar. Det är ju bökigt för en heltalsprocessor, att behöva dividera med 100. Egentligen borde jag föredra att använda binära tal här (binaler)... så ett 16-bitars tal skulle kunna delas upp i 8.8 om vilket då representerar tal från 0 till 255.996 om det är unsigned. Enda orsaken till att jag inte gjort så är att jag har ärvt en massa gamla "print"-funktioner från äldre program som skriver ut decimaltal som just består av heltal i tiondelar eller hundradelar. T.ex.

printDP1(12345); // skriver ut 1234.5
printDP2(12345); // skriver ut 123.45
printDP3(12345); // skriver ut 12.345
printDP(12345678, 4); // skriver ut 1234.5678

Om jag skulle använt normal "fixed point" baserad på basen 2 hade jag kunnat få avrundningsfel vid utskrift som kan se konstigt ut.

Men rent allmänt.... ganska ofta i ett program kommer man till en situation där du ska multiplicera ett 16-bitars heltal med en konstant i storleksordningen 0.5 - 2. Hur skriver man det snyggast? Anger man decimaltalet i kommentarerna endast och skriver bara heltal i koden?

Kod: Markera allt

uint16_t input_value = 25000;
uint16_t output_value = ((u32)input_value * 52439) / 65536; // multiplicera med konstant 0.80
Hur skulle du skrivit :?:
Användarvisningsbild
jesse
Inlägg: 9240
Blev medlem: 10 september 2007, 12:03:55
Ort: Alingsås

Re: C programmering: konstanter i float --> int

Inlägg av jesse »

Fixed point discussion:

ISO-standard for C-code:
Fixed Point Extensions to the C Programming Language

Fixed Point Extensions to the C Programming Language
(verkar dock inte vara så optimalt för mikrocontrollers!)

Why aren't fixed-point types included in C99?
From 'Using the GNU Compiler Collection' (for GCC version 4.4.0):

§5.13 Fixed-Point Types

As an extension, the GNU C compiler supports fixed-point types as defined in the N1169 draft of ISO/IEC DTR 18037. Support for fixed-point types in GCC will evolve as the draft technical report changes. Calling conventions for any target might also change. Not all targets support fixed-point types.

The fixed-point types are short _Fract, _Fract, long _Fract, long long _Fract, unsigned short _Fract, unsigned _Fract, unsigned long _Fract, unsigned long long _Fract, _Sat short _Fract, _Sat _Fract, _Sat long _Fract, _Sat long long _Fract, _Sat unsigned short _Fract, _Sat unsigned _Fract, _Sat unsigned long _Fract, _Sat unsigned long long _Fract, short _Accum, _Accum, long _Accum, long long _Accum, unsigned short _Accum, unsigned _Accum, unsigned long _Accum, unsigned long long _Accum, _Sat short _Accum, _Sat _Accum, _Sat long _Accum, _Sat long long _Accum, _Sat unsigned short _Accum, _Sat unsigned _Accum, _Sat unsigned long _Accum, _Sat unsigned long long _Accum.

Fixed-point data values contain fractional and optional integral parts. The format of fixed-point data varies and depends on the target machine.
Jag testade att skriva detta i Atmel Studio 6:

Kod: Markera allt

_Fract a;
resultat:
Du har inte behörighet att öppna de filer som bifogats till detta inlägg.
Användarvisningsbild
TomasL
EF Sponsor
Inlägg: 46974
Blev medlem: 23 september 2006, 23:54:55
Ort: Borås
Kontakt:

Re: C programmering: konstanter i float --> int

Inlägg av TomasL »

Använd heltalsaritmetik, så slipper du problem.
Skriv svar