Модераторы: skyboy, MoLeX, Aliance, ksnk

Поиск:

Закрытая темаСоздание новой темы Создание опроса
> Проблема с переводом из double в integer 
V
    Опции темы
Icaros
  Дата 7.1.2007, 21:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 30
Регистрация: 26.5.2006

Репутация: нет
Всего: нет



Добрый день, уважаемые. 
Столкнулся с серьезнейшей проблемой. Уже приличное время назад столкнулся с такой штукой: есть массив с данными, состоящий из 100 элементов, в цикле обращаюсь к элементам для их вывода. Вот код:
Код

// $answ = array(0, 1, 2, 3, 4, ...)
$i = 0;
$dx = 0.01;
$L = 1;
for ($i=0; $i<L; $i+=$dx)
{
   echo $answ[$i*100];
}

И вот это долгое время у меня имеется странный эффект:
Код

Вывод программы:
0, 1, 2, ..., 9, 10, 10, 11, 12, 13, 15, 16...
 
Поначалу был, мягко скажем, в шоке...
После экспериментов с пошаговым проходом по коду (спасибо Zend`у) обнаружил интересную особенность. При переводе числа из double в integer ПХП исходит из своих соображений, не совпадающих с моими. Т.е. число 0.11*100, переведенное в integer, станет 10. Вы спросите: а зачем так странно обращаться к массиву (т.е. 0,0х * 100)? Может и странно, но я был уверен, что нет никакой разницы и так было удобнее. Вот эта особенность морочила мне голову такое время...  smile
В одном из журналов  PHPinside я читал (вот сейчас вдруг вспомнил, а тогда не придал значения), что в ПХП особый способ представления double (или integer) чисел. Там было сказано, что выражение 0 == 0.0 выдаст false (или как-то так) и надо пользоваться неким math (может ошибаюсь, но я не в курсе что это).
Если быть короче, то как корректно перевести из double в integer ? Или, если это все ахинея, скажите, чтобы я учил учебник ПХП за первый класс церковно-приходской школы. 
PM MAIL   Вверх
vasac
Дата 7.1.2007, 21:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1060
Регистрация: 4.5.2006

Репутация: 8
Всего: 36



Лично я не могу сказать с полным на то основанием, что это ахинея, по той причине, что слабо понял о чем здесь написано.
Опишите, пожалуйста, саму суть проблемы без кода этого ужасного цикла.

Цитата(Icaros @  7.1.2007,  21:43 Найти цитируемый пост)
в ПХП особый способ представления double (или integer) чисел

Возможно Вы имеете ввиду особенности представления чисел с плавающей запятой, то да, double вообще сравнивать по большому счету нельзя, даже с double. Но это проблема (проблема ли) отнюдь не только php.

Это сообщение отредактировал(а) vasac - 7.1.2007, 21:58
PM WWW   Вверх
murod
Дата 7.1.2007, 22:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 113
Регистрация: 17.9.2005
Где: Uzbekistan/Tashke nt

Репутация: 2
Всего: 4



да есть такой глюк,
тебе надо так:
Код

echo $answ["".$i*100];

может и не глюк, но у меня нет обяснений.  smile  
--------------------
Люди всего мира берегите природу!  
PM MAIL ICQ   Вверх
Icaros
Дата 7.1.2007, 22:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 30
Регистрация: 26.5.2006

Репутация: нет
Всего: нет



Вот что получалось у меня:

Код:
Код

$c = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
$txt = "";
$j = 0;

for($i=0; $i<0.17; $i+=0.01)
{
     $txt .= (string)($j++)." : "; 
     $txt .= (string)$c[$i*100];
     $txt .= "<br />";
}

echo $txt;

Результат:
Код

0 : 0
1 : 1
2 : 2
3 : 3
4 : 4
5 : 5
6 : 6
7 : 7
8 : 8
9 : 9
10 : 10
11 : 10
12 : 11
13 : 12
14 : 13
15 : 15
16 : 16


Уважаемый murod, Вы совершенно правы. Спасибо. Чудеса. С Вашим хаком все встало на круги своя. Что же это за "особенность", попортившая мне нервы ?..
PM MAIL   Вверх
Mal Hack
Дата 7.1.2007, 22:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Мудрый...
****


Профиль
Группа: Участник Клуба
Сообщений: 9926
Регистрация: 15.2.2004

Репутация: 122
Всего: 261



Icaros, что за бред вы тут несете?
Я не понимаю, что религия не позволяет писать по человечески, как большинство нормальных людей?
Код
// $answ = array(0, 1, 2, 3, 4, ...)
$i = 0;
$L = 100;
for ($i=0; $i<L; $i++)
{
   echo $answ[$i];
}


PM ICQ   Вверх
murod
Дата 7.1.2007, 23:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 113
Регистрация: 17.9.2005
Где: Uzbekistan/Tashke nt

Репутация: 2
Всего: 4



Mal Hack,  мне кажется Icaros просто забыл поставить знак $. ничего страшного. ты лучше скажи свое мнение что это такое? глюк или это мы- не из "большинства нормальных людей"? smile

Добавлено @ 23:15 
с типом String все работает правильно, а с Integer глюки
--------------------
Люди всего мира берегите природу!  
PM MAIL ICQ   Вверх
Mal Hack
Дата 7.1.2007, 23:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Мудрый...
****


Профиль
Группа: Участник Клуба
Сообщений: 9926
Регистрация: 15.2.2004

Репутация: 122
Всего: 261



murod, я уже привел свою точку зрения касательно кода в первом посте.
PM ICQ   Вверх
murod
Дата 7.1.2007, 23:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 113
Регистрация: 17.9.2005
Где: Uzbekistan/Tashke nt

Репутация: 2
Всего: 4



Mal Hack, кажется ты немножка не понял проблему 
Icaros'а 
Код

<?php
$i = 0;
$dx = 0.01;
$L = 0.12;
$arr=array(0,1,2,3,4,5,6,7,8,9,10,11,12);
for ($i=0; $i<$L; $i+=$dx)
{
 $g=(100*$i);
 echo($arr[$g]."<br>");
}
?>

выводит:
0
1
2
3
4
5
6
7
8
9
10
10
11

а этот код :
Код

<?php
$i = 0;
$dx = 0.01;
$L = 0.12;
$arr=array(0,1,2,3,4,5,6,7,8,9,10,11,12);
for ($i=0; $i<$L; $i+=$dx)
{
 $g=(String)(100*$i);
 echo($arr[$g]."<br>");
}
?>

выводит:
0
1
2
3
4
5
6
7
8
9
10
11
12
именно с массивами работает не правильно!!
вот без массива:
Код

<?php
$i = 0;
$dx = 0.01;
$L = 0.12;
for ($i=0; $i<$L; $i+=$dx)
{
 $g=(100*$i);
 echo($g."<br>");
}
?>

выводит правильно:
0
1
2
3
4
5
6
7
8
9
10
11
12

Добавлено @ 23:42 
Народ как это понять?
--------------------
Люди всего мира берегите природу!  
PM MAIL ICQ   Вверх
vasac
Дата 7.1.2007, 23:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1060
Регистрация: 4.5.2006

Репутация: 8
Всего: 36



murod
техническая проблема, как я уже указал, в представлении чисел с плавающей запятой. Насчет ее понимания — в поиск.
глобальная же проблема в том, что вы творите здесь какую-то фигню и пытаетесь понять, почему же в итоге фигня и получается.
PM WWW   Вверх
Mal Hack
Дата 7.1.2007, 23:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Мудрый...
****


Профиль
Группа: Участник Клуба
Сообщений: 9926
Регистрация: 15.2.2004

Репутация: 122
Всего: 261



murod, я прекрасно понял проблему, жаль модераторский статус е позволяет сказать где у автора ошибка...

Цитата(murod @  7.1.2007,  23:41 Найти цитируемый пост)
а этот код :

ИДИОТИЗМ !!!
PM ICQ   Вверх
skyboy
Дата 8.1.2007, 00:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


неОпытный
****


Профиль
Группа: Модератор
Сообщений: 9820
Регистрация: 18.5.2006
Где: Днепропетровск

Репутация: 75
Всего: 260



Mal Hack, но ведь такая проблема(а она, по-видимому, в наличии) вполне может выплыть в другом, внешне "вполне приличном" коде. Не в курсе, от чего такая ерунда при работе с хэшами?
PM MAIL   Вверх
Mal Hack
Дата 8.1.2007, 01:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Мудрый...
****


Профиль
Группа: Участник Клуба
Сообщений: 9926
Регистрация: 15.2.2004

Репутация: 122
Всего: 261



Какие хэши?
Код

<?php

$c = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
$txt = "";
$j = 0;
for($i=0.0; $i<0.17; $i+=0.01)
{
    print $g = ((float)100.0*(float)$i);  
    $index = number_format($g,2, ',', ' ');
    
    print "=======".$c{(int)$g}."  ======== " .$c{(int)$index}."   ====  ".($i*100)." ==== ".$i." <br/>";
}
?>


Судя по всему это баг в двоичном представлении числа...
К сожалению, как хранятся числа во внутреннем представлении - вопрос, ответ на которой знают очень мало людей.
PM ICQ   Вверх
ewolf
Дата 8.1.2007, 01:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 389
Регистрация: 15.8.2006
Где: г. Москва

Репутация: 8
Всего: 18



Причина такой ошибки, как мне кажется, состоит в следующем:

Из-за того, что числа в формате IEEE представляются в двоичной форме в формате [экспонента][мантисса]. В результате, некоторые десятичные дроби не могут быть точно представлены в этой записи. Об этом сказано тут например http://www.php.net/manual/ru/language.types.float.php 
В результате при постепенном добавлении к переменной значения 0.01 к 11 шагу накапливается ошибка, и вместо числа 11 мы храним число 10.9999999999999999. 

Ключ может быть только либо строкой, либо целым числом, поэтому у числа 10.(9) отбрасывается дробная часть. И мы получаем значение 10.

При преобразовании в строку алгоритм окруления другой и преобразует дробное число в целое, основываясь на количестве разрядов после запятой. 

Сравните, например

Код

echo 10.9999999999; // = 10.9999999999
echo 10.99999999999; // = 11


В этом и причина "ошибки"

Это сообщение отредактировал(а) ewolf - 8.1.2007, 01:19
PM MAIL ICQ   Вверх
Mal Hack
Дата 8.1.2007, 01:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Мудрый...
****


Профиль
Группа: Участник Клуба
Сообщений: 9926
Регистрация: 15.2.2004

Репутация: 122
Всего: 261



Хм... Знал я конечно, о такой фишке, но не думал, что в PHP на столько это криво реализовано..
Код

<?php

$c = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
$txt = "";
$j = 0;
for($i=0.0; $i<0.17; $i+=0.01)
{
    print $g = ((float)100.0*(float)$i);  
    $index = number_format($g,2, ',', ' ');
    
    print "=======".sprintf("%10.250e",$g)."=======".$c{(int)$g}."  ======== " .$c{(int)$index}."   ====  ".($i*100)." ==== ".$i." <br/>";
}
?>

на выходе получаем:
Цитата

0    0.000000000000000000000000000000000000000e-1    0    0    0    0 
1    1.000000000000000000000000000000000000000e+0    1    1    1    0.01 
2    2.000000000000000000000000000000000000000e+0    2    2    2    0.02 
3    3.000000000000000000000000000000000000000e+0    3    3    3    0.03 
4    4.000000000000000000000000000000000000000e+0    4    4    4    0.04 
5    5.000000000000000000000000000000000000000e+0    5    5    5    0.05 
6    6.000000000000000888178419700125232338905e+0    6    6    6    0.06 
7    7.000000000000000888178419700125232338905e+0    7    7    7    0.07 
8    8.000000000000000000000000000000000000000e+0    8    8    8    0.08 
9    9.000000000000000000000000000000000000000e+0    9    9    9    0.09 
10    1.000000000000000000000000000000000000000e+1    10    10    10    0.1 
11    1.099999999999999822364316059974953532219e+1    10    11    11    0.11 
12    1.199999999999999822364316059974953532219e+1    11    12    12    0.12 
13    1.299999999999999822364316059974953532219e+1    12    13    13    0.13 
14    1.399999999999999822364316059974953532219e+1    13    14    14    0.14 
15    1.500000000000000000000000000000000000000e+1    15    15    15    0.15 
16    1.600000000000000000000000000000000000000e+1    16    16    16    0.16 

Хм....
Вот написал тестик на С++...
Код
int _tmain(int argc, _TCHAR* argv[])
{
    int c[17] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
    double i,g;
    int index;

    for(i=0.0; i<0.17; i+=0.01)
    {
        g = ((float)100.0*(float)i);
        printf( "%2.2f\t", g);  

        index = (int)(g);

        printf( "%2.20f\t\t%d\t%d\t%2.2f\t%2.2f\n",g,c[(int)g],c[(int)index],(i*100),i);
    }

    return 0;
}


Результаты:
Цитата

0.00    0.00000000000000000000          0       0       0.00    0.00
1.00    0.99999997764825821000          0       0       1.00    0.01
2.00    1.99999995529651640000          1       1       2.00    0.02
3.00    2.99999993294477460000          2       2       3.00    0.03
4.00    3.99999991059303280000          3       3       4.00    0.04
5.00    5.00000007450580600000          5       5       5.00    0.05
6.00    5.99999986588954930000          5       5       6.00    0.06
7.00    7.00000002980232240000          7       7       7.00    0.07
8.00    7.99999982118606570000          7       7       8.00    0.08
9.00    9.00000035762786870000          9       9       9.00    0.09
10.00   10.00000014901161200000         10      10      10.00   0.10
11.00   10.99999994039535500000         10      10      11.00   0.11
12.00   11.99999973177909900000         11      11      12.00   0.12
13.00   12.99999952316284200000         12      12      13.00   0.13
14.00   14.00000005960464500000         14      14      14.00   0.14
15.00   15.00000059604644800000         15      15      15.00   0.15
16.00   15.99999964237213100000         15      15      16.00   0.16


PM ICQ   Вверх
skyboy
Дата 8.1.2007, 02:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


неОпытный
****


Профиль
Группа: Модератор
Сообщений: 9820
Регистрация: 18.5.2006
Где: Днепропетровск

Репутация: 75
Всего: 260



да уж. почему бы не округлять-то? спасибо за приведенные тесты, Mal Hackewolf.
Кстати, Mal Hack, всё же не только в кривости кода дело, верно?  smile 
PM MAIL   Вверх
Закрытая темаСоздание новой темы Создание опроса
Правила форума "PHP"
Aliance
IZ@TOP
skyboy
SamDark
MoLeX

Новичкам:

  • PHP редакторы собираются и обсуждаются здесь
  • Электронные книги по PHP, документацию можно найти здесь
  • Интерпретатор PHP, полную документацию можно скачать на PHP.NET

Важно:

  • Не брезгуйте пользоваться тегами [code=php]КОД[/code] для повышения читабельности текста/кода.
  • Перед созданием новой темы воспользуйтесь поиском и загляните в FAQ
  • Действия модераторов можно обсудить здесь

Внимание:

  • Темы "ищу скрипт", "подскажите скрипт" и т.п. будут переноситься в форум "Web-технологии"
  • Темы с именами: "Срочно", "помогите", "не знаю как делать" будут УДАЛЯТЬСЯ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, IZ@TOP, skyboy, SamDark, MoLeX, awers.

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | PHP: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0825 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.