[JAVA] problemen met getal na de komma/punt

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • VNA9216
  • Registratie: Mei 2004
  • Laatst online: 11-07 00:48
Ik heb gisteren al even gekeken of er iets dergelijks al op de forums rond zweeft (ik kom hier niet zo vaak) maar kon er niets over vinden.
Ik ben er nu al meerdere keren tegen aan gelopen dat java vreemde dingen kan doen als je iets berekent met getal na de komma.

een voorbeeld is
Java:
1
2
3
4
5
6
7
8
9
 
public class testnum {


    public static void main(String[] args) {
        System.out.println(0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1);
    }

}


Je zou verwachten dat dit als resultaat 1.0 heeft. Echter is dit niet het geval. Java komt doodleuk op 0.9999999999999999

Wanneer je de berekening om gooit naar floats (zelf pikt java doubles op deze manier) dan komt hij op 1.000001

Op zich is dit geen gigantisch issue, maar bijvoorbeeld als ik dit doe:

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class testnum {


    public static void main(String[] args) {
        
        double a1=0.1;
        a1=a1+0.1;
        a1=a1+0.1;
        
        if (a1==0.3){
            System.out.println("hij doet het goed");
        }
        else {
            System.out.println("de waarde in a1="+a1);
        }
    }
}


dan snap je al dat het een probleem gaat worden.
De waarde in a1 blijkt namelijk geen 0.3 maar 0.30000000000000004 te zijn.


Weet iemand wat de reden hier van is? en/of waarom dit zo is? Ik snap er zelf geen bal meer van waarom dit zo geimplementeerd is.

Ik liep er tegenaan omdat ik in een stuk code voor een programma continu met standaard debug waarden van 0.# aan het optellen was en vervolgens een resultaat kreeg met een hele stapel getallen na de komma :X

Normaal let ik er niet zo op, maar in dit geval had ik die getallen in mijn debug output opgenomen en viel het toch wel op dat je in theorie als je steeds getallen optelt met 1 getal na de komma nooit een resultaat zou kunnen krijgen met een hele stapel getallen na de komma.

Bovenstaande is wel een heel simpel voorbeeld overigen in vergelijking met wat er in mn programma gebeurt, maar het illustreert het probleem wel goed. hoewel de afwijkingen niet groot zijn kan het wel vreemde resultaten opleveren.


anyway, iemand een idee hoe en waarom? Of heb ik een simpele fout gemaakt (behalve java gebruiken natuurlijk :+ ) Of is mn java setup gewoon om zeep? (al zo het dan wel zo moeten zijn bij alle 6 machines hier in huis waar ik het op getest heb).


OS=windows (vista (64)/XP(32))
Op de vista machine de laatste JRE/JDK, op de XP bak versies van een jaar oud ongeveer.

Acties:
  • 0 Henk 'm!

  • geforce5_guy
  • Registratie: December 2001
  • Niet online
Als je hierop google vind je heel veel uitleg hierover. Het heeft te maken met de presisie van een double. En dat je het convert naar een string.

Acties:
  • 0 Henk 'm!

  • matthijsln
  • Registratie: Augustus 2002
  • Laatst online: 17-09 14:35
Dit is niet een Java specifiek probleem, bij andere programmeertalen gaat het net zo. Zie de FAQ over floats en afronding.

Acties:
  • 0 Henk 'm!

  • roeleboel
  • Registratie: Maart 2006
  • Niet online

roeleboel

en zijn beestenboel

Dit is geen probleem van java, wel een van pc's in het algemeen.
Kommagetallen zijn normaal gezien altijd onnauwkeurig op pc's, speciale implementaties daargelaten.

http://en.wikipedia.org/wiki/Floating_point is een beginpunt om je in te lezen hierover.

Acties:
  • 0 Henk 'm!

  • denyos
  • Registratie: Februari 2004
  • Laatst online: 21:26
Het is heel simpel:

What Every Computer Scientist Should Know About Floating-Point Arithmetic
http://docs.sun.com/source/806-3568/ncg_goldberg.html

Strava


Acties:
  • 0 Henk 'm!

  • Salandur
  • Registratie: Mei 2003
  • Laatst online: 23:51

Salandur

Software Engineer

dit komt doordat floating point getallen niet exact kunnen worden gemapt in het geheugen. 0.1 valt bijvoorbeeld niet goed om te zetten naar een binaire representatie, waardoor je afrondings verschillen krijgt. dit heb je in elke programmeertaal en is geen issue binnen java.

er is een IETF beschrijving van floating point getallen, maar die weet ik even niet uit mijn hoofd.

Assumptions are the mother of all fuck ups | iRacing Profiel


Acties:
  • 0 Henk 'm!

  • Niemand_Anders
  • Registratie: Juli 2006
  • Laatst online: 09-07-2024

Niemand_Anders

Dat was ik niet..

Hiervoor dient je een epsilon vergelijking te gebruiken. Deze gaat uit van de standaard afronding afwijkingen.


Java:
1
2
3
4
5
private final static double EPSILON = 1e-12;
public static bool equals(double a, double b)
{
    return Math.abs(a - b) < EPSILON;
}

If it isn't broken, fix it until it is..


Acties:
  • 0 Henk 'm!

  • VNA9216
  • Registratie: Mei 2004
  • Laatst online: 11-07 00:48
Tnx voor de info, het was me wel duidelijk dat die afwijking er was, maar waar hij vandaan kwam was me onduidelijk. Deze docs leggen het inderdaad duidelijk uit.

Het is helaas dus niet iets waar je makkelijk omheen kan werken of af kan vangen. Naja, dan moet het maar door de laatste paar getallen steeds te negeren of iets dergelijks (voor de if statements). Tnx voor de info iig. Het verklaart een hoop!

Edit: wel opvallend, net een project van 6 jaar terug bekeken waar het inderdaad zich ook al voor doet. Maar omdat die kleine afwijking niet zo heel belangrijk is en ik nooit IF gebruikte op die getallen is het me nooit opgevallen

Grappig hoe je al jaren met 2d/3d spul bezig kan zijn en dan door een simpel administratie programma onderuit kan gaan op iets dat je nog nooit opgevallen was :o

Ik ga me in ieder geval even in een hoekje zitten schamen |:(

[ Voor 36% gewijzigd door VNA9216 op 17-11-2009 12:13 ]


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 17-09 14:05

.oisyn

Moderator Devschuur®

Demotivational Speaker

Niemand_Anders schreef op dinsdag 17 november 2009 @ 11:51:
Hiervoor dient je een epsilon vergelijking te gebruiken. Deze gaat uit van de standaard afronding afwijkingen.


Java:
1
2
3
4
5
private final static double EPSILON = 1e-12;
public static bool equals(double a, double b)
{
    return Math.abs(a - b) < EPSILON;
}
Lekker handig, je vergelijkt niet eens in de orde van grootte van a en b. 1.23456e+50 kan net zo goed gerepresenteerd worden als 1.23456e-50. Alleen in het eerste geval is jouw epsilon te klein, en in het tweede geval is je epsilon veel te groot. Je moet dus schalen naar de orde van grootte van a en b.
Java:
1
2
3
4
5
private final static double EPSILON = 1e-12;
public static bool equals(double a, double b)
{
    return Math.abs(a - b) < EPSILON * (Math.abs(a) + Math.abs(b));
}

Overigens is dit alsnog te kort door de bocht, want als je het écht goed wilt aanpakken dan moet je afrondingsfouten bij gaan houdenn van elke berekening waar a en b indirect uit voortvloeien, en kom je dus niet weg met een equals functie zoals hij hier staat. Epsilon wordt dan een derde parameter, die wordt berekend uit de inputs van de berekening en het soort berekening.

Rekening houden met afrondingsfouten is niet bepaald een simpel probleem.

[ Voor 18% gewijzigd door .oisyn op 17-11-2009 12:26 ]

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.


Acties:
  • 0 Henk 'm!

  • Macros
  • Registratie: Februari 2000
  • Laatst online: 15-05 16:29

Macros

I'm watching...

Of gewoon BigDecimal gebruiken. Ik gebruik die sowieso heel vaak in dit soort gevallen.

"Beauty is the ultimate defence against complexity." David Gelernter


Acties:
  • 0 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 16-09 09:15

Janoz

Moderator Devschuur®

!litemod

.oisyn schreef op dinsdag 17 november 2009 @ 12:15:

Overigens is dit alsnog te kort door de bocht, want als je het écht goed wilt aanpakken dan moet je afrondingsfouten bij gaan houdenn van elke berekening waar a en b indirect uit voortvloeien, en kom je dus niet weg met een equals functie zoals hij hier staat. Epsilon wordt dan een derde parameter, die wordt berekend uit de inputs van de berekening en het soort berekening.

Rekening houden met afrondingsfouten is niet bepaald een simpel probleem.
Ik moet gelijk terug denken aan een vak Numerieke Wiskunde dat ik op de universiteit had. Bij elke berekening vervolgens ook de mogelijke fout bijhouden. Mooi om te zien hoe dat soms zo ver door propageerde dat het toch wel een enorme invloed had op de significantie.

Mooi 'real-life' voorbeeld van dergelijke propagatie is quake. Dat gebruikte dan wel geen floating point, maar had wel bij specifieke framerates afrondfouten die niet uitmiddelden.

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


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 17-09 14:05

.oisyn

Moderator Devschuur®

Demotivational Speaker

Macros schreef op dinsdag 17 november 2009 @ 12:40:
Of gewoon BigDecimal gebruiken. Ik gebruik die sowieso heel vaak in dit soort gevallen.
Een BigDecimal kan alsnog niet 1/3 opslaan. Dat soort classes zijn alleen handig als je getallen op zijn te slaan in decimalen. Zodra je een berekening uit gaat voeren dat verder gaat dan optellen, aftrekken en vermenigvuldigen hou je alsnog afrondingsfouten.
Janoz schreef op dinsdag 17 november 2009 @ 12:46:
Mooi 'real-life' voorbeeld van dergelijke propagatie is quake. Dat gebruikte dan wel geen floating point, maar had wel bij specifieke framerates afrondfouten die niet uitmiddelden.
Het probleem was juist dat de floatingpoint getallen van de "physics" werden geconverteerd naar int voor een compactere representatie in de network layer. En idd, bij 125fps maximaliseerde de afronding zich waardoor je verder en hoger kon springen :)

[ Voor 37% gewijzigd door .oisyn op 17-11-2009 13:19 ]

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.


Acties:
  • 0 Henk 'm!

  • Remus
  • Registratie: Juli 2000
  • Laatst online: 15-08-2021
VNA9216 schreef op dinsdag 17 november 2009 @ 10:26:
Zelf ben ik sinds gisteren van java afgestapt omdat ik er achter kwam dat java problemen heeft met rekenen ;)
Net als alle andere talen bij het rekenen met floating points...

Acties:
  • 0 Henk 'm!

  • VNA9216
  • Registratie: Mei 2004
  • Laatst online: 11-07 00:48
Remus schreef op dinsdag 17 november 2009 @ 13:28:
[...]


Net als alle andere talen bij het rekenen met floating points...
Haha, en toch heb ik er bij andere talen nooit zoveel problemen mee gehad als met java.

Net wat testjes gedaan, maar sommige dingen gaan in pascal wel goed terwijl java er onderuit gaat O-)

Maar dat is weer iets voor een ander topic :+

Acties:
  • 0 Henk 'm!

  • Laurens-R
  • Registratie: December 2002
  • Laatst online: 29-12-2024
Ik ben geintrigeerd ... kan je een voorbeeld noemen? Je moet nl wel een dusdanige precisie werken van je floating point getallen voordat je echt last van zoiets krijgt (8>

edit: no flame intended .. begrijp me niet verkeerd... ik ben gewoon benieuwd of dat zoiets voorkomt bij wetenschappelijke apps bijvoorbeeld. (hoewel ik zelf dan custom classes ala bigint zou gebruiken)

[ Voor 37% gewijzigd door Laurens-R op 17-11-2009 13:57 ]


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 17-09 14:05

.oisyn

Moderator Devschuur®

Demotivational Speaker

VNA9216 schreef op dinsdag 17 november 2009 @ 13:42:
[...]


Haha, en toch heb ik er bij andere talen nooit zoveel problemen mee gehad als met java.

Net wat testjes gedaan, maar sommige dingen gaan in pascal wel goed terwijl java er onderuit gaat O-)

Maar dat is weer iets voor een ander topic :+
Klopt. Deze topic om precies te zijn: [JAVA] problemen met getal na de komma/punt. En ja, Pascal met normale floats heeft daar net zoveel last van. Het is namelijk een intrinsiek feit van floating point getallen. Taal heeft er echt geen *zak* mee te maken. Het enige wat eventueel zou kunnen zijn is dat je in Pascal niet de standaard floating point typen hebt gebruikt om mee te rekenen (hadden die niet het type Real oid?). Wat tevens betekent dat het makkelijk een factor 100 trager kan zijn. Maar ook in Java heb je gewoon custom datatypes voor kommagetallen met (praktisch) oneindige precisie.

[ Voor 7% gewijzigd door .oisyn op 17-11-2009 14:08 ]

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.


Acties:
  • 0 Henk 'm!

  • VNA9216
  • Registratie: Mei 2004
  • Laatst online: 11-07 00:48
EvilB2k schreef op dinsdag 17 november 2009 @ 13:54:
Ik ben geintrigeerd ... kan je een voorbeeld noemen? Je moet nl wel een dusdanige precisie werken van je floating point getallen voordat je echt last van zoiets krijgt (8>

edit: no flame intended .. begrijp me niet verkeerd... ik ben gewoon benieuwd of dat zoiets voorkomt bij wetenschappelijke apps bijvoorbeeld. (hoewel ik zelf dan custom classes ala bigint zou gebruiken)
Het is geen kwestie van er "last" van hebben, maar meer dat het opviel in de debug uitvoer. Normaal gezien keek ik nooit naar die waardes, dus dan sta je er ook niet bij stil. In dit geval viel het op omdat als je bij een waarde met max 1 getal na de komma steeds 0.1 optelt er nooit een getal met 5-6 getallen na de komma uit zou moeten komen.

Ik was bezig met een projectje om gegevens te verzamelen uit een 3d "scene" en dat om te zetten naar tabellen (hoeveelheden zichtbare vlakken vanuit een perspectief etc) en liet daardoor wat waardes in de debug output zetten. Daarin viel het wel op dat die dingen nogal afweken van wat je zou verwachten.

Grappig detail is dat ik het projectje in 2 talen zoveel mogelijk identiek in elkaar draai (pascal en java) om ook de verschillen in snelheid en het aantal benodigde regels code te vergelijken.

nu valt op dat pascal het probleem niet heeft in het specifieke geval waar het me in java opviel. Vandaar mn andere topic met de vraag of iemand het kon verklaren, immers weet ik nu waar ik zou moeten zoeken.
Dat het in pascal niet voorkomt op het zelfde punt als waar java in de fout ging ziet er nu meer uit als toeval aangezien het op andere plaatsen bij pascal ook fout gaat. Of dat in mijn code zit of in hoe java/pascal intern werken weet ik (nog) niet.
Maar laten we dit topic verder maar on-topic houden voordat de mods boos worden O-)

edit: tnx voor het verplaatsen mod! zo blijft het tenminste te volgen.

Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 17-09 14:05

.oisyn

Moderator Devschuur®

Demotivational Speaker

VNA9216 schreef op dinsdag 17 november 2009 @ 14:14:
nu valt op dat pascal het probleem niet heeft in het specifieke geval waar het me in java opviel.
Het probleem heeft Pascal net zo goed. Het enige verschil is dat voor de uitvoer naar de debugger er in Pascal het getal wordt afgerond. Een verschil in representatie dus, niet een verschil in de daadwerkelijke waarde van het getal.

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.


Acties:
  • 0 Henk 'm!

  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 21:27

Creepy

Tactical Espionage Splatterer

Modbreak:Mochten er wat posts verwarrend overkomen, er zijn wat posts verplaatst hier naar toe vanuit Minimalistische Java IDE

[ Voor 4% gewijzigd door Creepy op 17-11-2009 14:26 ]

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney


Acties:
  • 0 Henk 'm!

  • matthijsln
  • Registratie: Augustus 2002
  • Laatst online: 17-09 14:35
EvilB2k schreef op dinsdag 17 november 2009 @ 13:54:
Ik ben geintrigeerd ... kan je een voorbeeld noemen? Je moet nl wel een dusdanige precisie werken van je floating point getallen voordat je echt last van zoiets krijgt (8>

edit: no flame intended .. begrijp me niet verkeerd... ik ben gewoon benieuwd of dat zoiets voorkomt bij wetenschappelijke apps bijvoorbeeld. (hoewel ik zelf dan custom classes ala bigint zou gebruiken)
Sterker nog, er zijn applicaties waar je ook met rationele en dergelijke getallen kunt rekenen zonder enkel verlies van precisie. Alleen krijg je als resultaat dan mogelijk geen getal, maar een formule met symbolen en (vereenvoudigde) breuken er nog in. Het kost echter ordes van groottes meer rekenkracht en het resultaat is soms een enorme formule.

Acties:
  • 0 Henk 'm!

  • Remus
  • Registratie: Juli 2000
  • Laatst online: 15-08-2021
VNA9216 schreef op dinsdag 17 november 2009 @ 13:42:
[...]


Haha, en toch heb ik er bij andere talen nooit zoveel problemen mee gehad als met java.

Net wat testjes gedaan, maar sommige dingen gaan in pascal wel goed terwijl java er onderuit gaat O-)

Maar dat is weer iets voor een ander topic :+
Tsja, sommige talen ronden standaard af bij uitvoer als string (iig Python doet dat geloof ik, Pascal is al te lang geleden). Daardoor kan het lijken alsof de waarde klopt, terwijl dat niet zo is. In andere woorden, je doet nu net alsof het probleem bij Java ligt.

Dat is echter niet zo: het ligt aan je beperkte begrip van hoe floating points werken, en hoe jouw taal de string presentatie afhandelt.
Pagina: 1