Название: Траблы в PHP
Отправлено: naxellar от Июль 02, 2007, 11:06:18
echo floor(493/29).' '.$Member->MemberActiveMoney.' '.floor($Member->MemberActiveMoney/29); Выдается ответ: 17 493 16 Почему???
Название: Re: Траблы в PHP
Отправлено: LazarusLong от Июль 02, 2007, 07:29:58
<?php class mem { var $MemberActiveMoney; }
$Member = new mem;
$Member->MemberActiveMoney = 492.9999999999;
echo floor(493/29).' '.$Member->MemberActiveMoney.' '.floor($Member->MemberActiveMoney/29); ?> Результат 17 493 16
Полагаю, это связано наверное с потерей точности при делении: Точность числа с плавающей точкой
Довольно часто простые десятичные дроби вроде 0.1 или 0.7 не могут быть преобразованы в свои внутренние двоичные аналоги без небольшой потери точности. Это может привести к неожиданным результатам: например, floor((0.1+0.7)*10) скорее всего возвратит 7 вместо ожидаемой 8 как результат внутреннего представления числа, являющегося в действительности чем-то вроде 7.9999999999....
Это связано с невозможностью точно выразить некоторые дроби в десятичной системе счисления конечным числом цифр. Например, 1/3 в десятичной форме принимает вид 0.3333333. . ..
Так что никогда не доверяйте точности последних цифр в результатах с числами с плавающей точкой и никогда не проверяйте их на равенство. Если вам действительно необходима высокая точность, вам следует использовать математические функции произвольной точности или gmp-функции.
Название: Re: Траблы в PHP
Отправлено: LazarusLong от Июль 02, 2007, 07:38:06
В моем коде - десять раз обновляю страницу и результат всегда одинаковый. Или ты имеешь ввиду, что при обработке твоего кода все становится нормально?
Блин... тут же нет телепатов. Надо было это сразу писать.
И вообще объявление класса и способ инициализации члена класса - в студию!
Название: Re: Траблы в PHP
Отправлено: naxellar от Июль 02, 2007, 07:43:43
<?php class GameMember { var $MemberID, $MemberLogin, $MemberSurname, $MemberName, $MemberRegistrationDateTime, $MemberSex, $MemberDepartment, $MemberCourse, $MemberProfession, $MemberLastLocation, $MemberHealth, $MemberActiveMoney, $MemberEnergy, $MemberEuroMoney, $MemberAuthorized; function __construct() { global $MySQL, $ENW; session_start(); if (isset($_POST['AuthorizationLogin']) && isset($_POST['AuthorizationPassword'])) { $mysql_result=$MySQL->SendQuery('SELECT `MemberID`,`MemberLogin`,`MemberSurname`,`MemberName`,`MemberSex`,UNIX_TIMESTAMP(`MemberRegistrationDateTime`) AS `MemberRegistrationDateTime`,`MemberDepartment`,`MemberCourse`,`MemberProfession`,`MemberLastLocation`,`MemberHealth`,`MemberEnergy`,`MemberActiveMoney`,`MemberEuroMoney` FROM `members` WHERE `MemberLogin`=? AND `MemberPassword`=?',$_POST['AuthorizationLogin'],md5($_POST['AuthorizationPassword'])); if (strlen(mysql_error())>0) $ENW->WriteErrorE('Error SQL - query',mysql_error()); $LogIn=true; }elseif (isset($_SESSION['UID'])) { $mysql_result=$MySQL->SendQuery('SELECT `MemberID`,`MemberLogin`,`MemberSurname`,`MemberName`,`MemberSex`,UNIX_TIMESTAMP(`MemberRegistrationDateTime`) AS `MemberRegistrationDateTime`,`MemberDepartment`,`MemberCourse`,`MemberProfession`,`MemberLastLocation`,`MemberHealth`,`MemberEnergy`,`MemberActiveMoney`,`MemberEuroMoney` FROM `members` WHERE `MemberID`=?',$_SESSION['UID']); if (strlen(mysql_error())>0) $ENW->WriteErrorE('Error SQL - query',mysql_error()); $LogIn=false; }else { $this->MemberAuthorized=false; return; } if (mysql_num_rows($mysql_result)==1) { $Data=mysql_fetch_array($mysql_result); $_SESSION['UID']=$Data['MemberID']; $this->MemberID=$Data['MemberID']; $this->MemberLogin=$Data['MemberLogin']; $this->MemberSurname=$Data['MemberSurname']; $this->MemberName=$Data['MemberName']; $this->MemberRegistrationDateTime=$Data['MemberRegistrationDateTime']; $this->MemberSex=$Data['MemberSex']; $mysql_result=$MySQL->SendQuery('SELECT `DepartmentName` FROM `departments` WHERE `DepartmentID`=?',$Data['MemberDepartment']); if (strlen(mysql_error())>0) $ENW->WriteErrorE('Error SQL - query',mysql_error()); if (mysql_num_rows($mysql_result)==1) $this->MemberDepartment=mysql_result($mysql_result,0,'DepartmentName'); else $this->MemberDepartment='No'; $this->MemberCourse=$Data['MemberCourse']; $mysql_result=$MySQL->SendQuery('SELECT `ProfessionName` FROM `professions` WHERE `ProfessionID`=?',$Data['MemberProfession']); if (strlen(mysql_error())>0) $ENW->WriteErrorE('Error SQL - query',mysql_error()); if (mysql_num_rows($mysql_result)==1) $this->MemberProfession=mysql_result($mysql_result,0,'ProfessionName'); else $this->MemberProfession='No'; $mysql_result=$MySQL->SendQuery('SELECT `LocationAddress` FROM `locations` WHERE `LocationID`=?',$Data['MemberLastLocation']); if (strlen(mysql_error())>0) $ENW->WriteErrorE('Error SQL - query',mysql_error()); if (mysql_num_rows($mysql_result)==1) $this->MemberLastLocation=mysql_result($mysql_result,0,'LocationAddress'); else $this->MemberLastLocation='/'; $this->MemberHealth=$Data['MemberHealth']; $this->MemberActiveMoney=$Data['MemberActiveMoney']; $this->MemberEnergy=$Data['MemberEnergy']; $this->MemberEuroMoney=$Data['MemberEuroMoney']; $this->MemberAuthorized=true; if ($LogIn) Log::RegisterAuthorization($this->MemberID); }else $this->MemberAuthorized=false; } function SetProperty($Name,$Value) { global $MySQL, $ENW; $mysql_result=$MySQL->SendQuery('UPDATE `members` SET `'.$Name.'`=? WHERE `MemberID`=?',$Value,$this->MemberID); if (strlen(mysql_error())>0) $ENW->WriteErrorE('Error SQL - query',mysql_error()); } function GoLocation($Location) { header('location: '.$Location); exit; } } ?> $Member=new GameMember(); if ($Member->MemberAuthorized) { if ($Member->MemberLastLocation!='/') $Member->GoLocation($Member->MemberLastLocation); if ($GameGringotts->ChangeEuroToMM()==0) $ChangeEuroToMM=true; else $ChangeEuroToMM=false; $Smarty->assign('ChangeEuroToMM',$ChangeEuroToMM); }else { $Member->GoLocation('/'); } $Smarty->register_modifier('CMU',array($GameGringotts,'CalculationMoneyUnits')); Cash: {$Member->MemberActiveMoney|CMU:'%Gg %Ss %Kk'} //Smarty <?php class GameGringotts { function CalculationMoneyUnits($Knuts,$Format) { $Sickles=floor($Knuts/29); $Knuts=$Knuts-($Sickles*29); $Galleons=floor($Sickles/17); $Sickles=$Sickles-($Galleons*17); $Result=str_replace('%G',$Galleons,$Format); $Result=str_replace('%S',$Sickles,$Result); $Result=str_replace('%K',$Knuts,$Result); return $Result; } // MM - MagicMoneys function CalculationExchangeEuroToMM() { global $MySQL, $ENW; $mysql_result=$MySQL->SendQuery('SELECT COUNT(`GringottsBuyingID`) AS `CountMagicBuyings` FROM `gringotts-buyings` WHERE `GringottsBuyingFront`="m"'); if (strlen(mysql_error())>0) $ENW->WriteErrorE('Error SQL - query',mysql_error()); $CountMagicBuyings=mysql_result($mysql_result,0,'CountMagicBuyings'); $mysql_result=$MySQL->SendQuery('SELECT COUNT(`GringottsBuyingID`) AS `CountPeopleBuyings` FROM `gringotts-buyings` WHERE `GringottsBuyingFront`="p"'); if (strlen(mysql_error())>0) $ENW->WriteErrorE('Error SQL - query',mysql_error()); $CountPeopleBuyings=mysql_result($mysql_result,0,'CountPeopleBuyings'); if ($CountPeopleBuyings==0) $CountPeopleBuyings=1; if ($CountMagicBuyings==0) $CountMagicBuyings=1; $KnutEuro=($CountMagicBuyings/$CountPeopleBuyings)*0.01; $mysql_result=$MySQL->SendQuery('INSERT INTO `gringotts-exchanges` SET `GringottsExchangeDate`=NOW(), `GringottsExchange`=?',$KnutEuro); return $KnutEuro; } function ChangeEuroToMM() { global $MySQL, $ENW, $Member, $GameGringotts; if ($Member->MemberAuthorized && isset($_POST['GringottsExchangeOfficeEuro']) && is_numeric($_POST['GringottsExchangeOfficeEuro'])) { if ($_POST['GringottsExchangeOfficeEuro']>=0.01) { $Knuts=$_POST['GringottsExchangeOfficeEuro']/($GameGringotts->CalculationExchangeEuroToMM()); $mysql_result=$MySQL->SendQuery('UPDATE `members` SET `MemberActiveMoney`=`MemberActiveMoney`+?, `MemberEuroMoney`=`MemberEuroMoney`-? WHERE `MemberID`=?',$Knuts,$_POST['GringottsExchangeOfficeEuro']+($_POST['GringottsExchangeOfficeEuro']*0.01),$Member->MemberID); if (mysql_errno()==1264) { return -1; }elseif (mysql_errno()!=0) $ENW->WriteErrorE('Error SQL - query',mysql_error()); else { $Member->MemberActiveMoney+=$Knuts; $Member->MemberEuroMoney-=$_POST['GringottsExchangeOfficeEuro']+($_POST['GringottsExchangeOfficeEuro']*0.01); return 0; } }else return -1; }else return -1; } } ?>
Название: Re: Траблы в PHP
Отправлено: LazarusLong от Июль 02, 2007, 08:18:03
Какие данные реально попадают в поле класса? в этой строке: $this->MemberActiveMoney=$Data['MemberActiveMoney']; Я ведь не знаю, что у тебя там в базе. А телепатов тут нету, как я уже говорил ;) ЗЫ: А ты сам ответ знаешь?
Название: Re: Траблы в PHP
Отправлено: naxellar от Июль 02, 2007, 08:23:54
Какие данные реально попадают в поле класса? в этой строке: $this->MemberActiveMoney=$Data['MemberActiveMoney']; Я ведь не знаю, что у тебя там в базе. А телепатов тут нету, как я уже говорил ;) ЗЫ: А ты сам ответ знаешь? Точно не знаю. Факт тот, что в нужный момент вызывается $Member->MemberActiveMoney+=$Knuts; где он и становится равным 493
Название: Re: Траблы в PHP
Отправлено: LazarusLong от Июль 02, 2007, 08:26:31
Тогда тема не в этот раздел. Нужно перенести в Программирование. см. Правила раздела "Викторины". п. 1.3. Автор должен знать правильный ответ, иначе тема по смыслу из викторины превращается в простой раздел форума.
А вообще я тебе же сказал: 99%, что это потеря точности при делении.
Название: Re: Траблы в PHP
Отправлено: naxellar от Июль 02, 2007, 08:34:31
п. 1.3. Автор должен знать правильный ответ, иначе тема по смыслу из викторины превращается в простой раздел форума.
Ладна, перенесите его туда (веб-программирование) админы плиз с именем Траблы в PHP. А вообще я тебе же сказал: 99%, что это потеря точности при делении.
Она была бы все время одна и та же. А так при обновлении она всегда потом правильно пашет. Вобщем, я торжественно клянусь, что расскажу, в чем пряталсо сей хитроумный баг. LazarusLong, спасибо, что отозвался. +1 тебе. ;)
Название: Re: Траблы в PHP
Отправлено: LazarusLong от Июль 02, 2007, 08:40:20
И еще это может быть связано с представлением в ПХП вещественных чисел, которое, как ивзестно, платформозависимо. Кстати, говоря - попробуй все-таки вот это: Если вам действительно необходима высокая точность, вам следует использовать математические функции произвольной точности или gmp-функции.
Название: Re: Траблы в PHP
Отправлено: LazarusLong от Июль 02, 2007, 11:09:55
Кстати говоря, если тебе нужно округлять, то c помощью floor это делать не корректно - floor(4.549) == 4.54, хотя по правилам математики должно быть 4.55. floor округляет вниз, а ceil - вверх. Юзай round.
Название: Re: Траблы в PHP
Отправлено: LazarusLong от Июль 03, 2007, 10:01:35
Суть не в том. Важно то, что floor округляет вниз, а не по математичким правилам округления.
Название: Re: Траблы в PHP
Отправлено: naxellar от Июль 03, 2007, 10:05:48
Суть не в том. Важно то, что floor округляет вниз, а не по математичким правилам округления.
Эта функция не округления, а отрезания дробной части!!
Название: Re: Траблы в PHP
Отправлено: LazarusLong от Июль 03, 2007, 10:20:05
Нет floor это округление вниз.
floor(-4.1) = -5. Потому как -5 меньше чем -4. Для отрицательных в таком случае нужно использовать ceil
Название: Re: Траблы в PHP
Отправлено: naxellar от Июль 03, 2007, 11:09:15
Функция найдена: gmp_div();
Название: Re: Траблы в PHP
Отправлено: naxellar от Июль 03, 2007, 12:19:46
Несите меня в дурдом: var_dump($Knuts); settype($Knuts,'string'); var_dump($Knuts); settype($Knuts,'integer'); var_dump($Knuts); Так оно нормально выдает: float(493) string(3) "493" int(493)
|