[C/C++] Significante cijfers

Pagina: 1
Acties:

  • Pooh
  • Registratie: April 2001
  • Niet online

Pooh

Lees eens een boek

Topicstarter
Hoe rond ik op een elegante manier een float af op een gegeven aantal significante cijfers?

Mijn huidige oplossing is iets als: (baggercode, niet op letten... Ik heb elke stap uitgeschreven om 't begrijpelijk te houden)
code:
1
2
3
4
5
6
7
8
float f = 0.06235555;
int s = 3; //aantal significante cijfers nodig
float logf = log10(f); // -1.2
int logf2 = floor(logf); // -2

float f2 = f / pow(10,logf2); // 1 cijfer voor komma, dus 6.235555
f2 *= pow(10,s-1) // s cijfers voor komma, dus 623.5555
f2 = round(f2) * pow(10,logf2); //klaar

Dit werkt prima natuurlijk, maar is erg weinig elegant. Is er een mooiere oplossing?

Verwijderd

Ja, dat is mogelijk.

Zoek eens in een C++ Standard Library reference naar numeric_limits en round_style.

Verwijderd

Ook weinig elegant en ongetest maar werkt wel denk ik?
code:
1
2
3
  char rounder[15];
  sprintf(rounder,"%.3f",number);
  number = atof(rounder);

  • Pooh
  • Registratie: April 2001
  • Niet online

Pooh

Lees eens een boek

Topicstarter
Op zondag 31 maart 2002 12:58 schreef Yarvieh het volgende:
Ook weinig elegant en ongetest maar werkt wel denk ik?
code:
1
2
3
  char rounder[15];
  sprintf(rounder,"%.3f",number);
  number = atof(rounder);
Daarmee krijg je 3 cijfers achter de komma, maar dat is dus niet wat ik wil.
0.06235555 met 3 cijfers achter komma = 0.062
0.06235555 met 3 significante cijfers = 0.0623

'k Zal die library eens doorzoeken...

Verwijderd

Kan dit niet veel simpeler opgelost worden, ik doe maar een gooi:

schrijf een case statement

case x< 0,1 and x > 0,01
afronden op 4 cijfers
case x< 0,01 and x >0,001
afronden op 5 cijfers
etc

Of wil dat niet, vaag probleem zeg :?

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 09-09 20:58

Janoz

Moderator Devschuur®

!litemod

Op zondag 31 maart 2002 13:23 schreef alexp het volgende:
Kan dit niet veel simpeler opgelost worden, ik doe maar een gooi:

schrijf een case statement

case x< 0,1 and x > 0,01
afronden op 4 cijfers
case x< 0,01 and x >0,001
afronden op 5 cijfers
etc

Of wil dat niet, vaag probleem zeg :?
De topicstarter vroeg om een elegantere oplossing :+

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


  • Pooh
  • Registratie: April 2001
  • Niet online

Pooh

Lees eens een boek

Topicstarter
Op zondag 31 maart 2002 13:23 schreef alexp het volgende:
Kan dit niet veel simpeler opgelost worden, ik doe maar een gooi:

schrijf een case statement

case x< 0,1 and x > 0,01
afronden op 4 cijfers
case x< 0,01 and x >0,001
afronden op 5 cijfers
etc

Of wil dat niet, vaag probleem zeg :?
Ehm, dat kan eventueel, maar dat levert 2 problemen op:
Ten eerste, mijn floats varieren van 500000 tot 0.00001 ofzo, dat levert dus nogal wat cases op.
Ten tweede, elegant afronden op een x aantal cijfers is ook nog niet zo eenvoudig.

Elegant is 't ook niet echt. :+

  • Pooh
  • Registratie: April 2001
  • Niet online

Pooh

Lees eens een boek

Topicstarter
Op zondag 31 maart 2002 12:56 schreef Sneechy het volgende:
Ja, dat is mogelijk.

Zoek eens in een C++ Standard Library reference naar numeric_limits en round_style.
Hmm... die levert toch alleen info over de manier waarop intern floats worden afgerond en het maximale aantal significante cijfers dat het datatype kan bevatten? Of lees ik ergens overheen?

Verwijderd

Ik heb nog even gekeken, maar ik denk toch niet dat wat jij wil in de standard library zit. :'(

Verwijderd

Op zondag 31 maart 2002 13:27 schreef Poohbear het volgende:

[..]

Ehm, dat kan eventueel, maar dat levert 2 problemen op:
Ten eerste, mijn floats varieren van 500000 tot 0.00001 ofzo, dat levert dus nogal wat cases op.
Ten tweede, elegant afronden op een x aantal cijfers is ook nog niet zo eenvoudig.

Elegant is 't ook niet echt. :+
Dan doe je dat in versie 2.0 :+

  • Pooh
  • Registratie: April 2001
  • Niet online

Pooh

Lees eens een boek

Topicstarter
Op zondag 31 maart 2002 13:31 schreef Sneechy het volgende:
Ik heb nog even gekeken, maar ik denk toch niet dat wat jij wil in de standard library zit. :'(
Vermoedde ik al. :P (ben al paar dagen op zoek naar oplossing, kon me niet voorstellen zoiets simpels over 't hoofd te hebben gezien).

Nouja, iemand een idee hoe mijn logaritme-oplossing mooier en eventueel ook sneller (ik kom er net achter dat ik 'm ook wel erg vaak moet aanroepen) kan?

En dus niet aankomen met:
code:
1
2
float tmp = pow(10,floor(log10(f)));
float f2 = round((f /  tmp) * pow(10,s-1)) * tmp;

Want dat snap ik ook nog wel. :P

Verwijderd

In ieder geval een manier om de log10 te omzeilen :

int d = 0;
float f2 = abs(f); //werkt ie ook met negatieve getallen
int highlim = pow(10,s); //voor performance vooraf uitrekenen
int lowlim = pow(10,s-1);//idem
while(f2 < lowlim || f2 >= highlim)
{
if (f2 >= highlim)
{
f2/=10;
d--;
}
else
{
f2*=10;
d++;
}
}
f = round(f2) * sign(f)/ pow(10,d);

T'is iets meer code, maar je bent van die log10 af (die zijn niet best voor de performance), en nu werkt ie ook met negatieve getallen (doet ie in jouw originele code niet)
Let wel op dat een log10 in extreme gevallen sneller zal zijn (als het aantal cijfers voor de komma in f veel afwijkt van het aantal significante cijfers)

edit : puntkomma achter while{} weggehaald en op de juiste plekken toegevoegd :o

  • RdeTuinman
  • Registratie: Mei 2001
  • Laatst online: 16-08 07:25
Misschien dat dit nog iets is. Het is iig voor het afdrukken, maar met een beetje sprintf en atof kun je het ook wel als toekenning gebruiken:
code:
1
2
3
4
5
6
7
8
9
#include <stdio.h>

int main()
{
   // Afronden op 2 significante cijfers
   printf ("345631.516 wordt: %.2g\n", 345631.516);
   printf ("0.00031516 wordt: %.2g\n", 0.00031516);
   return 0;
}

Uitvoer:
code:
1
2
345631.516 wordt: 3.5e+05
0.00031516 wordt: 0.00032

  • TlighT
  • Registratie: Mei 2000
  • Laatst online: 22-03 10:40
Bedoel je dit?
code:
1
2
3
4
5
6
7
8
float f = 0.06235555;
int s = 3;

double p;
p = pow(10, s+1);
f = floor(f * p) / p;

printf("%f\n", f); // 0.062300

't zal wel niet :)

  • Pooh
  • Registratie: April 2001
  • Niet online

Pooh

Lees eens een boek

Topicstarter
Op zondag 31 maart 2002 18:38 schreef TlighT het volgende:
Bedoel je dit?
code:
1
2
3
4
5
6
7
8
float f = 0.06235555;
int s = 3;

double p;
p = pow(10, s+1);
f = floor(f * p) / p;

printf("%f\n", f); // 0.062300

't zal wel niet :)
Huh???

edit: Ohnee... lijkt heel er mooi, maar wat jij doet is gewoon afronden op 4 cijfers achter de komma toch?

  • Pooh
  • Registratie: April 2001
  • Niet online

Pooh

Lees eens een boek

Topicstarter
quote ipv edit

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 09-09 22:48

.oisyn

Moderator Devschuur®

Demotivational Speaker

Op zondag 31 maart 2002 20:59 schreef Poohbear het volgende:

[..]

Huh???

edit: Ohnee... lijkt heel er mooi, maar wat jij doet is gewoon afronden op 4 cijfers achter de komma toch?
en hij rondt ook nog eens af naar beneden ipv naar de dichtstbijzijnde int (wat je met round () doet) :)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Pooh
  • Registratie: April 2001
  • Niet online

Pooh

Lees eens een boek

Topicstarter
Op zondag 31 maart 2002 21:07 schreef OiSyN het volgende:
en hij rondt ook nog eens af naar beneden ipv naar de dichtstbijzijnde int (wat je met round () doet) :)
Dat zag m'n vriendin zelfs... maar je moet niet op elke slak zout leggen... ;)

  • TlighT
  • Registratie: Mei 2000
  • Laatst online: 22-03 10:40
Op zondag 31 maart 2002 21:07 schreef OiSyN het volgende:

[..]

en hij rondt ook nog eens af naar beneden ipv naar de dichtstbijzijnde int (wat je met round () doet) :)
Tja, maar dan doe je gewoon:
code:
1
f = floor(f * p + 0.5) / p;

maar dat heeft weinig zin als dat niet is wat de vraagsteller bedoelt... :)

* TlighT zal zich niet meer met moeilijke afrondings-vraagjes bemoeien :P

Verwijderd

//
// dValue = value to round
// dSmallest = lowest value (e.g. 0.001)
//
double roundit(double dValue, double dSmallest)
{
return floor( ( dValue + ( dSmallest / 2.0 ) ) / dSmallest ) * dSmallest;
}

Have fun!

  • Pooh
  • Registratie: April 2001
  • Niet online

Pooh

Lees eens een boek

Topicstarter
Op zondag 31 maart 2002 21:22 schreef GentleGiant het volgende:
//
// dValue = value to round
// dSmallest = lowest value (e.g. 0.001)
//
double roundit(double dValue, double dSmallest)
{
return floor( ( dValue + ( dSmallest / 2.0 ) ) / dSmallest ) * dSmallest;
}

Have fun!
Ja leuk... dat is mijn oplossing zonder log10, maar hoe kom jij ooit aan die dSmallest? :?
Zoals ik al zei, mijn floats varieren van 500000 tot 0.00001 geloof ik.

Verwijderd

Is dit niks ?
code:
1
2
3
4
5
6
7
float round_significant (float f, int s)
{
  if (f < 0)
    return -round_significant(-f, s);
  int t = pow(10, s - 1 - floor(log10(f)));
  return floor(f * t + .5) / t;
}

Lijkt nogal op wat je had, maar scheelt een functie-aanroepje.

.edit:

Uiteraard moeten er een zooitje 'using std::'s bij, en nog een mooie exception voor verkeerde input (0 in f is lache ;)).

.edit2:

En misschien kun je voor optimale efficiëncy die machten van 10 in een lookup table gooien, maar dat laat ik verder over aan de fanatiekelingen ;).

Verwijderd

Op zondag 31 maart 2002 21:14 schreef TlighT het volgende:
Tja, maar dan doe je gewoon:
code:
1
f = floor(f * p + 0.5) / p;
Maar dat gaat weer buggen met negative getallen >:)

Verwijderd

Op zondag 31 maart 2002 21:25 schreef Poohbear het volgende:

[..]

Maar hoe kom jij ooit aan die dSmallest? :?
Zoals ik al zei, mijn floats varieren van 500000 tot 0.00001 geloof ik.
Je vroeg zelf "Hoe rond ik op een elegante manier een float af op een gegeven aantal significante cijfers?" Jij bepaalt sSmallest dus zelf! Zijnde 0.001 in je voorbeeld bij 3 significate cijfers.

Het leuke van deze formule is dat je ook kan afronden op bijvoorbeeld 0.05.

  • Pooh
  • Registratie: April 2001
  • Niet online

Pooh

Lees eens een boek

Topicstarter
Op zondag 31 maart 2002 21:31 schreef GentleGiant het volgende:

[..]

Je vroeg zelf "Hoe rond ik op een elegante manier een float af op een gegeven aantal significante cijfers?" Jij bepaalt sSmallest dus zelf! Zijnde 0.001 in je voorbeeld bij 3 significate cijfers.

Het leuke van deze formule is dat je ook kan afronden op bijvoorbeeld 0.05.
Dat is dus niet waar... zie de rest van het topic. Ik wil niet afronden op x cijfers achter de komma, maar op x significante cijfers.

0.06235555 op 3 cijfers achter komma = 0.062;
0.06235555 met 3 significante cijfers = 0.0624;

Of voor jouw voorbeeld: Als ik al mijn getallen nu af wil ronden op 3 significante cijfers, en ik geef als values bijv. 12345.678 en 0.0012345678, wat moet mijn dSmallest dan zijn?

  • TlighT
  • Registratie: Mei 2000
  • Laatst online: 22-03 10:40
Op zondag 31 maart 2002 21:36 schreef Poohbear het volgende:

[..]

Dat is dus niet waar... zie de rest van het topic. Ik wil niet afronden op x cijfers achter de komma, maar op x significante cijfers.

0.06235555 op 3 cijfers achter komma = 0.062;
0.06235555 met 3 significante cijfers = 0.0624;
Maar dat is toch gewoon (naar boven) afronden op 4 cijfers achter de komma??? :?
Of voor jouw voorbeeld: Als ik al mijn getallen nu af wil ronden op 3 significante cijfers, en ik geef als values bijv. 12345.678 en 0.0012345678, wat moet mijn dSmallest dan zijn?
0.0001???

Verwijderd

Even voor iedereen die niet precies weet wat afronden op significante cijfers is (ik wist het ook niet..):

Dr. Math -> Significant Digits

  • Pooh
  • Registratie: April 2001
  • Niet online

Pooh

Lees eens een boek

Topicstarter
Op zondag 31 maart 2002 21:42 schreef TlighT het volgende:

[..]

Maar dat is toch gewoon (naar boven) afronden op 4 cijfers achter de komma??? :?
[..]

0.0001???
Nee... bij mijn tweede voorbeeld, 12345.678, moet er 12300 uitkomen...

  • RdeTuinman
  • Registratie: Mei 2001
  • Laatst online: 16-08 07:25
Is er nog naar mijn reply gekeken? Of doe ik dit voor jan l*l? :)

Maargoed, ik voer dit voorbeeld dan maar aan. Je zou het bijvoorbeeld eens kunnen proberen ;)
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>

float roundAtSignificant(float number, int s);

int main()
{
      float f = 12345.678;
      float f2;

      f2 = roundAtSignificant(f,3); // rond f af op 3 significante cijfers
      return 0;
}

float roundAtSignificant(float number, int s) {
      char format[4],uitkomst[15];
      sprintf(format,"%%.%dg",s); // zet de juiste formaatstring (bijvoorbeeld %.3g)
      sprintf (uitkomst,format,number); // gebruik dat formaat om de string te printen
      return atof(uitkomst); // return de float-waarde (eigenlijk double, maargoed)
}

Verwijderd

Op maandag 01 april 2002 15:04 schreef RdeTuinman weer een heel interessant, maar helaas compleet irrelevant verhaal
Je weet niet wat we met significante cijfers bedoelen, zie de link die ik enkele replies hierboven postte.

  • RickN
  • Registratie: December 2001
  • Laatst online: 14-06-2025
Op maandag 01 april 2002 15:34 schreef Sneechy het volgende:

[..]

Je weet niet wat we met significante cijfers bedoelen, zie de link die ik enkele replies hierboven postte.
Als jij nou z'n reactie eens goed zou lezen....

Hij heeft namelijk gewoon gelijk.

Zie hier voor de relevantie van zijn verhaal.

He who knows only his own side of the case knows little of that.


Verwijderd

Op maandag 01 april 2002 15:39 schreef RickN het volgende:

[..]

Als jij nou z'n reactie eens goed zou lezen....

Hij heeft namelijk gewoon gelijk.

Zie hier voor de relevantie van zijn verhaal.
Zijn voorbeeldcode crasht hier op mijn systeem (WinXP, BC++5.6) :).

.edit: Wat grotere buffers hielp (C :r). Maargoed, dit blijkt inderdaad een mogelijke oplossing te zijn, maar natuurlijk zowieso niet eleganter/sneller dan de topicstarter's of mijn oplossing.

  • brammetje
  • Registratie: Oktober 2000
  • Laatst online: 12-01-2025
Op zondag 31 maart 2002 21:49 schreef Poohbear het volgende:

[..]

Nee... bij mijn tweede voorbeeld, 12345.678, moet er 12300 uitkomen...
nee, dat is niet helemaal waar..

er moet 1,23 E4 uitkomen..

Leuk probleem, maar ik heb geen idee :)
Pagina: 1