Russian Qt Forum
Ноябрь 24, 2024, 03:55 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: 1 [2] 3   Вниз
  Печать  
Автор Тема: Как правильно сделать преобразование для целых и вещественных чисел?  (Прочитано 22482 раз)
Eten
Гость
« Ответ #15 : Февраль 28, 2011, 05:53 »

значит в книжке ошибка... такое часто бывает... да и какая разница. в 2 раза точнее, не в 2 раза хуже  Улыбающийся
А есть ли различие на Windows, MAC, Linux и мобильных системах в размерности с плавающей точкой? Если есть, может кто привести их значения для MAC и Linux (У меня было описано результаты на Windows)?
Записан
Eten
Гость
« Ответ #16 : Февраль 28, 2011, 06:15 »

это не дело Си/С++, это дело общего стандарта(IEEE754). Вот  перевод на русский. Там вообще много всего интересного. то о чём мы говорили(nan и inf) расписываются в п. 6.
про то как проще всего получить их на Си:
Код
C
#include <cstdio>
 
int main(int , char *[])
{
float f=1;
f=0;
f=1/f;
printf("INF: %e\n",f);
f=0;
f=0/f;
printf("NAN: %e\n",f);
f=0;
f=-1/f;
printf("-INF: %e\n",f);
 
return 0;
}
 

[added]
почитал там примеры ошибок... как же страшно жить, работая с плавующей точкой...

Как я понял из ваших примеров Nan это ситуация деления на ноль, ясное дело что такое не допустимо (как например с логарифмами и квадратным корнем, но там проверяется без нас и выводит соответствующие значение). Другое дело INF. Тем более у меня на Qt под Windows, в программе хоть и пишет +/-INF, но видимо каким то образом помнит значения, т.к. проверка на границы у меня срабатывает на ура, поэтому не было нужды брать тип размерностью выше (а уж если нужно, то как вообще тогда проверять самый максимальный вещественный тип чисел). К тому же подобное использование выхода за пределы с inf не сработает с целочисленными типами, но зато все это можно провернуть присвоив значение изначально переменной с типом float (видел такое у многих), но с проверкой границ для целочисленного типа, потом можно спокойно преобразовать эту переменную в этот целочисленный тип. Вот пример кода ниже, который кстати тоже работает.
Код:
            long arg0, arg1=std::numeric_limits<long>::max(), arg2=std::numeric_limits<long>::max();
            float res = float(arg1) + float(arg2);
            if (res >= std::numeric_limits<long>::max())
                arg0 = std::numeric_limits<long>::max();
            else
                if (res <= std::numeric_limits<long>::min())
                    arg0 = std::numeric_limits<long>::min();
                else
                    arg0 = long(res);
Тут даже, если переменная res станет inf, проверки границы все равно проверятся и присваиваются соответствующие значения. Проверял, что предыдущий пример кода, что код выше в этом посте, все работало, как надо. Igors, как я понял Nan это сродни ошибка (деление на ноль, например), если да, то достаточно допустить запись nan, либо выдавать сообщение об ошибке и делать останов программы в случае необходимости (хотя лучше сделать этой тихой ошибкой, чем рушить программу, но зависит от ТЗ программы). Igors, я не упустил никаких подводных камней? И что мне даст использование fpclassify?
Записан
Eten
Гость
« Ответ #17 : Февраль 28, 2011, 07:16 »

До меня кажется дошло о чем говорил Igors, когда затронул тему про Nan (не числа). Вот здесь в п.7.2, как раз изображен Полный диапазон чисел одинарной точности (32 бит) по стандарту IEEE754 с достаточным пояснением, чтобы можно было понять в чем собака зарыта. Но для лучшего понимания стоит ознакомиться с п. 5 и 6.  Подмигивающий

З.Ы.
Пойду, пока подумаю еще, если ответите на мои вопросы, заранее спасибо.  Улыбающийся
Записан
Eten
Гость
« Ответ #18 : Февраль 28, 2011, 08:51 »

Не знаю как у других, но задавая шестнадцатеричным форматом я не получал тех же чисел. Но, несмотря на это, с помощью numeric_limits<float> смог найти все нужные мне значения по стандарту IEEE754, необходимые для проверки границ (кстати в месте указанном выше, есть те самые реальные границы, которые и используются, а не как указано в справочниках). NAN я в расчет не беру, т.к. NAN - это Не числа NAN(No a Numbers). К ним относятся символы, или результаты недопустимых операций, поэтому тут нужно делать проверку на ошибки, а не на проверку границ, т.е. выходит за рамки проверок границ. Но это уже другая тема или ее часть. В абзаце ниже я поясню на пальцах, почему можно использовать один float для проверки границ целочисленных типов до long int (32 бита, за 64 бита говорить не берусь) и сам тип float (также равносильно и для double и long double, т.е. в плане проверки самих себя).

Для начала стоит сказать о структуре по битового построения двоичного формата с одинарной точностью, но это уже сделано до меня и можно узнать об этом здесь в п.7.2. И хорошенько по размыслив и поэкспериментировав с числами можно придти к следующим выводам. Во-первых, в отличии от целочисленных типов, вещественные не используют по максимуму свои байты, а хранят значения в определенном формате, позволяющем определить все ситуации для работы с числами с плавающей запятой. Во-вторых, понятие NAN (не числа) относится к символам или недопустимым операциям (например, деление на ноль), здесь стоит делать обработку на ошибки, т.к. к проверкам на граничные значения NAN никакого прямого отношения не имеет, разве что косвенное (но уже математика на уровне Mathcad-а, могу ошибаться, но опираясь на практику можно полагать, как было сказано). В-третьих, если значение превышает граничные значения положительного максимального нормализованного, то ему присваивается положительная бесконечность (для отрицательных, отрицательная бесконечность и проверка, как для отрицательных чисел). В-четвертых, если значение меньше положительного денормализованного минимума, то ему присваивают +0 (могу ошибаться, но вроде так, по крайней мере +inf не присваивают, а присваивают 0, из-за этого и такие выводы Строит глазки ) (опять же в случае отрицательных присваивают -0). Но, учет выхода за границы минимального денормализованного значения полезно для программ с инженерными расчетами (например для Mathcad-а), для обычных операций достаточно ориентироваться на нормализованное минимальное значение.

В итоге получаем, что тип float, как и любой вещественный тип реализованный по стандарту IEEE75, не выходит (пробовал я ему записывать число +/-2e208, получил значение +/-inf) за границы своих диапазонов (учитываем формат записи +/-inf и +/-0), но для нормального приведения чисел к границам нам надо писать свои проверки границ, чего нам хватает numeric_limits<float> (кстати, я так и не смог найти денормализованного максимального значения, но для проверки границ это не так сущевственно). В случае значения NAN, разговор отдельный, т.к. этот момент требует отлавливания проверки ошибки (или ошибка возникнет сама с остановом программы) на символы и недопустимые операции исходя из самого определения NAN. Поэтому учитывать его при проверки границ или не учитывать это ваш выбор в зависимости от ваших потребностей.  Иначе говоря, мы можем использовать вещественный тип для границ, как для самого типа, так и для целого входящего своей размерностью в этот вещественный тип (например, long int входит в float). Но проверки на значение на NAN стоит делать до проверки границ.

А теперь, собственно рабочий код для проверки границ (на NAN тут не проверяется). Для использования numeric_limits нужно подключать "limits", т.е. #include <limits>.

Код для проверки границ типа long, через float:
Код:
            long arg0, arg1=std::numeric_limits<long>::max(), arg2=std::numeric_limits<long>::max();
            float res = float(arg1) + float(arg2);
            if (res >= std::numeric_limits<long>::max())
                arg0 = std::numeric_limits<long>::max();
            else
                if (res <= std::numeric_limits<long>::min())
                    arg0 = std::numeric_limits<long>::min();
                else
                    arg0 = long(res);

Пример функции для проверки границ (с нормализованными граничными значениями):
Код:
    inline float floatborders(float Value)
    {
        if (Value < std::numeric_limits<float>::max()*(-1))
            return std::numeric_limits<float>::max()*(-1);

        if (Value > std::numeric_limits<float>::max())
            return std::numeric_limits<float>::max();

        if (Value > std::numeric_limits<float>::min()*(-1) && Value > -1 && Value < 0)
            return std::numeric_limits<float>::min()*(-1);

        if (Value < std::numeric_limits<float>::min() && Value < 1 && Value > 0)
            return std::numeric_limits<float>::min();

        return Value;
    }

Примечание: Функции привел как есть, поэтому если кому насущна оптимизация, то вам ее нужно сделать самим, я же предпочел выложить в удобочитаемом виде.

З.Ы.
Не знаю, что может еще выясниться с этим числами с плавающей запятой, но полагаю, что на свой вопрос этим постом я дал полный и развернуты ответ.  Если в чем-то ошибаюсь, то просьба поправить с объяснениями. Подмигивающий
Записан
Fat-Zer
Гость
« Ответ #19 : Февраль 28, 2011, 12:48 »

Цитировать
Как я понял из ваших примеров Nan это ситуация деления на ноль, ясное дело что такое не допустимо.
поправлю: тфт деление нуля на ноль. и прочий бред типа ноль в нулевой степени, а в общем случае деление на 0 даёт +-inf. Всё это на x86 на других архитектурах FPU может вести себя по-другому. И даже на x86 сопроцессор может возбудить исключение при делении на 0.(правда у меня такого добиться не получалось, мысль пришла в голову при рассматрении пятого бита CR0)
Цитировать
Код для проверки границ типа long, через float:
не стал бы я так смело мешать целочисленную и FPU арифметику... наверняка можно подобрать значения на грани с ошибкой округления. в ассеблере(для x86) для этих целей есть инструкции jo. А вот как простым способом в языках высокого уровня проверять уже не помню...
Цитировать
Не знаю как у других, но задавая шестнадцатеричным форматом я не получал тех же чисел.
то, что x86 little-endian не забыли учесть?
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #20 : Февраль 28, 2011, 14:19 »

А можно наглядный в виде кода привести? До применения подобного еще не доходил.  Подмигивающий
Код
C++ (Qt)
double bad = sqrt(-1.0); // провоцируем nan
if (!(bad >= 0) && !(bad < 0))     // кажется бессмысленным, но
printf("wrong float %g\n", bad);  // поймали nan
 
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #21 : Февраль 28, 2011, 14:57 »

Если есть такая потребность в задаче делать подобные проверки (согласитесь не страндартный случай), то может просто написать свой класс мега астрономических чисел, который знал бы такие вещи как деление на ноль и всё такое? И вёл бы себя как обычное число, был бы совместим с double.
Почему нет?
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #22 : Февраль 28, 2011, 15:26 »

Eten, отвечая на Ваши вопросы

- случай nan всегда надо отрабатывать отдельно, специальной проверкой. Так как все арифметические операции с nan дают в итоге тоже nan, он размножается очень быстро.
Примечание: не связывайтесь с ф-цией isNan, там есть заморочки, поэтому программисты используют приемчик что приведен выше.

- как Вы уже разобрались, характеристика float может оказаться недостаточной (INF/DENOM), число > e+38 или < e-38 (но не ноль). Это можно узнать с помощью fpclassify, нет никакого смысла проверяться по std::limits

- как (еще раз) указал m_ax, надо поработать с постановкой. Вы конечно можете считать INF "нормальным", но следующая операция с ним легко может породить nan - зачем же пускать козла в огород? Понятно что специфика Вашей задачи за рамками этого обсуждения, но в реальных расчетах пределы устанавливаются волевым решением и они намного меньше максимальных. Напр 1.0e+20 еще далеко до предела, но в квадрат это число уже не возвести. Часто надо учесть что от мантиссы могут остаться "рожки да ножки", т.е. знаки после запятой теряются если замешаны большие значения.

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

Записан
Fat-Zer
Гость
« Ответ #23 : Февраль 28, 2011, 16:53 »

про отлов nan, ещё более интересное условие:
Код
C++ (Qt)
if (!(bad==bad))  
printf("wrong float %g\n", bad);  
 
Записан
Eten
Гость
« Ответ #24 : Февраль 28, 2011, 17:55 »

...

- как (еще раз) указал m_ax, надо поработать с постановкой. Вы конечно можете считать INF "нормальным", но следующая операция с ним легко может породить nan - зачем же пускать козла в огород? Понятно что специфика Вашей задачи за рамками этого обсуждения, но в реальных расчетах пределы устанавливаются волевым решением и они намного меньше максимальных. Напр 1.0e+20 еще далеко до предела, но в квадрат это число уже не возвести. Часто надо учесть что от мантиссы могут остаться "рожки да ножки", т.е. знаки после запятой теряются если замешаны большие значения.

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

Полагаю стоит внести сюда немного практичности и конкретики, т.к. я хочу довести свой класс NNumeric (см. ниже) до разумных границ. Его задача, это хранение целочисленного или с плавающей запятой значения, а также сопровождающей ее информации, нужно для работы с классом в местах его хранения, при выполнении математики и просто обращения с ним, как с обычным типом, но в разумных пределах. Т.е. у него есть два автоматических конструктора для типов float и long (собственно, с которыми он и работает), конструктор по умолчанию и конструктор копирования. Несколько функций позволяющих изменять отдельные значения в объекте. Плюс операторы преобразования в типы float, double, int, long; операторы инкремента и декремента, оператор присвоения объекта того же класса, а также операторы сравнения. Все это мне нужно для создания типа "Числовой", который может работать, либо как целочисленный тип (long), либо как вещественный (float). Я ухватился за float и long потому, что они оба занимают по 4 байта, а для меня это весомый фактор в выборе типов. Экземпляры класса NNumeric используются у меня для хранения временных переменных (в мат. операциях), хранения переменных и константных значений в проигрывателе (можно подразумевать исполнитель входного кода). 4 байта насущны для меня из-за записи/чтения самого значения из произвольного файла. Сам класс NNumeric  почти обкатан, но вот проверка границ для него не маловажна (я тока еще на погрешности учет не ввел для суммы и разности чисел, там при разнице между числами 232 появляются погрешности). Т.к. тип long (учитывая про чтение из файла) не может быть превышен его задании (вручную можно, в моей проге нет места ручному использованию этого класса), то тут проверку на границы я не ставлю, а произвожу ее в функции "математических операций и функций" используя тип float (например, не получится сделать проверку на границы, если оба целочисленных типа будут иметь максимальное положительное значение при выполнении ариф. операции "+" между ними). В математике NNumeric используется для работы с простыми арифметическими операциями (+, -, /, *), квадр. корень, степень, логарифмы (e и 10), модуль числа, мин и макс, целочисленное деление и остаток от него, рандом от 0 и до N/1, exp, sin, cos, tg, ctg, arccos, arcsin, arctg, arcctg, выделение дробной и целой части, а также три вида окргления (round, floor, ceil).

Сам класс NNumeric:
Код:
#include <limits>
#include <cmath>


class NNumeric{
public:
    inline NNumeric() {this->data._isreal = true; this->data._value = 0; this->data._id = -1; this->data._constvalue = false; this->data._name = "";}
    inline NNumeric(float Value) {this->data._isreal = true; this->setValue(floatborders(Value)); this->data._id = -1; this->data._constvalue = false; this->data._name = "";}
    inline NNumeric(long Value) {this->data._isreal = false; this->setValue(Value); this->data._id = -1; this->data._constvalue = false; this->data._name = "";}
    inline NNumeric(const NNumeric& a) {this->setConstValue(a.data._constvalue); this->setID(a.data._id); this->setName(a.data._name); this->setIsReal(a.data._isreal); this->setValue(a.data._value);}

    inline float round() {return std::floor(this->Value() + 0.5);}
    inline float fractional() {return this->Value() < 0 ? this->Value() - std::ceil(this->Value()) : this->Value() - std::floor(this->Value());}
    inline float integer() {return this->Value() < 0 ? std::ceil(this->Value()) : std::floor(this->Value());}

    inline int ID() {return this->data._id;}
    inline void setID(int ID) {this->data._id = ID;}
    inline bool ConstValue() {return this->data._constvalue;}
    inline void setConstValue(bool ConstValue) {this->data._constvalue = ConstValue;}
    inline QString Name() {return this->data._name;}
    inline void setName(QString Name) {this->data._name = Name;}
    inline void setIsReal(bool IsReal) {this->data._isreal = IsReal;}
    //Если ложно, то значение принимается и выдается (при печати числа на экране)
    // с отсечение дробной части числа и соблюдением границ типа long int.
    inline bool IsReal() const {return this->data._isreal;}
    inline void setValue(float Value)
    {
        if (!this->data._isreal)
        {
            if (Value >= std::numeric_limits<long>::max())
                Value = std::numeric_limits<long>::max();
            else
                if (Value <= std::numeric_limits<long>::min())
                    Value = std::numeric_limits<long>::min();
                else
                    Value = long(Value);
        }
        this->data._value = Value;
    }
    inline float Value() const {return this->data._value;}

    inline void operator=(const NNumeric& a) {this->setConstValue(a.data._constvalue); this->setID(a.data._id); this->setName(a.data._name); this->setIsReal(a.data._isreal); this->setValue(a.data._value);}
    inline operator QString() {return QString::number(this->data._value);}
    inline operator float() {return float(this->data._value);}
    inline operator double() {return double(this->data._value);}
    inline operator int() {if (this->Value() < std::numeric_limits<int>::min()) return std::numeric_limits<int>::min(); if (this->Value() > std::numeric_limits<int>::max()) return std::numeric_limits<int>::max(); return int(this->Value());}
    inline operator long() {if (this->Value() < std::numeric_limits<long>::min()) return std::numeric_limits<long>::min(); if (this->Value() > std::numeric_limits<long>::max()) return std::numeric_limits<long>::max(); return long(this->Value());}
    inline void operator++(int) {this->data._value + 1 >= std::numeric_limits<long>::max() ? this->data._value = std::numeric_limits<long>::max() : this->data._value++;}
    inline void operator--(int) {this->data._value - 1 <= std::numeric_limits<long>::min() ? this->data._value = std::numeric_limits<long>::min() : this->data._value--;}
    inline bool operator==(const NNumeric& a) {return this->data._value == a.Value();}
    inline bool operator!=(const NNumeric& a) {return this->data._value != a.Value();}
    inline bool operator&&(const NNumeric& a) {return this->data._value && a.Value();}
    inline bool operator||(const NNumeric& a) {return this->data._value || a.Value();}
    inline bool operator<(const NNumeric& a) {return this->data._value < a.Value();}
    inline bool operator<=(const NNumeric& a) {return this->data._value <= a.Value();}
    inline bool operator>(const NNumeric& a) {return this->data._value > a.Value();}
    inline bool operator>=(const NNumeric& a) {return this->data._value >= a.Value();}

private:

    inline float floatborders(float Value)
    {
        if (Value < std::numeric_limits<float>::max()*(-1))
            return std::numeric_limits<float>::max()*(-1);

        if (Value > std::numeric_limits<float>::max())
            return std::numeric_limits<float>::max();

        if (Value > std::numeric_limits<float>::min()*(-1) && Value > -1 && Value < 0)
            return std::numeric_limits<float>::min()*(-1);

        if (Value < std::numeric_limits<float>::min() && Value < 1 && Value > 0)
            return std::numeric_limits<float>::min();

        return Value;
    }

    struct NData {
    //если идентификатор равен -1, значит это либо временная переменная, либо константное значение
    int _id;
    //константные или временные значения имен не имеют, только переменные.
    QString _name;
    float _value;
    bool _isreal;
    //это указывает на константное значение
    bool _constvalue;
    };
    NData data;
};

Сама функция, где выполняются математические операторы:
Код:
void result::MathematicalOperations(ushort Modifikator)
{
    NNumeric arg0, arg1, arg2;
    switch (Modifikator)
    {
        case 2:
            arg2 = (this->StackValue.pop()).value<NNumeric>();
            arg1 = (this->StackValue.pop()).value<NNumeric>();

            if (!arg1.IsReal() && !arg2.IsReal())
            {
                 float res = float(arg1) + float(arg2);
                 if (res >= std::numeric_limits<long>::max())
                     arg0 = std::numeric_limits<long>::max();
                 else
                     if (res <= std::numeric_limits<long>::min())
                         arg0 = std::numeric_limits<long>::min();
                     else
                         arg0 = long(res);
            }
            else
                 arg0 = float(arg1) + float(arg2);

            this->StackValue.push(QVariant::fromValue(arg0));
        break;
        case 3:
            arg2 = (this->StackValue.pop()).value<NNumeric>();
            arg1 = (this->StackValue.pop()).value<NNumeric>();

            if (!arg1.IsReal() && !arg2.IsReal())
            {
                float res = float(arg1) - float(arg2);
                if (res <= std::numeric_limits<long>::min())
                    arg0 = std::numeric_limits<long>::min();
                else
                    if (res >= std::numeric_limits<long>::max())
                        arg0 = std::numeric_limits<long>::max();
                    else
                        arg0 = long(res);
            }
            else
                 arg0 = float(arg1) - float(arg2);

            this->StackValue.push(QVariant::fromValue(arg0));
        break;
        case 4:
            arg2 = (this->StackValue.pop()).value<NNumeric>();
            arg1 = (this->StackValue.pop()).value<NNumeric>();

            if (!arg1.IsReal() && !arg2.IsReal())
            {
                 float res = float(arg1) * float(arg2);
                 if (res >= std::numeric_limits<long>::max())
                     arg0 = std::numeric_limits<long>::max();
                 else
                     if (res <= std::numeric_limits<long>::min())
                         arg0 = std::numeric_limits<long>::min();
                     else
                         arg0 = long(res);
            }
            else
                 arg0 = float(arg1) * float(arg2);

            this->StackValue.push(QVariant::fromValue(arg0));
        break;
        case 5:
            arg2 = (this->StackValue.pop()).value<NNumeric>();
            arg1 = (this->StackValue.pop()).value<NNumeric>();

            if (!arg1.IsReal() && !arg2.IsReal())
                 arg0 = long(arg1) / long(arg2);
            else
                 arg0 = float(arg1) / float(arg2);

            this->StackValue.push(QVariant::fromValue(arg0));
        break;
        case 6:
            arg0 = (this->StackValue.pop()).value<NNumeric>();

            arg0++;
            arg0.setID(-1);
            arg0.setConstValue(false);
            arg0.setName("");

            this->StackValue.push(QVariant::fromValue(arg0));
        break;
        case 7:
            arg0 = (this->StackValue.pop()).value<NNumeric>();

            arg0--;
            arg0.setID(-1);
            arg0.setConstValue(false);
            arg0.setName("");

            this->StackValue.push(QVariant::fromValue(arg0));
        break;
        case 8:
            arg2 = (this->StackValue.pop()).value<NNumeric>();
            arg1 = (this->StackValue.pop()).value<NNumeric>();

            arg0 = (ldiv(arg1, arg2)).quot;

            this->StackValue.push(QVariant::fromValue(arg0));
        break;
        case 9:
            arg2 = (this->StackValue.pop()).value<NNumeric>();
            arg1 = (this->StackValue.pop()).value<NNumeric>();

            arg0 = (ldiv(arg1, arg2)).rem;

            this->StackValue.push(QVariant::fromValue(arg0));
        break;
        case 10:
            arg2 = (this->StackValue.pop()).value<NNumeric>();
            arg1 = (this->StackValue.pop()).value<NNumeric>();

            if (!arg1.IsReal() && !arg2.IsReal())
            {
                 float res = std::pow(float(arg1),int(arg2));
                 if (res > std::numeric_limits<long>::max())
                     arg0 = std::numeric_limits<long>::max();
                 else
                     if (res <= std::numeric_limits<long>::min())
                         arg0 = std::numeric_limits<long>::min();
                     else
                         arg0 = long(res);
            }
            else
                arg0 = std::pow(float(arg1),float(arg2));

            this->StackValue.push(QVariant::fromValue(arg0));
        break;
        case 11:
            arg0 = (this->StackValue.pop()).value<NNumeric>();

            if (!arg0.IsReal())
                arg0 = long(std::sqrt(float(arg0)));
            else
                arg0 = std::sqrt(float(arg0));

            this->StackValue.push(QVariant::fromValue(arg0));
        break;
        case 12:
            arg0 = (this->StackValue.pop()).value<NNumeric>();

            arg0 = std::fabs(float(arg0));

            this->StackValue.push(QVariant::fromValue(arg0));
        break;
        case 13:
            arg2 = (this->StackValue.pop()).value<NNumeric>();
            arg1 = (this->StackValue.pop()).value<NNumeric>();

            if (!arg1.IsReal() && !arg2.IsReal())
                if (long(arg1) < long(arg2))
                    arg0 = arg1;
                else
                    arg0 = arg2;
            else
                if (float(arg1) < float(arg2))
                    arg0 = arg1;
                else
                    arg0 = arg2;

            arg0.setID(-1);
            arg0.setConstValue(false);
            arg0.setName("");

            this->StackValue.push(QVariant::fromValue(arg0));
        break;
        case 14:
            arg2 = (this->StackValue.pop()).value<NNumeric>();
            arg1 = (this->StackValue.pop()).value<NNumeric>();

            if (!arg1.IsReal() && !arg2.IsReal())
                if (long(arg1) > long(arg2))
                    arg0 = arg1;
                else
                    arg0 = arg2;
            else
                if (float(arg1) > float(arg2))
                    arg0 = arg1;
                else
                    arg0 = arg2;

            arg0.setID(-1);
            arg0.setConstValue(false);
            arg0.setName("");

            this->StackValue.push(QVariant::fromValue(arg0));
        break;
        case 15:
            arg0 = (this->StackValue.pop()).value<NNumeric>();

            srand ( time(NULL) );
            arg0 = long(rand() % long(arg0));

            this->StackValue.push(QVariant::fromValue(arg0));
        break;
        case 16:
            arg0 = (this->StackValue.pop()).value<NNumeric>();

            arg0 = std::log10(float(arg0));

            this->StackValue.push(QVariant::fromValue(arg0));
        break;
        case 17:
            arg0 = (this->StackValue.pop()).value<NNumeric>();

            arg0 = std::log(float(arg0));

            this->StackValue.push(QVariant::fromValue(arg0));
        break;
        case 18:
            arg0 = (this->StackValue.pop()).value<NNumeric>();

            arg0 = std::exp(float(arg0));

            this->StackValue.push(QVariant::fromValue(arg0));
        break;
        case 19:
            arg0 = (this->StackValue.pop()).value<NNumeric>();

            arg0 = std::cos(float(arg0));

            this->StackValue.push(QVariant::fromValue(arg0));
        break;
        case 20:
            arg0 = (this->StackValue.pop()).value<NNumeric>();

            arg0 = std::sin(float(arg0));

            this->StackValue.push(QVariant::fromValue(arg0));
        break;
        case 21:
            arg0 = (this->StackValue.pop()).value<NNumeric>();

            arg0 = std::tan(float(arg0));

            this->StackValue.push(QVariant::fromValue(arg0));
        break;
        case 22:
            arg0 = (this->StackValue.pop()).value<NNumeric>();

            arg0 = 1.0f/std::tan(float(arg0));

            this->StackValue.push(QVariant::fromValue(arg0));
        break;
        case 23:
            arg0 = (this->StackValue.pop()).value<NNumeric>();

            arg0 = std::acos(float(arg0));

            this->StackValue.push(QVariant::fromValue(arg0));
        break;
        case 24:
            arg0 = (this->StackValue.pop()).value<NNumeric>();

            arg0 = std::asin(float(arg0));

            this->StackValue.push(QVariant::fromValue(arg0));
        break;
        case 25:
            arg0 = (this->StackValue.pop()).value<NNumeric>();

            arg0 = std::atan(float(arg0));

            this->StackValue.push(QVariant::fromValue(arg0));
        break;
        case 26:
            arg0 = (this->StackValue.pop()).value<NNumeric>();

            arg0 = 1.0f/std::atan(float(arg0));

            this->StackValue.push(QVariant::fromValue(arg0));
        break;
        case 27:
            arg0 = (this->StackValue.pop()).value<NNumeric>();

            arg0 = std::floor(float(arg0));

            this->StackValue.push(QVariant::fromValue(arg0));
        break;
        case 28:
            arg0 = (this->StackValue.pop()).value<NNumeric>();

            arg0 = std::ceil(float(arg0));

            this->StackValue.push(QVariant::fromValue(arg0));
        break;
        case 29:
            arg0 = (this->StackValue.pop()).value<NNumeric>();

            arg0 = arg0.round();

            this->StackValue.push(QVariant::fromValue(arg0));
        break;
        case 30:
            arg0 = (this->StackValue.pop()).value<NNumeric>();

            arg0 = arg0.fractional();

            this->StackValue.push(QVariant::fromValue(arg0));
        break;
        case 31:
            arg0 = (this->StackValue.pop()).value<NNumeric>();

            arg0 = arg0.integer();

            this->StackValue.push(QVariant::fromValue(arg0));
        break;
        case 32:

            srand ( time(NULL) );
            arg0 = float(rand() % 1001)/1000.0f;

            this->StackValue.push(QVariant::fromValue(arg0));
        break;
    }
}

И вот как мне быть с границами, учитывая ваши ответы и то, что вы можете почерпнуть из моего кода?

Про inf понятно, забыл несколько моментов. Просто предлагаю вам сделать свои предположения или суждения из полученного материала, может я что-то упустил из внимания или вы могли не учесть. А над проверками границы для float, надо еще подумать, это да.   Подмигивающий

З.Ы.
Надеюсь я не переборщил с постом. Но это все, что использует числа и в той или иной мере затрагивает вопрос о границах.  Строит глазки
Записан
Eten
Гость
« Ответ #25 : Февраль 28, 2011, 18:02 »

Пардон, забыл кое-что к коду с функцией операций приложить:
Код:
Модификатор	Комментарий
2 +
3 -
4 *
5 /
6 ++
7 --
8 целочисленное деление
9 % (остаток от целочисленного деления)
10 ^
11 квадратный корень
12 модуль числа
13 минимальное число
14 максимальное число
15 целочисленный рандом от 0 и до m-1
16 log10
17 ln
18 exp
19 cos
20 sin
21 tg
22 ctg
23 arccos
24 arcsin
25 arctg
26 arcctg
27 округление в сторону нуля до целого
28 округление в большую сторону до целого
29 округление до целого
30 выделение дробной части
31 отсечение дробной части
32 рандом от 0 до 1

Число, соответсвует значению case  в условии.
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #26 : Февраль 28, 2011, 18:40 »

Ёлки зелёные))

И всё же почему в классе NNumeric не хранить переменную в double? Всё равно NNumeric у вас весит больше 4 байт.
А вместо операций, например в такой:
Код
C++ (Qt)
inline float Value() const {return this->data._value;}
 
 
делать следующее:
Код
C++ (Qt)
inline float Value() const {
   double d = fabs(this->data._value);
   bool sign = (this->data._value >= 0.0);
   if (d > std::numeric_limits<float>::max())
       return (sign) ? std::numeric_limits<float>::max() : -std::numeric_limits<float>::max();
   if (sign)
        return (d > std::numeric_limits<float>::min()) ? float(d) : float(0);
   return (d > std::numeric_limits<float>::min()) ? float(-d) : float(0);
}
 
double то Вам точно хватит)
« Последнее редактирование: Февраль 28, 2011, 18:52 от m_ax » Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #27 : Февраль 28, 2011, 19:12 »

про отлов nan, ещё более интересное условие:
Код
C++ (Qt)
if (!(bad==bad))  
printf("wrong float %g\n", bad);  
 
То да, но не стоит так дразнить компилятор  Улыбающийся

2Eten Пока не забыл - срочно избавьтесь от long, на моей платформе (Mac) он 4 байта только в 32-бит, но 8 в 64-бит. Если sizeof важен, используйте "явный" тип, напр. SInt32 (для Mac и Linuх он родной, а на Вындоуз заряжайте через typedef)

И всё же почему в классе NNumeric не хранить переменную в double? Всё равно NNumeric у вас весит больше 4 байт.
Как я понял, Eten крутит что-то типа своего интепретатора, ему нужно не просто "расчеты" а еще и с учетом типа. Какие-то операции пройдут для флотов, но не должны для int - так задумано

Код:
    if (d > std::numeric_limits<float>::max())
А как это должно работать?  Улыбающийся Если d = nan, то проверка не сработает. И что то за число больше максимального?

Мое предложение: вставить проверки на валидность и выбрасывать exception, напр так (псевдокод)

Код
C++ (Qt)
// static
void NNumeric::ValidateOperation( int operation, const NNumeric & arg1, const NNumeric & arg2 )
{
 if (arg1.Nan()) throw "arg1 is nan";  // стоп, с nan ничего доброго не выйдет
 if (arg2.Nan()) throw "arg2 is nan";  
 
// для конкретных операций
 switch (operation) {
  case NNumeric::operation_divide:
   if (!arg2.CanBeDivider()) throw "ZeroDivide";
   break;
   ...
 }
}
 
« Последнее редактирование: Февраль 28, 2011, 19:14 от Igors » Записан
Eten
Гость
« Ответ #28 : Март 01, 2011, 05:56 »

И всё же почему в классе NNumeric не хранить переменную в double? Всё равно NNumeric у вас весит больше 4 байт.
Ээм, в принципе для меня не страшно хранить больше 4 байт, важно, чтобы float в Windows, Mac и Linux в файл записывал 4 байта. А вот насчет типа double вы правы, особенно если учитывать различия в ситуациях c +/-inf, то в таком случае очень просто делать проверки на inf и nan. У меня только один вопрос. Сколько занимает байт тип float в ОС Windows, Mac и Linux? Надеюсь я не выйду за границы 4 байт, хотя по стандарту так и должно быть 4 байта (т.е. 32 бита).

Цитата: Igors
2Eten Пока не забыл - срочно избавьтесь от long, на моей платформе (Mac) он 4 байта только в 32-бит, но 8 в 64-бит. Если sizeof важен, используйте "явный" тип, напр. SInt32 (для Mac и Linuх он родной, а на Вындоуз заряжайте через typedef)
Кхм, кхм, а как мне в Си++ указать SInt32 так, чтобы на всех трех ОС оно занимало по 4 байта? Т.к. я пишу на Qt, то мне хватить qint32, но не возникнет ли проблемы с использованием std::numeric_limits<qint32>?

Цитата: Igors
Как я понял, Eten крутит что-то типа своего интепретатора, ему нужно не просто "расчеты" а еще и с учетом типа. Какие-то операции пройдут для флотов, но не должны для int - так задумано
Дело не в типе исполнителя, а в том, что мне нужно сохранить промежуточный тип (целый или вещественный). Если рассмотреть ситуацию суммы двух argX (X - номер) и то, что операция суммы и присвоения это две разных операций, то очень сохранить тип временной переменной (опять же вопрос границ тут присутствует). Т.е., если оба argX целые, то и временная переменная временная, иначе вещественные. А дальше и стека она поступает или на операцию присвоения (где учитывается тип временной и принимающей переменной, также как и в СИ только здесь с типами проще). И для выполнения некоторых операций, мне тоже перевод в целые, опять же из-за границ и т.д.  Подмигивающий

З.Ы.
Ладно, пойду перепишу класс с учетом всего здесь сказанного. Возьму типы qint32 и double для работы со всем этим, но на вопросы по ним прошу ответить в любом случае. Как перепишу, выложу новый вариант.  Подмигивающий
Записан
Eten
Гость
« Ответ #29 : Март 01, 2011, 06:08 »

Код:
    if (d > std::numeric_limits<float>::max())
А как это должно работать?  Улыбающийся Если d = nan, то проверка не сработает. И что то за число больше максимального?

Например, d = 2e208, тогда условие отреагирует, т.к. экспонента максимального нормализованного значения 38 (3.4e38). Также условие отреагирует, если d=+inf, но т.к. мы оперируем float и ориентируемся на границы, то получить +inf из-за превышения границы в самом d (который типа double) мы не можем, кроме случаев, когда у нас выходит +/-inf из-за некоторых мат. операций.  Подмигивающий Т.е. надо сюда проверку, на inf, но я что-то пока не надумал, что делать в этом случае.  В замешательстве
Записан
Страниц: 1 [2] 3   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.282 секунд. Запросов: 22.