Что за хрень творится (math.h)
От: Yozh_Programmer  
Дата: 11.12.04 17:17
Оценка: 12 (1)
Почему этот код


#include <windows.h>
#include <stdio.h>
#define _USE_MATH_DEFINES
#include <math.h>
#include <conio.h>

const double Log10 = log(10.0);

int main()
{
    int x = 100;
    printf("%.20f\n\n", log((double)x) / Log10);
    printf("%.20f\n\n", log((double)x) / Log10);
    while (!kbhit()) {}
}


выдает такие результаты

1.99999999999999980000

2.00000000000000000000
Re: Что за хрень творится (math.h)
От: _Ramzes_ Россия  
Дата: 11.12.04 19:13
Оценка:
Здравствуйте, Yozh_Programmer, Вы писали:

Y_P>Почему этот код



Y_P>
Y_P>#include <windows.h>
Y_P>#include <stdio.h>
Y_P>#define _USE_MATH_DEFINES
Y_P>#include <math.h>
Y_P>#include <conio.h>

Y_P>const double Log10 = log(10.0);

Y_P>int main()
Y_P>{
Y_P>    int x = 100;
Y_P>    printf("%.20f\n\n", log((double)x) / Log10);
Y_P>    printf("%.20f\n\n", log((double)x) / Log10);
Y_P>    while (!kbhit()) {}
Y_P>}
Y_P>


Y_P>выдает такие результаты


Y_P>1.99999999999999980000


Y_P>2.00000000000000000000


У меня он выводит 1.99999999999999980000 в обоих случаях. В чем дело-то?
Re: Что за хрень творится (math.h)
От: HiFix Россия  
Дата: 11.12.04 19:21
Оценка:
Здравствуйте, Yozh_Programmer, Вы писали:

Y_P>Почему этот код



Y_P>
Y_P>#include <windows.h>
Y_P>#include <stdio.h>
Y_P>#define _USE_MATH_DEFINES
Y_P>#include <math.h>
Y_P>#include <conio.h>

Y_P>const double Log10 = log(10.0);

Y_P>int main()
Y_P>{
Y_P>    int x = 100;
Y_P>    printf("%.20f\n\n", log((double)x) / Log10);
Y_P>    printf("%.20f\n\n", log((double)x) / Log10);
Y_P>    while (!kbhit()) {}
Y_P>}
Y_P>


Y_P>выдает такие результаты


Y_P>1.99999999999999980000


Y_P>2.00000000000000000000


Почитай про правила операций над числами с плавающей точкой, например здесь
... << RSDN@Home Sco
Не бейте, я только учусь
Re: Что за хрень творится (math.h)
От: Dr.Gigabit  
Дата: 11.12.04 19:35
Оценка:
Здравствуйте, Yozh_Programmer, Вы писали:

Y_P>Почему этот код


[skiped]

Y_P>выдает такие результаты


Y_P>1.99999999999999980000


Y_P>2.00000000000000000000


Еще бы не помешало знать, какой компилятор
<< RSDN@Home 1.1.4 @@subversion >>
Re[2]: Что за хрень творится (math.h)
От: _Winnie Россия C++.freerun
Дата: 11.12.04 20:48
Оценка: 1 (1) +1 -2
Здравствуйте, HiFix, Вы писали:

HF>Почитай про правила операций над числами с плавающей точкой, например здесь

Нисагласен.
Тут гораздо интересней, что результат разный в одинаковых строчках.
Правильно работающая программа — просто частный случай Undefined Behavior
Re: Что за хрень творится (math.h)
От: _Winnie Россия C++.freerun
Дата: 11.12.04 20:52
Оценка:
Здравствуйте, Yozh_Programmer, Вы писали:

Y_P>Почему этот код

Y_P>выдает такие результаты

Y_P>1.99999999999999980000


Y_P>2.00000000000000000000


Ниверю.
Что именно этот.
Какой компилятор/платформа/настройки.

cl 7.1 — 1.99999999999999980000
cl 8.0 — 1.99999999999999980000
icl — 2.00000000000000000000
mingw — 2.00000000000000000000
Правильно работающая программа — просто частный случай Undefined Behavior
Re[3]: Что за хрень творится (math.h)
От: HiFix Россия  
Дата: 11.12.04 21:10
Оценка:
Здравствуйте, _Winnie, Вы писали:
_W>Нисагласен.
С чем?
_W>Тут гораздо интересней, что результат разный в одинаковых строчках.
Действительно интересно VC7.1 в дебаге выдает в обоих случаях 2.00000, а в релизе 1.99999999999999980000. Причем 1.99 он выдает только в первом случае, а во втором, третьем, десятом 2.00. Надо самому еще раз про floating-point operations почитать .
... << RSDN@Home ...и тишина... а по бокам мертвые с косами стоят...
Не бейте, я только учусь
Re[2]: Что за хрень творится (math.h)
От: HiFix Россия  
Дата: 11.12.04 21:14
Оценка:
Здравствуйте, _Winnie, Вы писали:



_W>Ниверю.

_W>Что именно этот.
Именно этот код. Сам только что проверял.
_W>Какой компилятор/платформа/настройки.
Платформа Win32, компилятор VC7.1, релиз (в дебаг все нормально).
... << RSDN@Home ...и тишина... а по бокам мертвые с косами стоят...
Не бейте, я только учусь
Re: Что за хрень творится (math.h)
От: Yozh_Programmer  
Дата: 11.12.04 21:17
Оценка:
MSVC++ 7.1

1.99999999999999980000

2.00000000000000000000

MSVC++ 8 (Visual C++ 2005 Express Edition Beta)

1.99999999999999980000

1.99999999999999980000
Re: Что за хрень творится (math.h)
От: Yozh_Programmer  
Дата: 12.12.04 01:22
Оценка:
Проверил на VC++ 8 (VC++ 2005 beta express) в релизе

и тоже разные результаты

Я с ума сойду!!!
Re: Что за хрень творится (math.h)
От: Alex Reyst Россия  
Дата: 12.12.04 05:03
Оценка:
Здравствуйте, Yozh_Programmer, Вы писали:

Y_P>Почему этот код

Y_P>выдает такие результаты

Ну так посмотри ассемблерный код! Может быть это зависит от каких-то конкретных режимов оптимизации, а все здесь в рулетку играют.

У меня во всех случаях 1.99999999999999980000

Условия:

WinXP, VS2003, Release/Debug, процессор Athlon — ибо здесь могут быть и фокусы на аппаратном уровне; настройка компилятора в Release:

/Od /Ob2 /Op /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_ATL_MIN_CRT" /D "_MBCS" /FD /MT /GS /Fo"Release/" /Fd"Release/vc70.pdb" /W3 /nologo /c /Wp64 /Zi /TP


Код:
    printf("%.20f\n\n", log((double)x) / Log10);
0040103D DB 45 FC         fild        dword ptr [x] 
00401040 DD 5D EC         fstp        qword ptr [ebp-14h] 
00401043 DD 45 EC         fld         qword ptr [ebp-14h] 
00401046 83 EC 08         sub         esp,8 
00401049 DD 1C 24         fstp        qword ptr [esp] 
0040104C E8 CF 00 00 00   call        log (401120h) 
00401051 83 C4 08         add         esp,8 
00401054 DC 35 00 02 41 00 fdiv        qword ptr [Log10 (410200h)] 
0040105A 83 EC 08         sub         esp,8 
0040105D DD 1C 24         fstp        qword ptr [esp] 
00401060 68 34 C1 40 00   push        offset KERNEL32_NULL_THUNK_DATA+30h (40C134h) 
00401065 E8 44 00 00 00   call        printf (4010AEh) 
0040106A 83 C4 0C         add         esp,0Ch
Все, что здесь сказано, может и будет использоваться против меня...
double - одинаковые вычисления, разные результаты
От: What Беларусь  
Дата: 12.12.04 12:17
Оценка: 98 (10)
#Имя: FAQ.cpp.double.Op
Здравствуйте, Yozh_Programmer, Вы писали:

Y_P>Почему этот код



Y_P>
Y_P>#include <windows.h>
Y_P>#include <stdio.h>
Y_P>#define _USE_MATH_DEFINES
Y_P>#include <math.h>
Y_P>#include <conio.h>

Y_P>const double Log10 = log(10.0);

Y_P>int main()
Y_P>{
Y_P>    int x = 100;
Y_P>    printf("%.20f\n\n", log((double)x) / Log10);
Y_P>    printf("%.20f\n\n", log((double)x) / Log10);
Y_P>    while (!kbhit()) {}
Y_P>}
Y_P>


Y_P>выдает такие результаты


Y_P>1.99999999999999980000


Y_P>2.00000000000000000000


Если хочешь, чтобы результаты были одинаковые — используй опцию компиляции /Op (Improve Float Consistency)
Из MSDN:

This option improves the consistency of floating-point tests for equality and inequality by disabling optimizations that could change the precision of floating-point calculations.

By default, the compiler uses the coprocessor's 80-bit registers to hold the intermediate results of floating-point calculations. This increases program speed and decreases program size. However, because the calculation involves floating-point data types that are represented in memory by less than 80 bits, carrying the extra bits of precision (80 bits minus the number of bits in a smaller floating-point type) through a lengthy calculation can produce inconsistent results.

With /Op, the compiler loads data from memory prior to each floating-point operation and, if assignment occurs, writes the results back to memory upon completion. Loading the data prior to each operation guarantees that the data does not retain any significance greater than the capacity of its type.

A program compiled with /Op may be slower and larger than one compiled without /Op.

... << RSDN@Home 1.1.4 beta 3 rev. 185>>
Re: Что за хрень творится (math.h)
От: Аноним  
Дата: 13.10.06 03:36
Оценка:
Вполне возможная вещь. Числа с плавающей точкой вообще очень копризные.
У меня однажды в 6 была такая ситуация. На каждом шаге цикла с double переменной
прибавляется 0.1. Всё было замечательно, но на одном шаге он почемуто не прибавлял.
Не помню какое это было число. Но получил я его только со второго раза.
По этому стараюсь точную инфу хранить в целых числах.
Re: Что за хрень творится (math.h)
От: ДимДимыч Украина http://klug.org.ua
Дата: 13.10.06 06:40
Оценка:
Здравствуйте, Yozh_Programmer, Вы писали:

Y_P>Почему этот код выдает такие результаты


Y_P>1.99999999999999980000

Y_P>2.00000000000000000000

У меня выдает

2.00000000000000000000

2.00000000000000000000

на любом уровне оптимизации, gcc 3.3.6.
Обязательно бахнем! И не раз. Весь мир в труху! Но потом. (ДМБ)
Re: Что за хрень творится (math.h)
От: johny5 Новая Зеландия
Дата: 13.10.06 06:53
Оценка:
http://www.rsdn.ru/forum/?mid=1919364
Автор: johny5
Дата: 26.05.06
Re: Что за хрень творится (math.h)
От: Silent Bob  
Дата: 13.10.06 09:22
Оценка: -1
Здравствуйте, Yozh_Programmer, Вы писали:

Это printf шалит. Если сделать так:

#include <windows.h>
#include <stdio.h>
#define _USE_MATH_DEFINES
#include <math.h>
#include <conio.h>

const double Log10 = log(10.0);

int main()
{
    int x = 100;
    double a = log((double)x) / Log10;
    double b = log((double)x) / Log10;
    printf("%.20f\n\n", a);
    printf("%.20f\n\n", b);
    while (!kbhit()) {}
}


то результат будет одинаков:

1.99999999999999980000 — при дефолтных настройках и 2.00000000000000000000 с /Op
Re[2]: Что за хрень творится (math.h)
От: Кодт Россия  
Дата: 13.10.06 09:55
Оценка:
Здравствуйте, Silent Bob, Вы писали:

SB>Это printf шалит.


Не знаю, считать ли это шалостью. Вычисления зависят от необнулённых флагов FPU, которые отличаются
double a = .....;
// здесь
double b = .....;

// и

double a = .....;
printf("%.20f\n", a); // внутри тоже пользуется плавающей арифметикой - для перевода double в десятичный вид
// здесь
double b = .....;
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re: Что за хрень творится (math.h)
От: _DAle_ Беларусь  
Дата: 13.10.06 18:18
Оценка:
Здравствуйте, Yozh_Programmer, Вы писали:

Y_P>Почему этот код



Y_P>
Y_P>#include <windows.h>
Y_P>#include <stdio.h>
Y_P>#define _USE_MATH_DEFINES
Y_P>#include <math.h>
Y_P>#include <conio.h>

Y_P>const double Log10 = log(10.0);

Y_P>int main()
Y_P>{
Y_P>    int x = 100;
Y_P>    printf("%.20f\n\n", log((double)x) / Log10);
Y_P>    printf("%.20f\n\n", log((double)x) / Log10);
Y_P>    while (!kbhit()) {}
Y_P>}
Y_P>


Y_P>выдает такие результаты


Y_P>1.99999999999999980000


Y_P>2.00000000000000000000


http://www.parashift.com/c++-faq-lite/newbie.html#faq-29.18
Re: Что за хрень творится (math.h)
От: volk  
Дата: 14.10.06 08:57
Оценка:
Здравствуйте, Yozh_Programmer, Вы писали:

Y_P>Почему этот код выдает такие результаты

Y_P>1.99999999999999980000
Y_P>2.00000000000000000000

Такое поведение является абсолютно нормальным (в том смысле, что его допускают все имеющие отношение к теме стандарты). Причин такому фокусу может быть мильон и вот одна из них.

Процессор умеет представлять вещественные числа в 80-битном формате. А вот если они выгружаются в память, то число будет усечено до 64 бит. Результат расчета зависит от того, было ли число сохранено и потом получено из памяти, или все вычисления были проделаны с привлечением только регистров процессора.

Где в твоей программе имеет место этот эффект и он ли это -- можно только гадать (или посмотреть ассемблерный листинг). Скорее всего, чудо случается при подготовке результирующей строчки в printf.
Тот, кто желает, но не делает, распространяет чуму.
Re: Что за хрень творится (math.h)
От: icWasya  
Дата: 16.10.06 06:31
Оценка:
Здравствуйте, Yozh_Programmer, Вы писали:

Y_P>Почему этот код


...

Y_P>выдает такие результаты


Y_P>1.99999999999999980000


Y_P>2.00000000000000000000


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