MinGW 4.4: Странности с double
От: enji  
Дата: 25.07.10 07:38
Оценка:
Получается такая штука:
В одном месте программы есть double, который "чуть меньше" 10. После возврата его из функции и передаче в другую функцию, там оказывается double, который "чуть больше" 10. Эффект проявляется в релизном билде MinGW 4.4.0 (-O2) и не повторяется на MSVC (дебаг/релиз). Эффект исчезает, если вставить побольше дебажного вывода...

Код:


struct TNormalizeDouble
{
  double stagnat;
  int_fast16_t exp;
};

TNormalizeDouble normalizeDouble(double val)
{
  TNormalizeDouble res = {val, 0};
  if (res.stagnat < 0) // неверный вход - уходим
    return res;

  /* Использован код из стандартной библиотеки IAR (frmwri.c) */
  if (res.stagnat >= 1)
  {
    while (res.stagnat >= 1e11)        /* To speed up things a bit */
    {
      res.stagnat /= 1e10;
      res.exp += 10;
    }
    while (res.stagnat >= 10)
    {
      res.stagnat /= 10;
      res.exp++;
    }
  }
  else if (res.stagnat)            /* Not just 0.0 */
  {
    while (res.stagnat <= 1e-10)        /* To speed up things a bit */
    {
      //******* std::cout << "1e10 \n";
      res.stagnat *= 1e10;
      res.exp -= 10;
    }
    while (res.stagnat < 1)
    {
      //******* std::cout << "10 \n";
      res.stagnat *= 10;
      res.exp--;
    }
  }

  std::cout << res.stagnat << ' ' << (int)res.stagnat << ' ' << (res.stagnat < 10) << '\n';

  return res;
}

bool NormDoubleToBCD(TBCDLocation bcd, double value, uint_fast8_t bcdDigCnt)
{
  std::cout << value << ' ' << (int)value << ' ' << (value < 10) << '\n';
  ...
}  

void someFunc(double value)
{
  // тут код, который не трогает value

  TNormalizeDouble nd = normalizeDouble(value);
  
  // тут код, который не трогает nd 

  NormDoubleToBCD(bcd, nd.stagnat, bcdDigCnt);
  
}


При вызове someFunc(1e-20) получаю:

10 9 1
10 10 0


Если раскомментить помеченные ******* строки, то эффект пропадет
Re: MinGW 4.4: Странности с double
От: kpcb Россия  
Дата: 25.07.10 08:52
Оценка:
Здравствуйте, enji, Вы писали:

E>Получается такая штука:

E>В одном месте программы есть double, который "чуть меньше" 10. После возврата его из функции и передаче в другую функцию, там оказывается double, который "чуть больше" 10. Эффект проявляется в релизном билде MinGW 4.4.0 (-O2) и не повторяется на MSVC (дебаг/релиз). Эффект исчезает, если вставить побольше дебажного вывода...

Точность double ограничена. см numeric_limits<double>
Re[2]: Re: MinGW 4.4: Странности с double
От: enji  
Дата: 25.07.10 09:00
Оценка:
Здравствуйте, kpcb, Вы писали:

K>Точность double ограничена. см numeric_limits<double>


Ясно, что точность ограничена. И понятно, что 0.5 — это по факту 0.4999999999999999 или 0.5000000000000001.

Но не понятно, как в одном месте программы double < 10, а после нескольких копирований в другом месте этот же double >=10
Re: MinGW 4.4: Странности с double
От: Vain Россия google.ru
Дата: 25.07.10 20:02
Оценка:
Здравствуйте, enji, Вы писали:

E>Получается такая штука:

E>В одном месте программы есть double, который "чуть меньше" 10. После возврата его из функции и передаче в другую функцию, там оказывается double, который "чуть больше" 10. Эффект проявляется в релизном билде MinGW 4.4.0 (-O2) и не повторяется на MSVC (дебаг/релиз). Эффект исчезает, если вставить побольше дебажного вывода...
E>Код:
E>...
E> while (res.stagnat >= 1e11) /* To speed up things a bit */
E> {
E> res.stagnat /= 1e10;
E> res.exp += 10;
E> }
E>...
А чо это за говнокод? Умножение на 1e10 не эквивалентно сложению к экспоненте 10, это же экспонента по основанию 2, а не 10.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re[2]: MinGW 4.4: Странности с double
От: enji  
Дата: 26.07.10 04:24
Оценка:
Здравствуйте, Vain, Вы писали:

V>А чо это за говнокод? Умножение на 1e10 не эквивалентно сложению к экспоненте 10, это же экспонента по основанию 2, а не 10.


Код выдран из стандартной библиотеки компилятора IAR AVR.

Вашу мысль не понял — чем плохо умножение на 1e10?

Кроме того, дело ведь не в этом коде, как я понимаю. Каким образом double < 10 после копирований превращается в double >= 10?
Re[3]: MinGW 4.4: Странности с double
От: icWasya  
Дата: 26.07.10 06:19
Оценка:
Здравствуйте, enji, Вы писали:


E>Кроме того, дело ведь не в этом коде, как я понимаю. Каким образом double < 10 после копирований превращается в double >= 10?

Могу предположить, что без закомментированных строк промежуточные вычисления делаются в регистрах
с повышенной точностью(80-бит), а при раскомментированных — сохраняются в память с точностью double(64-бита)
Re[3]: MinGW 4.4: Странности с double
От: MasterZiv СССР  
Дата: 26.07.10 06:47
Оценка: -2
enji wrote:

> K>Точность double ограничена. см numeric_limits<double>

>
> Ясно, что точность ограничена. И понятно, что 0.5 — это по факту
> 0.4999999999999999 или 0.5000000000000001.

double вообще не может иметь какого-то определённого точного значения.
Он просто примерно 10, и при выводе (отладочном, где ты его наблюдаешь)
он выводится то как > 10 , то как < 10.
Posted via RSDN NNTP Server 2.1 beta
Re[4]: MinGW 4.4: Странности с double
От: enji  
Дата: 26.07.10 07:43
Оценка: +2
Здравствуйте, MasterZiv, Вы писали:

MZ>double вообще не может иметь какого-то определённого точного значения.

MZ>Он просто примерно 10, и при выводе (отладочном, где ты его наблюдаешь)
MZ>он выводится то как > 10 , то как < 10.

Не, стоп. Как это не может иметь точного значения? Конкретный double — это вполне конкретная последовательность бит. static_cast<int>(double) выдает вполне конкретное целое число, которое фиксировано для данного double.

И как в одном месте программы double может быть меньше 10, а в другом — больше, при том что между этими двумя местами он только копируется???
Re[4]: MinGW 4.4: Странности с double
От: enji  
Дата: 26.07.10 07:50
Оценка:
Здравствуйте, icWasya, Вы писали:

W>Здравствуйте, enji, Вы писали:



E>>Кроме того, дело ведь не в этом коде, как я понимаю. Каким образом double < 10 после копирований превращается в double >= 10?

W>Могу предположить, что без закомментированных строк промежуточные вычисления делаются в регистрах
W>с повышенной точностью(80-бит), а при раскомментированных — сохраняются в память с точностью double(64-бита)

Да у меня тоже были такие мысли, но по идее компилер должен как-то это дело обрабатывать, чтобы результат не менялся от вставки вызовов дополнительных функций и что бы если double < 10 в одном месте, то в другом он тоже был бы < 10...

Т.е. по идее косячок в MinGW?

Или есть какие-то флаги компиляции, которые это дело как-то регулируют?
Re[5]: MinGW 4.4: Странности с double
От: quodum  
Дата: 26.07.10 11:47
Оценка: 2 (1)
Здравствуйте, enji, Вы писали:

E>Или есть какие-то флаги компиляции, которые это дело как-то регулируют?


Возможно, дело в этом:

-fexcess-precision=style
This option allows further control over excess precision on machines where floating-point registers have more precision than the IEEE float and double types and the processor does not support operations rounding to those types. By default, -fexcess-precision=fast is in effect; this means that operations are carried out in the precision of the registers and that it is unpredictable when rounding to the types specified in the source code takes place.


GCC options
Re[6]: MinGW 4.4: Странности с double
От: enji  
Дата: 27.07.10 04:42
Оценка:
Здравствуйте, quodum, Вы писали:

Q>Возможно, дело в этом:


Q>

Q>-fexcess-precision=style
Q> This option allows further control over excess precision on machines where floating-point registers have more precision than the IEEE float and double types and the processor does not support operations rounding to those types. By default, -fexcess-precision=fast is in effect; this means that operations are carried out in the precision of the registers and that it is unpredictable when rounding to the types specified in the source code takes place.



-fexcess-precision поддерживается только для C. А вот -ffloat-store помогло. Спасибо!
Re[5]: MinGW 4.4: Странности с double
От: FR  
Дата: 29.07.10 06:49
Оценка:
Здравствуйте, enji, Вы писали:

E>Или есть какие-то флаги компиляции, которые это дело как-то регулируют?


Можно вместо double взять long double. Тогда в регистрах и памяти будет одно и тоже.
Re[3]: MinGW 4.4: Странности с double
От: Vain Россия google.ru
Дата: 03.08.10 03:46
Оценка:
Здравствуйте, enji, Вы писали:

V>>А чо это за говнокод? Умножение на 1e10 не эквивалентно сложению к экспоненте 10, это же экспонента по основанию 2, а не 10.

E>Код выдран из стандартной библиотеки компилятора IAR AVR.
E>Вашу мысль не понял — чем плохо умножение на 1e10?
Зависит от того как этот exp используется, если так и запаковывается в порядок, то не правильно.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re[4]: MinGW 4.4: Странности с double
От: enji  
Дата: 05.08.10 09:27
Оценка:
Здравствуйте, Vain, Вы писали:

V>>>А чо это за говнокод? Умножение на 1e10 не эквивалентно сложению к экспоненте 10, это же экспонента по основанию 2, а не 10.

E>>Код выдран из стандартной библиотеки компилятора IAR AVR.
E>>Вашу мысль не понял — чем плохо умножение на 1e10?
V>Зависит от того как этот exp используется, если так и запаковывается в порядок, то не правильно.

Все равно не пойму. Давайте упростим:

double val = ...; // пусть для простоты всегда больше 1


double norm = val;
int exp = 0;

while (norm >= 1e11)        /* To speed up things a bit */
{
    norm /= 1e10;
    exp += 10;
}
while (norm >= 10)
{
    norm /= 10;
    exp++;
}


assert(norm >= 1 && norm < 10);
assert(fabs(val - norm * pow10(exp)) < eps); // примерно равны


Вы считаете, тут что-то не так? Если да — то поясните пожалуйста...
... << RSDN@Home 1.2.0 alpha 4 rev. 1476>>
Re[5]: MinGW 4.4: Странности с double
От: Vain Россия google.ru
Дата: 05.08.10 13:09
Оценка:
Здравствуйте, enji, Вы писали:

V>>>>А чо это за говнокод? Умножение на 1e10 не эквивалентно сложению к экспоненте 10, это же экспонента по основанию 2, а не 10.

E>>>Код выдран из стандартной библиотеки компилятора IAR AVR.
E>>>Вашу мысль не понял — чем плохо умножение на 1e10?
V>>Зависит от того как этот exp используется, если так и запаковывается в порядок, то не правильно.
E>Все равно не пойму. Давайте упростим:
E>assert(fabs(val — norm * pow10(exp)) < eps); // примерно равны
E>Вы считаете, тут что-то не так? Если да — то поясните пожалуйста...
Если бы было pow2 то было бы не так, а на pow10 — умножать можно. Вот только не понятно что это за изврат, сначало N раз делить на X, а потом умножать один раз на N*X. assert при eps>0 может и сработать, всё зависит от самого значения в val.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re[6]: MinGW 4.4: Странности с double
От: enji  
Дата: 07.08.10 09:26
Оценка:
Здравствуйте, Vain, Вы писали:

V>Вот только не понятно что это за изврат, сначало N раз делить на X, а потом умножать один раз на N*X.

Исходный код был взят из функции printf стандартной библиотеки, его задача — нормализовать неотрицательный double (т.е. в результате получим или 0 или 1<=x<10 + десятичную экспоненту).
Все это надо чтобы преобразовать double в строку.
... << RSDN@Home 1.2.0 alpha 4 rev. 1476>>
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.