Ubuntu Suomen keskustelualueet

Ubuntun käyttö => Ohjelmointi, palvelimet ja muu edistyneempi käyttö => Aiheen aloitti: samja - 15.11.07 - klo:18.20

Otsikko: PHP ja liukuluvut
Kirjoitti: samja - 15.11.07 - klo:18.20

Koodia: [Valitse]
$x = -0.2;
$x += 0.1; print $x . '<br>';
$x += 0.1; print $x . '<br>';
$x += 0.1; print $x . '<br>';
=>
Lainaus
-0.1
0
0.1


Koodia: [Valitse]
$x = -0.3;
$x += 0.1; print $x . '<br>';
$x += 0.1; print $x . '<br>';
$x += 0.1; print $x . '<br>';
$x += 0.1; print $x . '<br>';
=>
Lainaus
-0.2
-0.1
2.77555756156E-17
0.1

Bugi PHP:ssä?


(Muutettu arveluttavan kohdan väriä)
Otsikko: Vs: PHP ja liukuluvut
Kirjoitti: Heikki Mäntysaari - 15.11.07 - klo:18.36
Koodia: [Valitse]
$x = -0.3;
$x += 0.1; print $x . '<br>';
$x += 0.1; print $x . '<br>';
$x += 0.1; print $x . '<br>';
$x += 0.1; print $x . '<br>';
=>
Koodia: [Valitse]
-0.2
-0.1
2.77555756156E-17
0.1
Bugi PHP:ssä?
Ei, aivan normaalia toimintaa. Kuten huomaat, tuo nollan "sijasta" tulostuva luku on hyvin pieni (2,77*10^-17), eli käytännössä nolla. Pieni ero johtuu tietokoneen tavasta käsitellä liukulukuja, ja voit törmätä vastaaviin tapauksiin myös muita ohjelmointikieliä käyttäessäsi.
Otsikko: Vs: PHP ja liukuluvut
Kirjoitti: samja - 15.11.07 - klo:18.50

Ok
Otsikko: Vs: PHP ja liukuluvut
Kirjoitti: raimo - 15.11.07 - klo:19.10
Ei, aivan normaalia toimintaa. Kuten huomaat, tuo nollan "sijasta" tulostuva luku on hyvin pieni (2,77*10^-17), eli käytännössä nolla. Pieni ero johtuu tietokoneen tavasta käsitellä liukulukuja, ja voit törmätä vastaaviin tapauksiin myös muita ohjelmointikieliä käyttäessäsi.
Kyllä tuo minusta on bugi, koska nolla pitää aina olla täsmälleen 0.
Kokeilin samaa asiaa Perlillä, ja se kyllä antaa pelkän nollan ihan OK.
Otsikko: Vs: PHP ja liukuluvut
Kirjoitti: Ville Pöntinen - 15.11.07 - klo:19.58

Noin se kone kuitenkin toimii... Perl kai vain käyttää enemmän bittejä kokeilemasi luvun esittämiseen. (Tai pyöristää sun puolestas?)

Jos käyttää kokonaislukumuuttujia, on nolla tietty nolla.
Otsikko: Vs: PHP ja liukuluvut
Kirjoitti: raimo - 15.11.07 - klo:20.35
Jos käyttää kokonaislukumuuttujia, on nolla tietty nolla.
Niin tietty on, mutta ihan täsmälleen tuon jäsen samjan koodin mukaan tein saman Perlillä, ja tulos on tasan 0.
Perli siis osaa laskea oikeammin kuin PHP.
Otsikko: Vs: PHP ja liukuluvut
Kirjoitti: Heikki Mäntysaari - 15.11.07 - klo:21.12
Testaus C++:lla:
Koodia: [Valitse]
#include <iostream>

int main() {
        float t=0.1;
        t-=0.1;
        t-=0.1;
        t-=0.1;
        t+=0.1;
        t+=0.1;
        std::cout << t << "\n";

        return 0;
}
ja tulostaa
Koodia: [Valitse]
$ ./testi
-1.49012e-09

Eli aivan normaalia.  Tässä tapauksessa käyttämällä tarkempaa liukulukumuuttujatyyppiä (double) tulosteena on tasan 0, eli sen tarkkuus riittää tässä tapauksessa.
Otsikko: Vs: PHP ja liukuluvut
Kirjoitti: raimo - 15.11.07 - klo:21.37
PHP:stä en ymmärrä onneksi yhtään mitään, enkä etenkään C++ höpinästä. Perlistä jotakin vähäsen...
Mutta mitenkäs ohjelmoijapolo tarkistaa tuollaisen melkein nollan olemassaolon, toivotonta lähes lienee?
Otsikko: Vs: PHP ja liukuluvut
Kirjoitti: peran - 15.11.07 - klo:21.43
PHP:stä en ymmärrä onneksi yhtään mitään, enkä etenkään C++ höpinästä. Perlistä jotakin vähäsen...
Mutta mitenkäs ohjelmoijapolo tarkistaa tuollaisen melkein nollan olemassaolon, toivotonta lähes lienee?

Yleensäkin liukuluvuissa == merkki on varsin huono tarkastelu tapa.

Vertailu
Koodia: [Valitse]
if (haluttutarkkuus+haettuluku>=vertailtavaluku && haluttutarkkuus-haettuluku<=vertailtavaluku) then
tehdään haluttu
end if

tulostamisessa pyöristetään ennen tulostamista haluttuun tarkkuuteen.
Otsikko: Vs: PHP ja liukuluvut
Kirjoitti: SuperOscar - 15.11.07 - klo:21.43
Esimerkki-istunto pythonilla:

Koodia: [Valitse]
Python 2.5.1 (r251:54863, Oct  5 2007, 13:36:32)
[GCC 4.1.3 20070929 (prerelease) (Ubuntu 4.1.2-16ubuntu2)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> n = 0.1
>>> n += 0.1
>>> n
0.20000000000000001
>>> n += 0.1
>>> n
0.30000000000000004
>>> n += 0.1
>>> n
0.40000000000000002
>>>

Sama vika siis Rahikaisella.

Mutta mitenkäs ohjelmoijapolo tarkistaa tuollaisen melkein nollan olemassaolon, toivotonta lähes lienee?

Tietysti niin, ettei yritäkään testata täsmällistä yhtäsuuruutta.
Otsikko: Vs: PHP ja liukuluvut
Kirjoitti: Heikki Mäntysaari - 15.11.07 - klo:21.44
Mutta mitenkäs ohjelmoijapolo tarkistaa tuollaisen melkein nollan olemassaolon, toivotonta lähes lienee?
Jos esimerkiksi luvun itseisarvo on pienempi kuin 10^-6, niin luku luultavasti on tasan nolla. Toki tilanteen mukaan soveltaen.
Otsikko: Vs: PHP ja liukuluvut
Kirjoitti: raimo - 15.11.07 - klo:21.55
Jos esimerkiksi luvun itseisarvo on pienempi kuin 10^-6, niin luku luultavasti on tasan nolla. Toki tilanteen mukaan soveltaen.
Kummallista minusta, ja onneksi myös Perlin mielestä. ;)
Otsikko: Vs: PHP ja liukuluvut
Kirjoitti: SuperOscar - 15.11.07 - klo:22.30
Perlistä en tiedä, mutta jopa matemaatikkojen lempikieli fortran laskee ”väärin”:

Koodia: [Valitse]
c     Koe
      program koe
      real l
      l=0.1
      write (*,*) l
      l=l+0.1
      write (*,*) l
      l=l+0.1
      write (*,*) l
      l=l+0.1
      write (*,*) l
      l=l+0.1
      write (*,*) l
      l=l+0.1
      write (*,*) l
      stop
      end

Tulokseksi tulee:

Koodia: [Valitse]
  0.100000001
  0.200000003
  0.300000012
  0.400000006
  0.5
  0.600000024

(Huom. Jo lähtöarvossa on pieni heitto.)
Otsikko: Vs: PHP ja liukuluvut
Kirjoitti: peran - 15.11.07 - klo:22.51
Tulokseksi tulee:

Koodia: [Valitse]
  0.100000001
  0.200000003
  0.300000012
  0.400000006
  0.5
  0.600000024

(Huom. Jo lähtöarvossa on pieni heitto.)

Tästä myös huomaa aavistuksen, miten liukuluvut tuotetaan ko. kielessä. Käytetään siis 2-kantaista desimaalia, kun 0.5:n näyttää oikein.  ;)
Otsikko: Vs: PHP ja liukuluvut
Kirjoitti: Aleksi Hankalahti - 16.11.07 - klo:19.37
Kaiken kaikkiaan liukuluvut ovat sangen monimutkainen aihe. Käytännössä niissä yritetään tallettaa äärelliseen määrään bittejä äärettömän tarkka reaaliluku. Esimerkiksi matemaattisesti reaalilukujen 0.0 ja 1.0 välissä on äärettömän monta lukua. Selvästi esimerkiksi 64 bittinen liukuluku ei voi esittää kaikkia noista äärettömistä luvuista: mahdollisuuksia kun on vain 264. Käytännössä liukuluvut toimivat siten, että reaaliakselilta pätkäistään jokin sopivan pituinen pätkä. Tältä pätkältä valitaan sopivat kohdat, joita sitten binäärinen liukuluku esittää.

Erityisesti liukulukuja ei koskaan saa verrata yhtäsuuruusoperaatiolla. Sen sijaan kaksi liukulukua a ja b katsotaan samoiksi, jos niiden etäisyys toisistaan on tarpeeksi pieni, eli |a-b| < e. Tässä e valitaan aina käyttötarkoituksen mukaan sopivan pieneksi vakioksi (esimerkiksi e = 10-6).

Lisää tietoa liukuluvuista netissä: http://fi.wikipedia.org/wiki/Liukuluku (http://fi.wikipedia.org/wiki/Liukuluku) (myös linkit sivun alalaidassa ja englanninkielisen wikipedian vastaava artikkeli).
Otsikko: Vs: PHP ja liukuluvut
Kirjoitti: samja - 17.11.07 - klo:16.23
Minulla oli vähän "ongelmia" kun hain polynomin y-arvot x:n vaihdellessa välillä x1..x2. Kun taulukon tulosti, näkyi x-sarake vähän tympeän leveänä eriskummallisen nolla-arvon takia. Olisi pitänyt pyöristellä round()-funktiolla.

$x = $x1; $xs[] = array(); $ys[] = array();
while ($x <= $x2) {
   $x += $inc;
   $xs[] = $x;
   $ys[] = $polynomi->value($x);
}
  => 
  => 
  => 
  => 
  => 
  => 
$xs[] = array(); $ys[] = array();
for ($x = $x1, $n = 0;  $x <= $x2;  ++$n) {
   $x = $x1 + $n * $inc;
   $xs[] = $x;
   $ys[]  = $polynomi->value($x);
}

Jälkimmäinen tapa on kai periaatteessa tarkempi, koska liukulukupyöristyksiä ei tule niin paljon. Jälkimmäinen luuppi ei tosin ole yhtä tehokas kuin eka.