[C++] integers uit file optellen gaat fout

Pagina: 1
Acties:
  • 255 views sinds 30-01-2008
  • Reageer

  • Snow_King
  • Registratie: April 2001
  • Laatst online: 22:11

Snow_King

Konijn is stoer!

Topicstarter
Hallo,

Ik heb een stukje C++ code geschreven waarmee ik een bytes log van Apache kan optellen.

Zo'n log is simpelweg een file met op elke regel het aantal bytes van een requests.

Voorbeeldje:
code:
1
2
3
4
5
6
343
446
782
6843
947
1463


Mijn probleem is nu, als er te veel regels zijn in dat bestand wordt het totaal te groot voor mijn integer "total".

Mijn code
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>
#include <fstream>
using namespace std;

int main(int argc, char* argv[]) {
    
    /* Controleren of we wel genoeg input krijgen */
    if (argc < 2) {
        cout << "Usage: " << argv[0] << " FILE" << endl << "Returns a summary of a byteslog (in MegaBytes)" << endl;
        return 0;   
    }
    
    /* Enkele variabelen declareren */
    char line[256];
    int total = 0;
    int bytes = 0;
    
    /* File openen */
    fstream byteslog(argv[1], ios::in);
    
    /* Controleren of het bestand open is */
    if (!byteslog.is_open()) {
        cout << "Error opening file" << endl;
        return 0;
    }
    
    /* Regel voor regel door bestand heenlopen en optellen bij het totaal */
    while(byteslog >> line) {
        /* Regel casten naar een int */
        bytes = atoi(line);
        
        /* Regel delen tot een MegaByte en toevoegen aan het totaal */
        total += (bytes / 1024 / 1024);
    }
 
    /* Bestand sluiten */
    byteslog.close();
    
    /* Totaal weergeven (in MB's) */
    cout << total << endl;

    return 0;
}


Ik doe al expres het volgende:
code:
1
total += (bytes / 1024 / 1024);


Zo wilde ik de grootte van "total" tegen gaan, maar alsnog gaat het fout.

Bij te veel rijen komt er gewoon 0 als total uit.

Nu is mijn vraag, ligt dit probleem bij het feit dat ik een té groot getal in die integer wil stoppen? Of maak ik ergens anders een fout in mijn code?

  • Opi
  • Registratie: Maart 2002
  • Niet online

Opi

Gezien de groottes van de waarden in je voorbeeld kan ik me goed voorstellen dat een overgroot deel naar 0 afgerond zal worden. Is het niet mogelijk om een tweetal counters te maken; eerst tot de 1024*1024 en op het moment dat die vol is een andere counter +1 doen?

  • zwippie
  • Registratie: Mei 2003
  • Niet online

zwippie

Electrons at work

Ik ken eigenlijk geen C++ maar als een gewone int te klein is kun je misschien een unsigned long int gebruiken.

Nog iets: één byte is 8 bits, dus als er 800 bytes gelogd worden en je deelt dit twee maal door 1024, dan blijft er uiteraard weinig over (oftwel nul als het een int wordt). Dit delen kun je beter pas doen bij het weergeven van het totaal.

How much can you compute with the "ultimate laptop" with 1 kg of mass and 1 liter of volume? Answer: not more than 10^51 operations per second on not more than 10^32 bits.


  • NMe
  • Registratie: Februari 2004
  • Laatst online: 22-01 23:51

NMe

Quia Ego Sic Dico.

Je gebruikt een signed int, en die gaan maar tot 215-1 = 32767*. Waarom gebruik je geen unsigned (negatieve waarden komen niet voor) long? Of een unsigned long long zelfs. :)

* Op de meeste systemen is dit in elk geval zo geïmplementeerd, al schijnt er geloof ik geen zekerheid voor te zijn. :P

[ Voor 29% gewijzigd door NMe op 19-03-2006 16:52 ]

'E's fighting in there!' he stuttered, grabbing the captain's arm.
'All by himself?' said the captain.
'No, with everyone!' shouted Nobby, hopping from one foot to the other.


  • CyBeR
  • Registratie: September 2001
  • Niet online

CyBeR

💩

Of gebruik een van de vele math libraries die om kunnen gaan met grote getallen. Verder zie ik dat je om de een of andere reden standaard ints gebruikt. Wat is er mis met uint128? (Meestal getypedef'ed als unsigned long long int).

edit:
Wat NME zegt dus :P

[ Voor 8% gewijzigd door CyBeR op 19-03-2006 16:40 ]

All my posts are provided as-is. They come with NO WARRANTY at all.


  • Snow_King
  • Registratie: April 2001
  • Laatst online: 22:11

Snow_King

Konijn is stoer!

Topicstarter
Ik ben een "C++ noob"

Ik ben pas een paar dagen C++ code aan het maken.

Kan dus beter doen:
code:
1
unsigned int total = 0;


En voordat ik ga optellen eerst kijken of ik wel iets op te tellen heb bij total?

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 00:46
-NMe- schreef op zondag 19 maart 2006 @ 16:37:
Je gebruikt een signed int, en die gaan maar tot 215-1 = 32767*. Waarom gebruik je geen unsigned (negatieve waarden komen niet voor) long? Of een unsigned long long zelfs. :)

* Op de meeste systemen is dit in elk geval zo geïmplementeerd, al schijnt er geloof ik geen zekerheid voor te zijn. :P
De int is gegarandeerd 16 bits, maar op de meest gangbare systemen 32 bits. Hoogstwaarschijnlijk kan de TS dus gewoon tot ~2 miljard tellen (231-1). Neemt niet weg dat het waarschijnlijk handig is om een long long of een unsigned long long te nemen, maar dat zal hier het probleem nog niet zijn.

Mocht je buiten het bereik komen, dan is de kans heel klein dat je op 0 uitkomt; er wordt namelijk gewoon doorgeteld. Voor een signed integer kom je eerst op een negatief getal uit (als je niet direct een heel groot getal optelt). Als je op 0 uitkomt is er dus waarschijnlijk iets anders aan de hand.

Ik denk dat Opi een goed punt heeft: zitten er überhaupt waarden groter dan 1024*1024 in de invoer? Anders rond je alles naar 0 af en komt het totaal natuurlijk ook nooit boven de 0 uit.

Sowieso is dit een typisch geval van simpelweg debuggen; zet eens een breakpoint in de lus en kijk wat je invoer is en wat er met total gebeurt. Bij gebrek aan een debugger kun je per iteratie de waarde van bytes en total printen. Je ziet dan snel genoeg waar het mis gaat.

[ Voor 12% gewijzigd door Soultaker op 19-03-2006 17:03 ]


  • Snow_King
  • Registratie: April 2001
  • Laatst online: 22:11

Snow_King

Konijn is stoer!

Topicstarter
Soultaker schreef op zondag 19 maart 2006 @ 17:00:
[...]

De int is gegarandeerd 16 bits, maar op de meest gangbare systemen 32 bits. Hoogstwaarschijnlijk kan de TS dus gewoon tot ~2 miljard tellen (231-1). Neemt niet weg dat het waarschijnlijk handig is om een long long of een unsigned long long te nemen, maar dat zal hier het probleem nog niet zijn.

Mocht je buiten het bereik komen, dan is de kans heel klein dat je op 0 uitkomt; er wordt namelijk gewoon doorgeteld. Voor een signed integer kom je eerst op een negatief getal uit (als je niet direct een heel groot getal optelt). Als je op 0 uitkomt is er dus waarschijnlijk iets anders aan de hand.

Ik denk dat Opi een goed punt heeft: zitten er überhaupt waarden groter dan 1024*1024 in de invoer? Anders rond je alles naar 0 af en komt het totaal natuurlijk ook nooit boven de 0 uit.

Sowieso is dit een typisch geval van simpelweg debuggen; zet eens een breakpoint in de lus en kijk wat je invoer is en wat er met total gebeurt. Bij gebrek aan een debugger kun je per iteratie de waarde van bytes en total printen. Je ziet dan snel genoeg waar het mis gaat.
Goed punt, het zijn allemaal dingen die na de deling kleiner dan 0 worden.

Het is niet voor niets een byteslog ;)

Hier had ik uiteraard ook zelf op kunnen komen.

  • MrBucket
  • Registratie: Juli 2003
  • Laatst online: 29-10-2022
Soultaker schreef op zondag 19 maart 2006 @ 17:00:
Ik denk dat Opi een goed punt heeft: zitten er überhaupt waarden groter dan 1024*1024 in de invoer? Anders rond je alles naar 0 af en komt het totaal natuurlijk ook nooit boven de 0 uit.
Wat je hier evt. tegen zou kunnen doen is om twee tellers bij te houden: een voor het aantal gehele MB's, en een voor alles onder de 1 MB. Op het moment dat die laatste waarde boven de 1 MB komt, dan heb je dus een of meer MB's erbij, en dus kan je het totaal aantal MB's verhogen en het aantal bytes verlagen:

(n.b. niet getest)
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
unsigned int iMegsRead = 0;
unsigned int iBytesRead = 0;
unsigned int iCount;

/* ...Lees nieuwe waarde in als iCount... */

//Voeg iCount toe aan het totaal
iBytesRead += iCount;

if(iBytesRead > 1024 * 1024){
  //Boven de 1 MB, tel het aantal hele MBs op bij het totaal aantal MB's...
  int iMegsAdded = iBytesRead / (1024 * 1024);
  iMegsRead += iMegsAdded;

  //...en verminder iBytesRead met hetzelfde aantal bytes, zodat het weer onder de 1MB komt
  iBytesRead -= iMegsAdded * (1024 * 1024)
}


Dit gaat goed zolang er geen waarde wordt ingelezen groter dan MAX_UINT - 1024*1024. Er vanuitgaande dat een unsigned int 32 bits is, ben je veilig met bestanden kleiner dan 4293 MB

  • bobo1on1
  • Registratie: Juli 2001
  • Laatst online: 19-10-2025
Je zou ook een unsigned long double kunnen gebruiken, dit type is wel minder nauwkeurig maar je kunt er wel veel grotere getallen in opslaan.

Impedance, a measure of opposition to time-varying electric current in an electric circuit.
Not to be confused with impotence.


  • Snow_King
  • Registratie: April 2001
  • Laatst online: 22:11

Snow_King

Konijn is stoer!

Topicstarter
Ik heb het opgelost met een unsigned long.

Nu werkt het probleemloos.

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 10-12-2025
In elk geval werkt het nu twee keer zo lang, ja.

Unsigned long double bestaat niet. long double wel, maar dat heeft andere problemen. Er is namelijk een long double ld die zo groot is dat (ld + 343) == ld. long double heeft op sommige systemen bijvoorbeeld 12 significante cijfers. Als ld > 1015 dan is 343 kleiner dan het twaalfde significante cijfer.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • igmar
  • Registratie: April 2000
  • Laatst online: 23-02 20:52

igmar

ISO20022

Snow_King schreef op zondag 19 maart 2006 @ 16:23:
code:
1
2
    int total = 0;
    int bytes = 0;
int is een 32 bits integer op de meeste systemen, en over het algemeen een signed versie (al hangt dat af van je compiler).

Ik zou bovenstaande vervangen in unsigned long long, of een u_int64_t. Da's 2^64-1 voor de maximale waarde. Dat zou je probleem voor een groot gedeelte op moeten lossen.

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 01:22

.oisyn

Moderator Devschuur®

Demotivational Speaker

int is altijd signed. typeid(int) == typeid(signed int). Dit itt char, die idd een ongedefinieerde signedness heeft (en daarom ook ongelijk is aan zowel signed char als unsigned char)

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.


  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 24-02 13:08

Janoz

Moderator Devschuur®

!litemod

bobo1on1 schreef op zondag 19 maart 2006 @ 21:44:
Je zou ook een unsigned long double kunnen gebruiken, dit type is wel minder nauwkeurig maar je kunt er wel veel grotere getallen in opslaan.
Dat werkt niet. Omdat je veel kleine getallen optelt zullen die waarden op een gegeven moment buiten de significantie gaan vallen waardoor het totaal niet meer wordt opgehoogd.

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


  • splrf
  • Registratie: November 2005
  • Laatst online: 24-01 13:34
Met die int64 moet het toch wel lukken. Tenzij je over de.. ehm, wat komt er na petabyte? :P

  • WormLord
  • Registratie: September 2003
  • Laatst online: 20-02 12:15

WormLord

Devver

splrf schreef op maandag 20 maart 2006 @ 23:08:
Met die int64 moet het toch wel lukken. Tenzij je over de.. ehm, wat komt er na petabyte? :P
offtopic:
* Exabyte
Pagina: 1