[JAVA] possible loss of precision

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • BlaTieBla
  • Registratie: November 2000
  • Laatst online: 22-09 11:02

BlaTieBla

Vloeken En Raak Schieten

Topicstarter
Ik heb afgelopen weekend de JAVA taal eens proberen op te pakken. Ik ben begonnen met iets simpels dat ik steeds verder probeer uit te bouwen.

Ik ben begonnen met een eenvoudige gallon naar liter converter. 1 gallon is 3,7854 liter. Op zich niet zo spannend, maar als ik een lijst maak, dan blijken er waardes tussen te zitten die niet kloppen.

Gebruikte software:
- IntelliJ Idea 8 (trail) op OSX 10.5.6
- java version "1.5.0_16"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_16-b06-284)
Java HotSpot(TM) Client VM (build 1.5.0_16-133, mixed mode, sharing)

Probleem doet zich voor op een Intel Based MacBook en een Intel Based iMac (zelfde OS en Java versie).

Java:
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
class GalToLit {
    public static void main(String args[]) {

        if (args.length == 2) {
            double liters = 0; // holds the number of gallons
            int c, counter = 0, gallons = 0;


            try {
                gallons = Integer.parseInt(args[0]);
                counter = Integer.parseInt(args[1]);
            }
            catch(NumberFormatException e) {
                System.out.println("Invalid parameter format");
            }
            for (c = 1; c <= counter; c++) {
                liters = c * gallons * 3.7854; // convert to liters
                System.out.println(c * gallons + " gallons is " + liters + " liters.");
            }

        }
        else {
            System.out.println("Amount in liters required or you forgot the counter.");
        }
    }
}

Bij gebruik van het volgende commando java GalToLit 1 10
krijg ik de volgende output
code:
1
2
3
4
5
6
7
8
9
10
1 gallons is 3.7854 liters.
2 gallons is 7.5708 liters.
3 gallons is 11.356200000000001 liters.
4 gallons is 15.1416 liters.
5 gallons is 18.927 liters.
6 gallons is 22.712400000000002 liters.
7 gallons is 26.4978 liters.
8 gallons is 30.2832 liters.
9 gallons is 34.0686 liters.
10 gallons is 37.854 liters.

De uitkomsten 3 en 6 wijken duidelijk af van de rest. Iets wat in mijn ogen niet zou moeten kunnen.

Er wordt een string to integer conversie uitgevoerd in de code, maar dat zou niet mogen omdat een integer geen decimalen kent. Overigens heb ik dezelfde afwijkingen wanneer ik het op de commandline compileer en uitvoer.

Wat (kort) speurwerk op het Internet hielp mij aan de term 'possible loss of precision', maar dat zou alleen voor moeten komen bij rekenkundige functies waar de uitkomst afgerond MOET worden in verband met floating point beperkingen (bijv. 1 / 3 = 0.33333333333etc). In dit/mijn geval zijn het 2 harde constanten die niet hard zijn 'afgebakend' en rekenkundig zouden het aantal cijfers achter de komma maximaal de optelling van de afzonderlijke aantal cijfers achter de komma mogen zijn (1.[2 cijfers achter de komma] * 4.[3 cijfers achter de komma] = 4.[5 cijfers achter de komma]). Trailing zero's even buiten beschouwing gelaten.

Is hier iets tegen te doen? (behalve een andere taal op te pakken dan ;) )

leica - zeiss - fuji - apple | PSN = Sh4m1n0


Acties:
  • 0 Henk 'm!

  • Haan
  • Registratie: Februari 2004
  • Laatst online: 21:05

Haan

dotnetter

Zoek eens op wat een 'double' precies is ;)

Kater? Eerst water, de rest komt later


Acties:
  • 0 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 17:18

Janoz

Moderator Devschuur®

!litemod

BlaTieBla schreef op woensdag 18 februari 2009 @ 10:34:
Wat (kort) speurwerk op het Internet hielp mij aan de term 'possible loss of precision', maar dat zou alleen voor moeten komen bij rekenkundige functies waar de uitkomst afgerond MOET worden in verband met floating point beperkingen (bijv. 1 / 3 = 0.33333333333etc).
1/3 is een voorbeeld voor het decimale stelsel. In een 9 tallig stelsel is de waarde keurig 'afgebakend' weer te geven met 0.3.
In dit/mijn geval zijn het 2 harde constanten die niet hard zijn 'afgebakend' en rekenkundig zouden het aantal cijfers achter de komma maximaal de optelling van de afzonderlijke aantal cijfers achter de komma mogen zijn (1.\[2 cijfers achter de komma] * 4.\[3 cijfers achter de komma] = 4.\[5 cijfers achter de komma]). Trailing zero's even buiten beschouwing gelaten.
De waarden die je gebruikt zijn in het decimale stelsel inderdaad keurig afgebakend. Floats en Doubles worden echter niet opgeslagen in het decimale, maar in het binaire stelsel.
Is hier iets tegen te doen? (behalve een andere taal op te pakken dan ;) )
Het is geen taal probleem, maar een beperking van floating points. Een andere taal pakken zal het probleem dus niet oplossen.

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: 22-09 16:37

.oisyn

Moderator Devschuur®

Demotivational Speaker

Het probleem zit 'm ook niet zozeer in de floating point getallen zelf (een gallon is immers ook niet exact 3,7854 liter, dus je kunt imho best leven met minimale afrondverschillen), maar de uiteindelijke textuele weergave ervan in het decimale stelsel. Probeer een andere numberformatter, eentje die maar n decimalen achter de komma afdrukt en de rest achterwege laat.

[ Voor 23% gewijzigd door .oisyn op 18-02-2009 10:53 ]

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!

  • roy-t
  • Registratie: Oktober 2004
  • Laatst online: 19-09 10:19
Als je echt geen precisie wilt verliezen kun je het beste een java.math.BigDecimal gebruiken. (of een gewone decimal).

Verder vraag ik me af waarom je java 1.5 gebruikt en niet java 1.6 wat echt al een hele tijd uit is en een paar belangrijke updates bevat.

~ Mijn prog blog!


Acties:
  • 0 Henk 'm!

  • BlaTieBla
  • Registratie: November 2000
  • Laatst online: 22-09 11:02

BlaTieBla

Vloeken En Raak Schieten

Topicstarter
Even mijn JAVA kennis achterwege gelaten blijf ik het een raar verschijnsel vinden aangezien ik het gedrag bij bijv. delen kan begrijpen, maar bij vermedigvuldigen niet.
roy-t schreef op woensdag 18 februari 2009 @ 11:20:
Als je echt geen precisie wilt verliezen kun je het beste een java.math.BigDecimal gebruiken. (of een gewone decimal).
[...]
Ik zal daar ook eens naar kijken.
roy-t schreef op woensdag 18 februari 2009 @ 11:20:
[...]
Verder vraag ik me af waarom je java 1.5 gebruikt en niet java 1.6 wat echt al een hele tijd uit is en een paar belangrijke updates bevat.
Bij OSX ben je grotendeels overgeleverd aan de nukken van Apple qua JAVA updates. Ik heb in het verleden wel eens zitten proberen om het up-to-dater te houden, maar dat kostte me te veel moeite.
Enig idee hoe dat werkt op OSX? Binnen Windows is dat net ff wat beter geregeld (vind ik).

leica - zeiss - fuji - apple | PSN = Sh4m1n0


Acties:
  • 0 Henk 'm!

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
BlaTieBla schreef op woensdag 18 februari 2009 @ 14:34:
Even mijn JAVA kennis achterwege gelaten blijf ik het een raar verschijnsel vinden aangezien ik het gedrag bij bijv. delen kan begrijpen, maar bij vermedigvuldigen niet.
Het heeft niet zoveel met Java te maken, maar meer met de manier waarop Floating points opgeslagen worden. Alle talen die floating point getallen op deze manier representeren hebben dit probleem ( Dus ook c,c++,c#, vb.net, etc )

Wikipedia: Floating point

[ Voor 10% gewijzigd door Woy op 18-02-2009 14:42 ]

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Acties:
  • 0 Henk 'm!

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

Macros

I'm watching...

Ik denk dat je met 4 cijfers achter de komma genoeg precisie hebt. Gebruik alleen BigDecimals als je met geld rekent of 100% precisie nodig hebt.
Dus gewoon afronden:
Verander deze regel:
Java:
1
System.out.println(c * gallons + " gallons is " + liters + " liters."); 

In dit:
Java:
1
System.out.format("%.4f gallons is %.4f liters\n",c * gallons, liters); 

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


Acties:
  • 0 Henk 'm!

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Macros schreef op woensdag 18 februari 2009 @ 14:41:
Ik denk dat je met 4 cijfers achter de komma genoeg precisie hebt. Gebruik alleen BigDecimals als je met geld rekent of 100% precisie nodig hebt.
Dus gewoon afronden:
Verander deze regel:
Java:
1
System.out.println(c * gallons + " gallons is " + liters + " liters."); 

In dit:
Java:
1
System.out.format("%.4f gallons is %.4f liters\n",c * gallons, liters); 
In principe heb je met Decimal hetzelfde probleem als met floating point getallen. Zo kan je met een decimal 1/3 niet representeren.

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Acties:
  • 0 Henk 'm!

  • Marcj
  • Registratie: November 2000
  • Laatst online: 16:21
BlaTieBla schreef op woensdag 18 februari 2009 @ 14:34:
Even mijn JAVA kennis achterwege gelaten blijf ik het een raar verschijnsel vinden aangezien ik het gedrag bij bijv. delen kan begrijpen, maar bij vermedigvuldigen niet.
Die 3.7854 is niet precies weer te geven in het binaire stelsel. Daar verlies je dus al precisie.

Maar (zoals oisyn ook al zei) een gallon is ook niet exact 3.7854 liter, dus zie ik niet echt het probleem in deze berekening. Het weergeven van die getallen zou je beter kunnen doen als:

Java:
1
System.out.printf("%6.3f gallon is %6.3f liter%n", c * gallons, liters);


Nu laat je alleen de laatste 3 cijfers achter de komma zien, wat wel het beoogde resultaat oplevert.

Je kunt ook met BigDecimal gaan zitten rekenen, maar dat is alleen maar schijn-nauwkeurigheid in dit geval.

Acties:
  • 0 Henk 'm!

  • BlaTieBla
  • Registratie: November 2000
  • Laatst online: 22-09 11:02

BlaTieBla

Vloeken En Raak Schieten

Topicstarter
Marcj schreef op woensdag 18 februari 2009 @ 14:47:
[...]


Die 3.7854 is niet precies weer te geven in het binaire stelsel. Daar verlies je dus al precisie.

Maar (zoals oisyn ook al zei) een gallon is ook niet exact 3.7854 liter, dus zie ik niet echt het probleem in deze berekening. Het weergeven van die getallen zou je beter kunnen doen als:

Java:
1
System.out.printf("%6.3f gallon is %6.3f liter%n", c * gallons, liters);


Nu laat je alleen de laatste 3 cijfers achter de komma zien, wat wel het beoogde resultaat oplevert.

Je kunt ook met BigDecimal gaan zitten rekenen, maar dat is alleen maar schijn-nauwkeurigheid in dit geval.
aha duidelijk. Weer wat geleerd.
Ben er in middels ook achter hoe de JAVA versie te wijzingen binnen OSX (Java Preferences ... duh :) )

leica - zeiss - fuji - apple | PSN = Sh4m1n0


Acties:
  • 0 Henk 'm!

  • Zsub
  • Registratie: Juli 2006
  • Laatst online: 21:51
Hmm... Eclipse geeft hier op OS X 10.5.6 gewoon aan dat java 1.6 de system default JRE is...? Dus up-to-date houden lijkt me niet expliciet nodig, dat doe ik ook niet.

Acties:
  • 0 Henk 'm!

  • Spockz
  • Registratie: Augustus 2003
  • Laatst online: 21-09 10:08

Spockz

Live and Let Live

De JRE op OS X loopt wel altijd aardig mee. Het is juist de JDK die vaak heel erg achterloopt.

C'est le ton qui fait la musique. | Blog | @linkedin
R8 | 18-55 IS | 50mm 1.8 2 | 70-200 2.8 APO EX HSM | 85 1.8


Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 23:04
.oisyn schreef op woensdag 18 februari 2009 @ 10:52:
Het probleem zit 'm ook niet zozeer in de floating point getallen zelf (een gallon is immers ook niet exact 3,7854 liter, dus je kunt imho best leven met minimale afrondverschillen)
Een gallon is wél exact gedefinieerd, via de exacte definitie van de inch (maar dan als 3,785411784 liter). Als je exact wil rekenen, kun je die waarde dus wel gebruiken, maar moet je een decimale representatie (b.v. fixed point) gebruiken.
Marcj schreef op woensdag 18 februari 2009 @ 14:47:
Je kunt ook met BigDecimal gaan zitten rekenen, maar dat is alleen maar schijn-nauwkeurigheid in dit geval.
Nee, dat geeft in dit geval échte nauwkeurigheid, omdat de conversiefactor eindig te representeren is in het decimale stelsel, en daarvoor is BigDecimal dan ook bedoelt.

De vraag is of het voor deze toepassing uitmaakt dat je wat precisie verliest. Waarschijnlijk niet, dus dan kun je beter op een vast aantal cijfers achter de komma afronden, zoals al was gesuggereerd.

Acties:
  • 0 Henk 'm!

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

.oisyn

Moderator Devschuur®

Demotivational Speaker

Soultaker schreef op woensdag 18 februari 2009 @ 16:48:
[...]

Een gallon is wél exact gedefinieerd
Natuurlijk, zo'n beetje alle veelgebruikte eenheden zijn exact gedefinieerd :). Ik zei alleen dat het niet exact 3,7854 liter was.

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.

Pagina: 1