LINUX.ORG.RU

Фигня с машинным эпсилон, или Либо я дурак...

 , ,


0

1

Эту задачку выполняют все начинающие. И вроде бы успешно, потому что задача тупая. Но у меня какая-то неожиданная чертовщина.

Так вот, берём два простеньких кода. Они настолько коротки, тупы и очевидны, что я даже не комментировал их.

Первый, работает нормально.

#include <iostream>

using std::cout;
using std::endl;

#include <iomanip>

using std::ios;
using std::setiosflags;

int main()
{
    float a, b, e;
    int counter;

    e=1.0; b=1.0; a=1.0;

    counter=0;

    do
    {
        e=e/2;
        a=b+e/2;

        counter++;
    }while (a>b);

    cout << setiosflags(ios::scientific);

    cout << sizeof(e) << endl;
    cout << "eps=" << e << endl;
    cout << "counter=" << counter << endl;

    return 0;
}

Второй, тоже вроде с виду пристойный, ещё и короче первого - казалось бы, ляпота!

#include <iostream>

using std::cout;
using std::endl;

#include <iomanip>

using std::ios;
using std::setiosflags;

int main()
{
    float a, e;
    int counter;

    e=1.0; a=1.0;

    counter=0;
    while (e/2+a>a)
    {
        e=e/2;

        counter++;
    }

    cout << setiosflags(ios::scientific);

    cout << sizeof(e) << endl;
    cout << "eps=" << e << endl;
    cout << "counter=" << counter << endl;

    return 0;
}

Оба кода написаны с помощью Qt Creator 2.0.1.

Определить машинное эпсилон с помощью первого кода для разных типов можно, меняя одно-единственное слово в тексте, ака тип переменных (ну заломало меня извращаться, пусть будет пока так). Для float первый код даёт значение эпсилон 1.19e-7, для double 2.22e-16, для long double 1.08e-19. *Голосом Малышевой* Это - нормально. НО: второй код для ЛЮБОГО типа выдаёт 1.08e-19. То бишь он какого-то хрена полосатого делает преобразование типа. Поговаривают, что в Builder то же самое. Онлайн-IDE, вот эта http://ideone.com/ , таких чудес не выдаёт, всё работает как надо. Вопрос: где и какого хрена происходит преобразование типа?



Последнее исправление: kozlik_kozlik (всего исправлений: 1)

Если тебе станет от этого легче, то твой код выдал такой результат на моём нетбуке:

4

eps=1.192093e-07

counter=23

anonymous
()

У меня не выдает. Все нормально меняется.

cdshines ★★★★★
()

Во втором исходнике вычисляется e/2+a, под эту переменную выделяется, судя по всему, long double - наиболее ёмкий из доступных типов (64bit система). И затем производится сравнение с a, преобразуемое к, опять таки, более ёмкому из двух типов - long double. У первого комментатора, судя по всему, система 32 разрядная.

В первом исходнике вы заранее определили переменные как float, в цикле проверялись два float.

fads ★★
()

use libgmp every day, and your hair will be soft and silky!

anonymous
()
Ответ на: комментарий от fads

У первого комментатора, судя по всему, система 32 разрядная

Нет, 64х-разрядная. Скорее всего, топик стартер что-то скрывает.

anonymous
()

Система x86? Опция -mpc32 спасет тебя.

Deleted
()
$ g++  q.cpp && ./a.out 
4
eps=1.192093e-07
counter=23
$ g++ -m32 q.cpp && ./a.out 
4
eps=1.084202e-19
counter=63
$ g++ -m32 -ffloat-store q.cpp && ./a.out 
4
eps=1.192093e-07
counter=23
i-rinat ★★★★★
()

Скорее всего во втором случае обе части сравнения e/2+a>a приводятся к long double. Точнее сначала приводится левая часть, как арифметическое выражение, затем для сравнения приводится и правая часть.

Belkrr
()

А не потому ли, что 2 - это int и результат выполнения e/2 компилятор приводит к long double? Хотя по стандарту

Many binary operators that expect operands of arithmetic or enumeration type cause conversions and yield result types in a similar way. The purpose is to yield a common type, which is also the type of the result. This pattern is called the usual arithmetic conversions, which are defined as follows:

  • If either operand is of scoped enumeration type (7.2), no conversions are performed; if the other operand does not have the same type, the expression is ill-formed.
  • If either operand is of type long double, the other shall be converted to long double.
  • Otherwise, if either operand is double, the other shall be converted to double.
  • Otherwise, if either operand is float, the other shall be converted to float.
  • ...

А что если попробовать e/2.0? e/2.0f?

Pavval ★★★★★
()
Последнее исправление: Pavval (всего исправлений: 1)
Ответ на: комментарий от kozlik_kozlik

Элементарно, Ватсон.

Машина на FPU всё вычисляет в long double. Но в первом коде ты присваиваешь значение float-переменной на стеке, а потом загружаешь обратно. Младшие разряды теряются.

Во втором коде всё выражение целиком вычисляется на FPU, преобразование типа не проводится.

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

geekless ★★
()
Ответ на: Элементарно, Ватсон. от geekless

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

Pavval ★★★★★
()

Qt офигеть как нужен, чтобы машинное эпсилон посчитать.

melkor217 ★★★★★
()
Ответ на: комментарий от Pavval

В итоге имеем поведение, которое противоречит стандарту, т.е. баг компилятора.

А теперь открываем стандарт и читаем:

The accuracy of the floating-point operations (+, -, *, /) and of the library functions in <math.h> and <complex.h> that return floating-point results is implementation-defined, as is the accuracy of the conversion between floating-point internal representations and string representations performed by the library functions in <stdio.h>, <stdlib.h>, and <wchar.h>. The implementation may state that the accuracy is unknown.

Так что ты стопроцентно прав:

стандарт знать обязательно всегда.

Вот ты, например, не знаешь.

geekless ★★
()
Последнее исправление: geekless (всего исправлений: 1)
Ответ на: комментарий от geekless

The accuracy of the floating-point operations (+, -, *, /) and of the library functions in <math.h> and <complex.h> that return floating-point results is implementation-defined, as is the accuracy of the conversion between floating-point internal representations and string representations performed by the library functions in <stdio.h>, <stdlib.h>, and <wchar.h>. The implementation may state that the accuracy is unknown.

Неприменимо, т.к. у нас нету указаных операций. a + e/2 пусть вычисляется в какой угодно аккуратностью, но последующий каст результата в float обязан происходить и сравнение (которое в твоей текстовке отсутствует) должно идти в float. Сравнение в принципе не имеет неточности.

Pavval ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.