Archief - [PROG] Java probleem

Het archief is een bevroren moment uit een vorige versie van dit forum, met andere regels en andere bazen. Deze posts weerspiegelen op geen enkele manier onze huidige ideeën, waarden of wereldbeelden en zijn op sommige plaatsen gecensureerd wegens ontoelaatbaar. Veel zijn in een andere tijdsgeest gemaakt, al dan niet ironisch - zoals in het ironische subforum Off-Topic - en zouden op dit moment niet meer gepost (mogen) worden. Toch bieden we dit archief nog graag aan als informatiedatabank en naslagwerk. Lees er hier meer over of start een gesprek met anderen.

jodeman

Legacy Member
Kn0t zei:
Je zal inderdaad wel in centen moeten rekenen, ofwel met BigDecimals moeten werken indien je wil dat je programma juist werkt.

Laat het programma van Jodeman maar eens het wisselgeld voor 1.2 berekenen, en hij zal je slechts 1.19 teruggeven.

Oorzaak van het probleem is dat 1.2 - 1.0 niet 0.2 maar 0.19999999999999996 geeft.

Ah yep, stom eigenlijk. Waarom lossen ze dat probleem niet op in java?

wlibaers

Legacy Member
jodeman zei:
Ah yep, stom eigenlijk. Waarom lossen ze dat probleem niet op in java?

Het is geen probleem in Java, het is een eigenschap van standaard floating point getallen. Het probleem is dat sommige mensen zonder kennis van zaken met floating point gaan werken en verwachten dat alles meteen correct werkt zonder dat ze erover moeten nadenken. Floating point getallen zijn erg handig, maar er zijn beperkingen en in bepaalde situaties kan dat tot ongewenste resultaten leiden. En soms (zoals hier) zijn er betere alternatieven (in cent rekenen)

Daar wil ik meteen aan toevoegen dat een goede analyse van floating point berekeningen moeilijk kan zijn, en dat iemand die er pas mee begint natuurlijk niet van alle valstrikken op de hoogte kan zijn. Bovenstaande commentaar is dus zeker niet bedoeld als een persoonlijke aanval op de threadstarter. De juiste manier om af te ronden had vermeld moeten worden in de oefening, of de oefening beperkt tot gehele getallen.

Er is een tijdje geleden wel kritiek geweest op floating point in Java, maar die was van een heel andere aard.
http://www.cs.berkeley.edu/~wkahan/JAVAhurt.pdf

jodeman

Legacy Member
Ok, dan volgende vraag, waarom Java wel en al de rest van de programmeertalen (die ik ken) niet?

logisch zou zijn -> double dat correct afrond, klasse voor floating point

MilM

Legacy Member
jodeman zei:
Ok, dan volgende vraag, waarom Java wel en al de rest van de programmeertalen (die ik ken) niet?

logisch zou zijn -> double dat correct afrond, klasse voor floating point
Java kan niet weten wat correct is. (en hoe correct af te ronden)
Hij slaat de getallen op, maar kan deze niet in perfecte precisie opslaan.
Er gaat dus correctheid verloren.

Je zou moeten zien hoe een FP getal binair wordt opgeslagen met tekenbit, mantisse en exponent en dan zou je het zien.

Maar Floating point zijn vlottende kommagetallen. Getallen waarbij de komma op een willekeurige plaats kan staan. Dit is hier niet het geval.
Hier hebben we vaste kommagetallen. (als je 100 euro als 100,00 bekijkt)
Dus je gaat een klein getal met vaste kommagetal gaan opslaan in een FP dat bedoeld is voor een zeer groot bereik van getallen te kunnen opslaan. Wegens dit grote bereik kan hij zoiezo enorm veel getallen niet precies opslaan.

Gewoon dus zorgen dat je in centen werkt of een voorstelling met vaste komma zoeken.
Je kunt trouwens ook gewoon twee int's bijhouden in de plaats: één met centen en één met euro's.

beware

Legacy Member
lijkt verdacht veel op mijn oefening :D.Toevallig 1e bach informatica/fysica/wiskunde in leuven ? :evil:

killgore

Legacy Member
jodeman zei:
Ok, dan volgende vraag, waarom Java wel en al de rest van de programmeertalen (die ik ken) niet?

logisch zou zijn -> double dat correct afrond, klasse voor floating point
onmogelijk, floating point is gemaakt om oa "reële" getallen op te slaan, het probleem van deze is dat deze onaftelbaar zijn en dus ONMOGELIJK vast te leggen zijn met eindig computergeheugen, hoe je ook probeert.
Daarnaast is floating-point ook zeer handig om vreselijk grote (of juist vreselijk kleine) getallen op te slaan, met natuurlijk het bijkomende verlies van exactie. Voor slechts 64bit heeft een floating-point getal een vrij "extreme" reikwijdte, de 128-bit versies zijn voor zowat alle normale toepassingen (niet-wetenschappelijke) meer dan voldoende. (floating-point is gebaseerd op de wetenschappelijke notatie van getallen, maar dan in het 2-tallig talstelsel ipv het 10-tallig).

Rationale getallen kan je in principe wel opslaan met "eindig" geheugen, door b.v. gewoon 2 (big)ints te nemen (1 voor teller, 1 voor noemer).

Een getal als 0,33333 kan je b.v. in principe gewoon opslaan als het koppel (1,3) of dus de breuk 1/3.
Het getal Pi echter kan je onmogelijk exact opslaan (en kent ook niemand :p).

Let op: met eindig geheugen bedoel ik niet dat er een eindig geheugen bestaat dat alle rationale getallen kan voorstellen. Je kan echter wel, in principe, voor elk rationaal getal dat je tegenkomt een geheugen creëren om het exact op te slaan. Dit zal echter voor bepaalde kommagetallen immens groot zijn.

Als je zuivere exactie wilt voor rationale getallen raad ik je aan om een dergelijk systeem te implementeren of, wat vrij analoog is, een fixed-point systeem maken, maar dat is dan weer niet handig voor oneindig lange rationale getallen zoals 1/3, wel om b.v. het getal 23495,2523 op te slaan (de eerste 16 bits b.v. voor waarden -32768 ->32767, de laatste 16 bits voor een kommagetal tot 4 cijfers lang).
Nog een andere manier voor "exacter" kommagetallen is eigenlijk het floating point te verbeteren, waar je een deel van je getal als een gewoon getal zal opslaan (geheel getal!) en dan daarbij een macht van 10 (of 2 als je bitsgewijs werkt) waarmee je moet vermenigvuldigen (of door moet delen).

edit: de nadelen aan zo een eigen systeem:
-Het is groot, voor ietwat deftige exactie heb je al snel vrij veel bits nodig.
-Het is niet universeel
-de berekeningen worden op programmatorisch niveau geïmplementeerd, met behulp van bestaande processorinstructies, ze worden niet op een (sneller!) chip-niveau gecreëerd, wat dus meestal resulteert in een tragere rekenset (hoewel dit natuurlijk afhangt van de exactie die je wilt bereiken).

jodeman

Legacy Member
ehh, allemaal goed, ik heb het over het volgende

C#
double test = 1.2 - 1.0;
Console.WriteLine(test); // 0.2

C++
double test = 1.2 - 1.0;
cout << test; // 0.2

Java
double test = 1.2 - 1.0;
System.out.println(test); // 0.19999999999999996


Als het onmogelijk is, waarom lukt het dan in elke programmeertaal behalve in Java.

killgore

Legacy Member
not really, in c++ gaat die cout de scherm-output afronden afaik, niet de interne storage. Daarom wordt er ook aangeraden om in c++ wiskundige systemen met floating points altijd met vergelijkingen (<,>) te evalueren ipv exacte gelijkheden.

Er zijn (cout) vbn in c++ waar de fouten ook naar buiten komen hoor. Je kan bv. ook al eens de precision van je cout hoger zetten, maar weet niet of dat echt veel zal helpen.

Yngwie

Legacy Member
btw die oefening, is he tniet gewoon op te lossen door een integer deling te maken met het grootst mogelijke biljet, en elke keer door te rekenen met de rest dus

Code:
int bedrag, aantal500, aantal200, ... ;

aantal500 = bedrag / 500;
bedrag = bedrag % 500;

aantal200 = bedrag / 200;
bedrag = bedrag % 200;

...

Zo heb ik het toch gezien in 1TI int rega in leuven
Het archief is een bevroren moment uit een vorige versie van dit forum, met andere regels en andere bazen. Deze posts weerspiegelen op geen enkele manier onze huidige ideeën, waarden of wereldbeelden en zijn op sommige plaatsen gecensureerd wegens ontoelaatbaar. Veel zijn in een andere tijdsgeest gemaakt, al dan niet ironisch - zoals in het ironische subforum Off-Topic - en zouden op dit moment niet meer gepost (mogen) worden. Toch bieden we dit archief nog graag aan als informatiedatabank en naslagwerk. Lees er hier meer over of start een gesprek met anderen.
Terug
Bovenaan