Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: C++: onnistuuko näin?

tim16 [04.03.2007 09:54:28]

#

moi,
yritän saada floatin charriksi. onnistuuko näin ja säilyykö tieto muuttumattomana?

unsigned char *humi;
float Humidity;
float *HumidityInt;

*HumidityInt = (Humidity * 10.0)/10;
esim. *HumidityInt on 36.5
HumidityInt = (void*)humi;  //onnistuuko näin?
write_sd((unsigned char*)HumidityInt);

sprintf funktiolla onnistuu, mutta kasvattaa tiedoston kokoa minun sovelluksessa liikaa.

(Mod. Edit. Kooditagit.)

Pekka Karjalainen [04.03.2007 10:55:51]

#

Haluat ilmeisesti muuttaa floattisi merkkijonoksi.

36.5 -> "36.5" (ja nulli loppuun)

Jos kerrot, mikä on floattiesi arvoalue, voimme ehkä kirjoittaa hyvin lyhyen koodinpätkän, joka tekee tämän muutoksen riittävällä tarkkuudella tällä arvoalueella. Tässä on esimerkkikoodia, jota et saa käyttää, koska se on huonosti tehty.

#include <stdio.h>

/* funktio muuttaa floatin v"lilt" -99.99 - 99.99 merkkijonoksi,
   jossa on kahden desimaalin tarkkuus. se asettaa merkkijonon
   annettuun osoittimeen, jossa on oltava tarpeeksi tilaa. funktio
   lopettaa merkkijonon nollaan (\0).

   palautusarvo 1, jos onnistui, muuten palautusarvo 0 virheen merkkin"
*/

int floatstr (float value, char* result) {
  const char digits[] =  "0123456789";
  char sign = value < 0.0 ? -1 : 1;
  unsigned int_part = (unsigned) (value * sign);

  /* t"ss" tulee py"ristysvirhe - korjattava oikeassa ohjelmassa */
  unsigned dec_part = ((unsigned) (value*100 * sign) % 100);

  char* next = result;

#ifndef NDEBUG
  printf ("floatstr: arvot ovat sign: %d int_part: %u dec_part %u\n",
           (int) sign, int_part, dec_part);
#endif

  if (int_part < 0 || int_part >= 100) return 0; /* ei ollut hyv" luku */

  if (sign == -1) *next++ = '-';

  *next++ = digits[ int_part / 10 ];
  *next++ = digits[ int_part % 10 ];
  *next++ = '.';
  *next++ = digits[ dec_part / 10 ];
  *next++ = digits[ dec_part % 10 ];
  *next++ = 0;

  return 1;
}

int main (void) {
  char buf[10];
  if (floatstr(10.12,   buf)) puts (buf);
  if (floatstr(-15.339, buf)) puts (buf);
  if (floatstr(100.12,  buf)) puts (buf); /* ei pit"isi tulostua */

  return 0;
}
esimerkkiajo
C:\temp>gcc -DNDEBUG floatstr.c -o fs.exe -Wall

C:\temp>fs
10.11
-15.33

Kertaus: tämä ei ole hyvä tapa, eikä järkevää koodia.

Kirjoitin sen tässä aamulla pikaisesti, jotta idea tulee selväksi. Toinen tapa on tutustua floatin esitykseen binaaritasolla ja jäsentää se suoraan merkkijonoksi. Sitä en ole koskaan tehnyt, joten en todellakaan halua neuvoa miten se tehdään oikeaan sovellukseen.

Se, miten tietokone esittää floatin muistissa ei ole lähelläkään sen merkkijonoesitystä.

tim16 [04.03.2007 11:05:09]

#

moi,
sorsa tulee philipsin lpc 2148 prossalle
floatin arvoalue on 0 - 100.

Siis onnistuuko tuo minun eka sorsani tekemään tuon muutoksen hukkaamatta tietoa?

koo [04.03.2007 14:54:55]

#

Et ole tekemässä castausta vaan merkkijonon muotoilua. Homma ei onnistu tuolla ekalla koodipätkällä. Lisäksi siinä urheillaan alustamattomilla osoittimilla.

Kopeekan luonnostelma on ihan oikean suuntainen.

Pekka Karjalainen [04.03.2007 19:16:16]

#

Ohjelmanpätkässäni on ainakin se vika, että se yrittää muuttaa float-tyyppiä kokonaisluvuksi, ennen kuin tarkistaa, sopiiko se sinne. Jos se ei sovi, tulos taitaa olla "implementation dependent", eli sen toiminnasta ei ole mitään erityisiä takuita.

No, varoitinkin, että se oli kiireessä tehty.

Tim16: Kertausta C-kielen tyypeistä. Muuttujan tyyppi on käännöksenaikainen tieto ja kertoo kääntäjälle, minkälaista koodia tehdään, kun muuttujaa käsitellään jotenkin. Ajonaikana muistissa on vain ykkösiä ja nollia. Jos muutat johonkin tietoon osoittava osoittimen tyyppiä, itse tieto ei muutu, vaan vain sen tulkinta.

Castaus, eli (unsigned int) tai (float) -tyyppiset ilmaukset, kertoo kääntäjälle, että tähän paikkaan pitää laittaa koodia, joka muuttaa tyypin toiseksi. Jos esim. liukuluvun -1.0 muuttaa etumerkilliseksi kokonaisluvuksi, sille tulee arvo -1. Liukuluvun -1.0 ja kokonaisluvun -1 esitys muistissa on aivan erilainen. Sen voi katsoa esim. debuggerilla, jos haluaa nähdä. Myös kääntäjän tekemää muutoskoodia voi tutkia, jos assembly on hallussa. (Sopivalla prosessorilla se on ehkä vain yksi operaatio.)

Antti Laaksosen kirjoitus

https://www.ohjelmointiputka.net/koodivinkit/24982-qb-luku-muistissa

kertoo lukujen esityksestä QBasicin yhteydessä. Tilanne C-kielessä on aika samanlainen, mutta riippuu tietenkin jonkin verran toteutuksesta.

Merkkijonoesitys taas on ihan eri asia. Ensinnäkin sen koko voi paljon merkkijonon pituuden mukaan. float-muuttuja vie aina float-muuttujan verran tilaa, mutta sen eri arvot ovat eripituisia merkkijonomuitoisina desimaali- tai potenssiesityksinä. Yksi merkki vie yhden char-muuttujan verran tilaa ja jonon lopussa on nollaksi asetettu char-kokoinen loppumerkki.

Tästäkin on Antin kirjoitus

https://www.ohjelmointiputka.net/oppaat/opas.php?tunnus=cohj_2

Joku näistä asioista on jäänyt sinulle hieman epäselväksi, koska tarjoat tuollaista kummaa osoitinmuuttelua ratkaisuksi. Ei voi muutenkaan sanoa: se nyt vain ei toimi noin.

Voin parantaa esimerkkikoodiani ja tarjota käytettäväksi tarkoitetun version yleisesti arvioitavaksi huomisaamuun mennessä. Kannattaa vielä katsoa, että joku tarkistaa siitä pahimmat mokat (joita en aio laittaa, mutta eihän sitä tiedä...).

Metabolix [04.03.2007 22:20:37]

#

Tuolla toisaalla (poistin aiheen, kun oli melkein sama kuin tämä) kyselit myös DWORDin eli unsigned long -tyypin muuttamisesta. Siihen sattuu käyttöjärjestelmäprojektin puolesta olemaan takataskussa funktio (muokkasin vähän tähän). Se tekee tekstin globaaliin puskuriin ja palauttaa tekstin pituuden.

/* Pisin 32-bittinen luku 4294967295 on 10-numeroinen; nollatavu lasketaan mukaan tuossa edempänä. */
#define BUF_KOKO 10
char sprintf_buf[BUF_KOKO + 1];
int sprintf_uint(unsigned int num)
{
	int i = BUF_KOKO;
	if (!num) {
		sprintf_buf[0] = '0';
		sprintf_buf[1] = 0;
		return 1;
	}
	do {
		sprintf_buf[--i] = (num % 10) + '0';
		num /= 10;
	} while (num);
#if ET_OMISTA_FUNKTIOTA_MEMMOVE
	/* Tämä toivottavasti meni oikein. :P */
	int j;
	if (i) for (j = BUF_KOKO - i; j--;) {
		sprintf_buf[j] = sprintf_buf[i+j];
	}
#else
	memmove(sprintf_buf, sprintf_buf + i, BUF_KOKO - i);
#endif
	sprintf_buf[BUF_KOKO - i] = 0;
	return BUF_KOKO - i;
}

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta