Kirjoittaja Aihe: PHP ja liukuluvut  (Luettu 5582 kertaa)

samja

  • Käyttäjä
  • Viestejä: 182
    • Profiili
PHP ja liukuluvut
« : 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ä)
« Viimeksi muokattu: 16.11.07 - klo:18.30 kirjoittanut samja »
Yleisfoorumi:  http://ajatusmylly.net

Heikki Mäntysaari

  • Käyttäjä / tiedottaja
  • Viestejä: 377
    • Profiili
Vs: PHP ja liukuluvut
« Vastaus #1 : 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.
Suomenkielinen Linux-wiki: Linux.fi - katso myös http://linux.fi/foorumi

samja

  • Käyttäjä
  • Viestejä: 182
    • Profiili
Vs: PHP ja liukuluvut
« Vastaus #2 : 15.11.07 - klo:18.50 »

Ok
Yleisfoorumi:  http://ajatusmylly.net

raimo

  • Käyttäjä
  • Viestejä: 4269
  • openSUSE Tumbleweed
    • Profiili
Vs: PHP ja liukuluvut
« Vastaus #3 : 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.
Tietä käyden tien on vanki. Vapaa on vain umpihanki.
Aaro Hellaakoski

Ville Pöntinen

  • Käyttäjä
  • Viestejä: 2078
    • Profiili
Vs: PHP ja liukuluvut
« Vastaus #4 : 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.

raimo

  • Käyttäjä
  • Viestejä: 4269
  • openSUSE Tumbleweed
    • Profiili
Vs: PHP ja liukuluvut
« Vastaus #5 : 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.
Tietä käyden tien on vanki. Vapaa on vain umpihanki.
Aaro Hellaakoski

Heikki Mäntysaari

  • Käyttäjä / tiedottaja
  • Viestejä: 377
    • Profiili
Vs: PHP ja liukuluvut
« Vastaus #6 : 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.
Suomenkielinen Linux-wiki: Linux.fi - katso myös http://linux.fi/foorumi

raimo

  • Käyttäjä
  • Viestejä: 4269
  • openSUSE Tumbleweed
    • Profiili
Vs: PHP ja liukuluvut
« Vastaus #7 : 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?
Tietä käyden tien on vanki. Vapaa on vain umpihanki.
Aaro Hellaakoski

peran

  • Vieras
Vs: PHP ja liukuluvut
« Vastaus #8 : 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.

SuperOscar

  • Käyttäjä
  • Viestejä: 4063
  • Ocatarinetabellatsumtsum!
    • Profiili
    • Legisign.org
Vs: PHP ja liukuluvut
« Vastaus #9 : 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.
pöytäkone 1, NUC: openSUSE Leap 15.6, kannettavat 1–3: Debian GNU/Linux 12; pöytäkone 2: openSUSE Tumbleweed; RPi 1: FreeBSD 14-RELEASE; RPi 2: LibreELEC 11

Heikki Mäntysaari

  • Käyttäjä / tiedottaja
  • Viestejä: 377
    • Profiili
Vs: PHP ja liukuluvut
« Vastaus #10 : 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.
Suomenkielinen Linux-wiki: Linux.fi - katso myös http://linux.fi/foorumi

raimo

  • Käyttäjä
  • Viestejä: 4269
  • openSUSE Tumbleweed
    • Profiili
Vs: PHP ja liukuluvut
« Vastaus #11 : 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ä. ;)
Tietä käyden tien on vanki. Vapaa on vain umpihanki.
Aaro Hellaakoski

SuperOscar

  • Käyttäjä
  • Viestejä: 4063
  • Ocatarinetabellatsumtsum!
    • Profiili
    • Legisign.org
Vs: PHP ja liukuluvut
« Vastaus #12 : 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.)
pöytäkone 1, NUC: openSUSE Leap 15.6, kannettavat 1–3: Debian GNU/Linux 12; pöytäkone 2: openSUSE Tumbleweed; RPi 1: FreeBSD 14-RELEASE; RPi 2: LibreELEC 11

peran

  • Vieras
Vs: PHP ja liukuluvut
« Vastaus #13 : 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.  ;)

Aleksi Hankalahti

  • Käyttäjä
  • Viestejä: 82
    • Profiili
Vs: PHP ja liukuluvut
« Vastaus #14 : 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 (myös linkit sivun alalaidassa ja englanninkielisen wikipedian vastaava artikkeli).

samja

  • Käyttäjä
  • Viestejä: 182
    • Profiili
Vs: PHP ja liukuluvut
« Vastaus #15 : 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.
« Viimeksi muokattu: 17.11.07 - klo:16.26 kirjoittanut samja »
Yleisfoorumi:  http://ajatusmylly.net