Huomasin vähän aikaa sitten, että (int)$luku muunnosta käytettäessä syntyy välillä virhe:
http://koti.mbnet.fi/winuus/jakotesti.php?luku=24
Alunperin luulin että koodissani oli virhe, kun lukujen summaaminen yhteen ei tuottanutkaan alkuperäistä lukua, mutta nähtävästi kyseessä olikin PHP:n sisäinen bugi(!?)
Ongelman saa korjattua (int)(string)$luku purkalla, mutta onko kellään parempaa ehdotusta?
Edit: Lukujen pyöristys olisi eräs keino, mutta tällöin tulos vääristyy paikoittain.
Yllä mainitun PHP-scriptin lähdekoodi:
<?php function scan_luku($luku, $pituus = 100){ $ulos = array(); for($u = 1; $u <= $pituus; $u++){ $jako = $u; $k = $luku/$jako; $yht = $k; for($s = 0; $s < $jako; $s++){ if($s == $jako-1){ if((int)$yht != $luku){ $ulos[] = $u; } } $yht += $k; } } return $ulos; } $luku = $_GET['luku']; $mm_luku = 500; $mm_skaala = 500; $lista = scan_luku($mm_luku, $mm_skaala); $max = count($lista); if($luku){ print"(float) on alkuperäinen luku, jolle ei ole tehty mitään muunnoksia. (int) on intiksi muunnettu luku.<br><br>"; $jako = $luku; $k = $mm_luku/$jako; $yht = $k; for($s = 0; $s < $jako; $s++){ if((int)$yht != (int)(string)$yht){ print"<b>(float)$yht = (int)".(int)$yht."</b> (pitäis tulla $yht, eikä ".(int)$yht."!!)"; }else{ print"(float)$yht = (int)".(int)$yht; } print"<br>"; $yht += $k; } print"<br><br><hr><br><br>"; } print"Luku $mm_luku jaettuna 1-$mm_skaala luvuilla, löytyy yhteensä $max virheellistä int-muunnoksen tulosta:<br><BR>"; foreach($lista as $key => $val){ print"<A HREF=\"?luku=$val\">$val</A>, "; } ?>
Noissa muunnoksissa muistaakseni oli jotain bugeja. Oletko kokeillut intval()-funktiota? Toki voisit tehdä oman muunnosfunktionkin...
No siis varsinaista virhettähään ei php:n (int) ja (float):ssa ole, vaan suurin virhe näyttäisi olevan koodin rakenteessa.
Koska tietokoneet käsittelevät lukuja N desimaalin tarkkuudella, olisi suositeltavaa tehdä ensin kertolaskut jonka jälkeen vasta jakolaskut.
Muutenkin olet saanut yksinkertaisen (X*Y)/Z (jossa 1<=Y<=Z)laskun näyttämään erittäin epäselvältä, ja käyttämällä useita muuttujia (joka selittäisi pyöristysvirheet).
<?php $luku = (intval($_GET['luku'])+1)-1; $mm_luku = 500; $mm_skaala = 500; if ($luku>0){ for ($i=1;$i<=$luku;$i++){ $lukulasku = ($mm_luku*$i)/$luku; echo "(float)$lukulasku ... (int)".(int)$lukulasku.""; if ( (int)(string)$lukulasku != (int)$lukulasku){ echo " ---> Virhe(float)$lukulasku != (int)".(int)$lukulasku.""; } echo "<br>"; } } ?>
Lebe80 kirjoitti:
Muutenkin olet saanut yksinkertaisen (X*Y)/Z (jossa 1<=Y<=Z)laskun näyttämään erittäin epäselvältä, ja käyttämällä useita muuttujia (joka selittäisi pyöristysvirheet).
Virhe ei edelleenkään ole laskennallinen, sillä tulostettaessa muuttujaa se näyttää ilman int-muunnosta tasan 500, mutta muunnoksen jälkeen se onkin 499 jostain kumman syystä:
print $luku; // 500 print (int)$luku; // 499 print floor($luku); // 499 print intval($luku); // 499
Edit: Vastaus alla olevaan: PHP:n version on 4.3.10
Mikä php-versio, sillä itselläni ei tätä ongelmaa ole?
EDIT:
otetaas takasin, eli itselläni tulee näistä samat tulokset seuraavalla arvolla.
$luku = 499.9999999999999;
print $luku; // 500
print (int)$luku; // 499
print floor($luku); // 499
print intval($luku); // 499
kun taas
$luku = 499.99999999999999; // yksi 9 lisää
print $luku; // 500
print (int)$luku; // 500
print floor($luku); // 500
print intval($luku); // 500
Eli PHP ei tulosta lukua sellaisenaan kuin se on muistissa, vaan pyöristää sen ylöspäin. int-muunnos taas käyttää pyöristämätöntä lukua, jolloin syntyy virhe...
Onko mitään ehdotusta ongelman korjaamiseen?
Pienen luvun lisääminen lukuun ei ole toimiva, koska luvun tarkkuus ei ole aina sama (eli jos tekisi tyyliin: (int)($luku+0.000000001)).
No ainakin tietty laskujärjestys ei pyöristä lukuja niin paljoa väärin.
Samoin "turhien" välivaiheiden vähentäminen minimoi virhettä.
===
edit:
sooda: et ole yhtään mukana.
Yleisesti tunnettu koodi kokonaisluvuksi pyöristämiseenhä on (int)(luku + 0.5). (positiivisille. negatiivisilla + -> -)
(int)((int)$luku) :?
tsuriga: eipä auta kokonaisluvun kokonaisluvuksi muuttaminen....
Itse näkisin ainoan ratkaisun suunnittelemalla laskutoimitukset toisella tavalla, koska Php ei näemmä pysty sisältämään 14 desimaalia.
Hups siis ceil((int)$luku). Toisaalta jos luku näytetään nyt merkkijonona "500" ja sitä käsitelläänkin 500.0000000001:nä niin tulee taas ongelmia, lieneekö moista ongelmaa?
Häh? ceil() pyöristää 50.999 luvun ylöspäin, ja int pudottaa desimaalit, eli tuosta ceil((int)$luku) tuottaisi 50, koska se yrittää pyöristää lukua 50 ylöspäin, mutta eihän siinä ole desimaaleja enää olemassakaan, joten se ei siitä miksikään muutu.
Aihe on jo aika vanha, joten et voi enää vastata siihen.