Проблема с вещественным числами
Столкнулся с проблемой C++ приведения вещественных чисел к целому типу. Этой проблемы в др. языках нет. Вот примеры:
В результате получается, что в переменной n содержится число 7, а не 8, как должно быть по логике!
В итоге получается, что здесь тоже выражение ложное. А должно быть истинным!
Я понимаю, что на самом деле в C++ вещественное число 8.0 записывается как 7.9999(9).
Но как же тогда программировать?!!! Как вы, профессиональные программисты, решаете эти проблемы?
Что нужно сделать, чтобы два примера, приведенные мною выше, выполнялись бы КАК НАДО?!
Недавно перешел на C++. До этого очень активно программировал на Perl и Delphi.
Столкнулся с проблемой C++ приведения вещественных чисел к целому типу. Этой проблемы в др. языках нет. Вот примеры:
В результате получается, что в переменной n содержится число 7, а не 8, как должно быть по логике!
В итоге получается, что здесь тоже выражение ложное. А должно быть истинным!
Я понимаю, что на самом деле в C++ вещественное число 8.0 записывается как 7.9999(9).
Но как же тогда программировать?!!! Как вы, профессиональные программисты, решаете эти проблемы?
Что нужно сделать, чтобы два примера, приведенные мною выше, выполнялись бы КАК НАДО?!
Странно, но в С# то же самое(это о др. языках). Это все нормальная потеря при приведении. Культурние надо быть. Преобразование к int идет путем отбрасывания дробной части, а не через округление. Если точность важна округляй по мат. правилам, а не приводи типы.
Странно, но в С# то же самое(это о др. языках). Это все нормальная потеря при приведении. Культурние надо быть. Преобразование к int идет путем отбрасывания дробной части, а не через округление. Если точность важна округляй по мат. правилам, а не приводи типы.
Округление - это не выход, т.к. иногда нужно просто отбросить именно дробную часть. Так же хочу заметить, что всегда программа должна выполнять вычисления со 100% точностью.
Не понимаю... Каждый знает, что (0.7+0.1)*10=8
Это вычисление может сделать со 100% точностью любой ребенок.
Так почему же C++ эти вычисления делает так неточно???!!! Как после этого программировать?
Должно быть какое-то решение, чтобы получить из вышеперечисленных выражений нормальные результаты.
Округление - это не выход, т.к. иногда нужно просто отбросить именно дробную часть. Так же хочу заметить, что всегда программа должна выполнять вычисления со 100% точностью.
Не понимаю... Каждый знает, что (0.7+0.1)*10=8
Это вычисление может сделать со 100% точностью любой ребенок.
Так почему же C++ эти вычисления делает так неточно???!!! Как после этого программировать?
Должно быть какое-то решение, чтобы получить из вышеперечисленных выражений нормальные результаты.
Для того чтобы округлить, а не отбросить дробную часть, просто прибавь к округляемому выражению 0.5. А в твоём примере ошибка связана с числом 0.7, дробная часть которого не является степенью двойки. Заменяем на 0.8 - всё канает... В общем, без введения некоторой очень малой погрешности, на мой взгляд, здесь не обойтись. Кстати, это самые неуловимые ошибки - ошибки округления. Каждый, кто с ними сталкивался на практике (когда часов шесть сидишь и пускаешь программу под отладчиком, пытаясь найти строчку кода с ошибкой, а потом выясняется, что ошибку сделал вовсе и не ты, а проклятая машина), с этим согласится. Увы...
Округление - это не выход, т.к. иногда нужно просто отбросить именно дробную часть. Так же хочу заметить, что всегда программа должна выполнять вычисления со 100% точностью.
Не понимаю... Каждый знает, что (0.7+0.1)*10=8
Это вычисление может сделать со 100% точностью любой ребенок.
Так почему же C++ эти вычисления делает так неточно???!!! Как после этого программировать?
Должно быть какое-то решение, чтобы получить из вышеперечисленных выражений нормальные результаты.
Вам уже сказали, что дело не в C++. Будьте же грамотны! Машина - не человек, она обладает конечной разрядностью (читай - точночтью). В любой цифровой системе Вы не получите 100% точности (при любом диапазоне). НИКОГДА. Отсюда вывод - либо проводите сравнение чисел с плавающей точкой с определенной точностью, либо переходите на целочисленную арифметику (но с резким уменьшением диапазона чисел).
И еще советую почитать о форматах хранения чисел в цифровых устройствах.
Так почему же C++ эти вычисления делает так неточно???!!! Как после этого программировать?
После этого на АСМ'е для FPU:!!!: :!!!: :!!!:
советую следующее правило : всегда.. то есть нет... никогда не перемешивай в одном выражении вычисления с плавающей точкой и целочисленные вычисления. так следующий код работает нормально.
int b = a;
assert( b == 8 );
Вам уже сказали, что дело не в C++. Будьте же грамотны! Машина - не человек, она обладает конечной разрядностью (читай - точночтью). В любой цифровой системе Вы не получите 100% точности (при любом диапазоне). НИКОГДА. Отсюда вывод - либо проводите сравнение чисел с плавающей точкой с определенной точностью, либо переходите на целочисленную арифметику (но с резким уменьшением диапазона чисел).
И еще советую почитать о форматах хранения чисел в цифровых устройствах.
Вы пишете, что такие погрешности в числах с плавающей точкой - это нормально. Но ведь, к примеру, в Perl и Delphi все считается 100% без погрешностей. Т.е. если я в этих языках после вычислений получаю в пременную значение 0.8, то я точно уверен, что это именно 0.8, а не 0.7999999999999999999.
Не смешивать переменные разных типов в вычислениях нельзя, т.к. часто в результате вычислений получаются значения, которые в дальнейшем нужно сравнивать. И я не могу заранее сказать, константы каких типов будут сравниваться между собой.
Может быть есть какие-то альтернативные типы данных, у которых нет проблем с погрешностями?
Повторюсь, на всех остальных языках программирования таких проблем нет!
7.(9) = 8. отбрасываем от первого числа дробную часть и получаем 7.
Не смешивать переменные разных типов в вычислениях нельзя, т.к. часто в результате вычислений получаются значения, которые в дальнейшем нужно сравнивать. И я не могу заранее сказать, константы каких типов будут сравниваться между собой.
бред. я ж тебе написал правило и пример кода дал для подражания. прежде чем делать столь категоричные утвержедния научись правильно организовывать код на C++.
Вы пишете, что такие погрешности в числах с плавающей точкой - это нормально. Но ведь, к примеру, в Perl и Delphi все считается 100% без погрешностей. Т.е. если я в этих языках после вычислений получаю в пременную значение 0.8, то я точно уверен, что это именно 0.8, а не 0.7999999999999999999.
Не смешивать переменные разных типов в вычислениях нельзя, т.к. часто в результате вычислений получаются значения, которые в дальнейшем нужно сравнивать. И я не могу заранее сказать, константы каких типов будут сравниваться между собой.
Может быть есть какие-то альтернативные типы данных, у которых нет проблем с погрешностями?
Повторюсь, на всех остальных языках программирования таких проблем нет!
Хм... Дело-то в том, что компилятор вычисляет выражение int n = (0.1 + 0.7) * 10 на этапе компиляции, а не по ходу выполнения программы. А вот float a = (0.1 + 0.7) * 10; int b = a; - по ходу выполнения:
mov DWORD PTR _n$[ebp], 7
; 6 : float f = (0.1 + 0.7)*10;
mov DWORD PTR _f$[ebp], 1090519040 ; 41000000H
; 7 : int a = f;
fld DWORD PTR _f$[ebp]
call __ftol2
mov DWORD PTR _a$[ebp], eax
С другой стороны, скомпилировав тот же код под Release, получим, что оба выражения оптимизатор скомпоновал сразу на этапе компиляции, однако так же получим в первом случае 7, во втором - 8. То есть:
; 6 : float f = (0.1 + 0.7)*10;
; 7 : int a = f;
; 8 :
; 9 : std::cout << n << std::endl << a << std::endl;
push 7
push OFFSET FLAT:?cout@std@@3V?$basic_ostream@...
call ??6?$basic_ostream@DU?$char_traits@D@std...
push 8
push eax
call ?endl@std@@YAAAV?$basic_ostream@DU?$... ; std::endl
Хм... Дело-то в том, что компилятор вычисляет выражение int n = (0.1 + 0.7) * 10 на этапе компиляции, а не по ходу выполнения программы. А вот float a = (0.1 + 0.7) * 10; int b = a; - по ходу выполнения:
mov DWORD PTR _n$[ebp], 7
; 6 : float f = (0.1 + 0.7)*10;
mov DWORD PTR _f$[ebp], 1090519040 ; 41000000H
; 7 : int a = f;
fld DWORD PTR _f$[ebp]
call __ftol2
mov DWORD PTR _a$[ebp], eax
С другой стороны, скомпилировав тот же код под Release, получим, что оба выражения оптимизатор скомпоновал сразу на этапе компиляции, однако так же получим в первом случае 7, во втором - 8. То есть:
; 6 : float f = (0.1 + 0.7)*10;
; 7 : int a = f;
; 8 :
; 9 : std::cout << n << std::endl << a << std::endl;
push 7
push OFFSET FLAT:?cout@std@@3V?$basic_ostream@...
call ??6?$basic_ostream@DU?$char_traits@D@std...
push 8
push eax
call ?endl@std@@YAAAV?$basic_ostream@DU?$... ; std::endl
может это прозвучит смешно но это потому что компилятор тоже написан на C++. :))))). но это только одно из правильных объяснений.
Как-то странно получается... Скорее всего, для первого выражения конверсия происходит с помощью CPU, а для второго - FPU, и поэтому происходят несовпадения. Вот только второй из приведённых мной примеров ставит меня в тупик... Почему тогда во втором выражении компилятор не допустил ту же ошибку, что и в первом?
А ты попробуй так:
int n = (0.1f + 0.7f)*10;
или вот так:
int n = float(0.1 + 0.7)*10;
Может быть есть какие-то альтернативные типы данных, у которых нет проблем с погрешностями?
А у float таких проблем и нет. Точность одинаковая что в Perl, что в C#, что в VB. Это от процессора зависит. А насчёт 100% точности в других языках ты не прав: в Delphi ты вообще такой код не напишешь, там конверсию из real в integer можно сделать только через библиотечные функции, а perl - язык вообще интерпетируемый, а значит выражение там будет вычисляться в поэтапно в runtime. В нашем же случае ошибка связана с конверсией на этапе компиляции, а именно при вычислении выражения челочисленным процессором. Если бы в Dephi можно было бы написать n := Integer((0.1 + 0.7) * 10), то, поверь мне, возникла бы точ такая же ошибка, потому что в таком случае конверсия в целое происходила бы на уровне CPU. Просто нужно явно указать компилятору, что в данном случае нужно провести конверсию препроцессором, как более специализированным логическим устройством, и всё будет OK. Поэтому см. пример от PitxBull
А ты попробуй так:
int n = (0.1f + 0.7f)*10;
или вот так:
int n = float(0.1 + 0.7)*10;
Умные все стали! Вам не угодишь... :) Я по всякому уже крутил:
int n = (float) ((0.1 + 0.7) *10.)
Лечится это (частично) так....
Не верный вариант:
А вот верный вариант:
------------------
Не верный вариант:
А вот верный вариант:
Т.е. принудительно надо привести выражение в нужный тип (в данном случае float), а уж потом отбросить дробную часть. Тип (float или double) выбираем в зависимости от нужной степени точность - у float она больше.
Если нужна супер-точность, лучше выбирать long float.
В выражении ниже в переменную n присваивается число 8:
А вот в этом выражении в переменную n присваивается уже число 7!!!
Прошу это объяснить, пожалуйста, ведь я посмотрел: тип double предоставляет большую точность, чем тип float.
Хотя действительно еще кое-что не понятно...
В выражении ниже в переменную n присваивается число 8:
А вот в этом выражении в переменную n присваивается уже число 7!!!
Прошу это объяснить, пожалуйста, ведь я посмотрел: тип double предоставляет большую точность, чем тип float.
(float) (0.1+0.7) равно 0.8
(double) (0.1+0.7) равно 0.79999999999999993
умножается на 10 , отбрасывается дробная часть и получаешь то что получаешь.
А вот цитата из MSDN c правилами преобразования.
**********************
Conversions from Floating-Point Types
A float value converted to a double or long double, or a double converted to a long double, undergoes no change in value. A double value converted to a float value is represented exactly, if possible. Precision may be lost if the value cannot be represented exactly. If the result is out of range, the behavior is undefined. See Limits on Floating-Point Constants in Chapter 1 for the range of floating-point types.
A floating value is converted to an integral value by first converting to a long, then from the long value to the specific integral value, as described below in Table 4.4. The decimal portion of the floating value is discarded in the conversion to a long. If the result is still too large to fit into a long, the result of the conversion is undefined.
Microsoft Specific —>
When converting a double or long double floating-point number to a smaller floating-point number, the value of the floating-point variable is truncated toward zero when an underflow occurs. An overflow causes a run-time error. Note that the Microsoft C compiler maps long double to type double.
END Microsoft
/////////////////////////////////////
Так это работает
Нравится или не нравится, но учитывать придется
Ребята, спасибо за пищу для размышлений и критику. Я немного разобралсмя. В итоге имеем следующее: в C++ есть проблема c точностью чисел с плавающей запятой.
Лечится это (частично) так....
Не верный вариант:
А вот верный вариант:
------------------
Не верный вариант:
А вот верный вариант:
Т.е. принудительно надо привести выражение в нужный тип (в данном случае float), а уж потом отбросить дробную часть. Тип (float или double) выбираем в зависимости от нужной степени точность - у float она больше.
Если нужна супер-точность, лучше выбирать long float.
Еще раз для особо понятливых. Дело НЕ в C++ - так работает арифметика с плавающей точкой в FPU. Попробуйте изменить в Дельфи строку
a := (0.1 + 0.7) * 10.1;
if a=8.08 then
writeln("Не может быть!");
Только что проверено в Дельфи 5. Не сработает!
Просто "умные" дельфийские ф-ции при определенной точности сами округляют число.
Кстати, верный вариант совсем другой.
1. Отбрасывание дробной части
2. Округление
static_cast<int>(someDouble - 0.5) :
static_cast<int>(someDouble + 0.5);
PS В предыдущем своем посте я ошибся. Есть одно число, представленное с абсолютной точностью - это ноль (0.0).
Умные все стали! Вам не угодишь... :) Я по всякому уже крутил:
int n = (float) ((0.1 + 0.7) *10.)
Да я и был умный. :)
Опрерации с плавающей точкой описаны стандартом ANSI/IEEE 754-1985 (там несколько последующих стандартов, но этот базовый).
На сколько помню, стандарт говорит о том, что для float после каждой операции в 80-битном регистре должно производится приведение опять к float. Это требует доп. расходов времени, поэтому по умолчанию в компиляторах VC++ это предписание стандарта отключено. Включить его можно зайдя в свойства проекта C/C++ -> Optimization -> Floating-Point Consistency заменив значение Default на Improve.
Да я и был умный. :)
Опрерации с плавающей точкой описаны стандартом ANSI/IEEE 754-1985 (там несколько последующих стандартов, но этот базовый).
На сколько помню, стандарт говорит о том, что для float после каждой операции в 80-битном регистре должно производится приведение опять к float. Это требует доп. расходов времени, поэтому по умолчанию в компиляторах VC++ это предписание стандарта отключено. Включить его можно зайдя в свойства проекта C/C++ -> Optimization -> Floating-Point Consistency заменив значение Default на Improve.
Вместо прибавления 0.5 умножай округляемое число на
1+16*DBL_EPSILON - и программа будет отбрасывать
дробную часть, и (0.7+0.1)*8 будет давать 8.
Да я и был умный. :)
Опрерации с плавающей точкой описаны стандартом ANSI/IEEE 754-1985 (там несколько последующих стандартов, но этот базовый).
На сколько помню, стандарт говорит о том, что для float после каждой операции в 80-битном регистре должно производится приведение опять к float. Это требует доп. расходов времени, поэтому по умолчанию в компиляторах VC++ это предписание стандарта отключено. Включить его можно зайдя в свойства проекта C/C++ -> Optimization -> Floating-Point Consistency заменив значение Default на Improve.
Ну, чё-то мы уже в дебри полезли. А если я захочу проект под более старым компилятором собрать? А если проект ещё и чужой, и я понятия не имею о включенной Improve Concistency? Что тогда? Вообще, на мой взгяд, в тех случаях, когда речь идёт о точности, целиком полагаться на настройки компилятора просто глупо
(float) (0.1+0.7) равно 0.8
(double) (0.1+0.7) равно 0.79999999999999993
Прошу объяснить, почему в этом примере при приведении к типу float в результате выражения получается 0.8, а при приведени к double получается 0.79999(9)?
Я все прочитал в этой теме, но не мегу получить ответ на этот вопрос.
Прошу объяснить, почему в этом примере при приведении к типу float в результате выражения получается 0.8, а при приведени к double получается 0.79999(9)?
Я все прочитал в этой теме, но не мегу получить ответ на этот вопрос.
Из-за способа представления (приближенно) и из-за
того, что результат действия в плавающей арифметике не может быть точен.
((0.7+0.1)*8)(1.+16.*DBL_EPSILON)
(можно сделать отдельную функцию, чтобы
на это не смотреть постоянно)
будет точно 8. (но опять-таки может отклоняться
только в большую сторону до 0.000000000001).
Прошу объяснить, почему в этом примере при приведении к типу float в результате выражения получается 0.8, а при приведени к double получается 0.79999(9)?
Я все прочитал в этой теме, но не мегу получить ответ на этот вопрос.
Да с чего вы взяли, что будет 0.8?
Простенький эксперимент:
printf("a=%.10f\n", a);
double b = 0.1 + 0.7;
printf("b=%.10f\n", b);
В результате:
a=0.8000000119
b=0.8000000000
При этом отмечу, что на самом деле b != 0.8, это иллюзия :).
Другой пример:
ASSERT(a == (float)0.8 );
ASSERT(a == 0.8 );
Вопрос - какой ASSERT не пропустит?
Ответ - второй, т.к. там а приводится к double, т.е. типу с повышенной точностью. Почему пропустил первый? Точность ниже - при преобразовании к float компилятор обрубил все лишнее. Почему не срабатывает double == double? Обрубать нечего - все честно.
if (a == (float)0.7)
сравнение вообще будет произведено в CPU (float - то 32 разрядный), поэтому условие сработает. Если же жесткого приведения к float не будет, то сравниваться будет в FPU, где все будет приведено к 80 разрядам (а double - 64 разрядный). Вот и неточность.
следующий код ( приведенный мной ) работает при любых настройках компилятора:
int b = a;
assert( b == 8 );
следующий код ( приведенный Green-ом ) работает только если включена соответсвующая опция компилятора ( Improve Consistency /Op ).
этот код не работает правильно никогда :
вот процедура реализующая в моем компиляторе ( VC++ 7 ) приведение к int.
004300AC push ebp
004300AD mov ebp,esp
004300AF sub esp,20h
004300B2 and esp,0FFFFFFF0h
004300B5 fld st(0)
004300B7 fst dword ptr [esp+18h]
004300BB fistp qword ptr [esp+10h]
004300BF fild qword ptr [esp+10h]
004300C3 mov edx,dword ptr [esp+18h]
004300C7 mov eax,dword ptr [esp+10h]
004300CB test eax,eax
004300CD je integer_QnaN_or_zero (43010Bh)
arg_is_not_integer_QnaN:
004300CF fsubp st(1),st // отнимаем от исходного числа то же число но без дробной части.(1)
004300D1 test edx,edx
004300D3 jns positive (4300F3h)
004300D5 fstp dword ptr [esp]
004300D8 mov ecx,dword ptr [esp]
004300DB xor ecx,80000000h
004300E1 add ecx,7FFFFFFFh
004300E7 adc eax,0
004300EA mov edx,dword ptr [esp+14h]
004300EE adc edx,0
004300F1 jmp localexit (43011Fh)
positive:
004300F3 fstp dword ptr [esp]
004300F6 mov ecx,dword ptr [esp] // в этих строках по всей видимости остаток от операции (1) проверяется
004300F9 add ecx,7FFFFFFFh // на то больше он чем 0.5 или нет
004300FF sbb eax,0 // именно после этой комманды 8 превращается в 7 ( регистр EAX).
00430102 mov edx,dword ptr [esp+14h]
00430106 sbb edx,0
00430109 jmp localexit (43011Fh)
integer_QnaN_or_zero:
0043010B mov edx,dword ptr [esp+14h]
0043010F test edx,7FFFFFFFh
00430115 jne arg_is_not_integer_QnaN (4300CFh)
00430117 fstp dword ptr [esp+18h]
0043011B fstp dword ptr [esp+18h]
localexit:
0043011F leave
00430120 ret
Прошу объяснить, почему в этом примере при приведении к типу float в результате выражения получается 0.8, а при приведени к double получается 0.79999(9)?
Вчера покапался в книгах и нашел ответ на данный вопрос...
В книге написано что по стандартам C/C++ разрешается приведение типа float к int. При этом точность не теряется, за исключением отбрасывания дробной части.
Но приведение типа double к int не разрешается, т.к. происходит потеря точность, что мы и наблюдаем в данной теме.
Вывод таков:
1. Можно проиводить переменные типа float к int без потери точности
2. Переменные типа double надо сначало привести к типу float, а уже потом приводить к типу int.
Т.е. вот так НЕ правильно (происходит потеря точности):
double d=(0.7+0.1)*10;
int a=d;
А вот так правильно (потери точности нет):
double d=(0.7+0.1)*10;
int a=(float) d;
Вот в чем заключается вся эта проблема. И то, что данный топик так сильно раздулся из-за обсуждений, означает, что большинство пограммистов C++ (в том числе и я) имеют не достаточно знаний в этой области. Надеюсь, теперь мы не будем совершать такие ошибки.
Вчера покапался в книгах и нашел ответ на данный вопрос...
В книге написано что по стандартам C/C++ разрешается приведение типа float к int. При этом точность не теряется, за исключением отбрасывания дробной части.
Но приведение типа double к int не разрешается, т.к. происходит потеря точность, что мы и наблюдаем в данной теме.
Выбросьте эту книгу. Подумайте сами, как может не быть потери точности при преобразовании 0.555 (не важно float или double) к int?
Вообще нет преобразования float к int. При преобразовании неявно вызывается ф-ция __ftol2, которая в качестве параметра принимает double, а возвращает long, отбрасывая дробную часть, а не округляя по правилам математики. Так вот, при преобразовании float оно сначала конвертируется в double, затем передается в __ftol2.
1. Можно проиводить переменные типа float к int без потери точности
Приведите 0.5 к int без потери точности.
Ага, а затем компилятор все сделает по-своему.
double d=(0.7+0.1)*10;
int a=d;
А вот так правильно (потери точности нет):
double d=(0.7+0.1)*10;
int a=(float) d;
Про потерю точности см. выше.
Топик разросся из-за того, что вы не хотите слышать того, о чем вам говорят.
Еще раз, почему в нижеследующем случае вы получаете свою 8
int a=(float) d;
double d - немного меньше, чем 8.0. Таково представление числа в формате double, ближайшее к 8 число, которое можно представить в этом формате меньше 8.
При преобразовании к float получается число, немного большее 8, опять же потому, что по счстливой случайности ближайшее к целому число, которое можно представить в формате float отклонено в "плюс".
Далее происходит преобразование к int. Вызывается ф-ция __ftol2. Ее аргумент - double, т.е. происходит преобразование от float к double. Но у double точность выше, поэтому число остается больше 8. Отбрасываем дробную чать - получаем (int)8.
Теперь уж совсем на пальцах (для примера - псевдокод):
float temp=(float)d; //temp=8.0000001
double arg=temp; //arg=8.000000100001
int i = __flol2(arg); //i=8 - отбросили дробную часть
И еще о потере точности. Неужели так сложно провести эксперимент?
double d = 1.0;
int i;
i = f;
i = d;
В ответ на оба преобразования вам даже компилятор скажет
warning C4244: 'initializing' : conversion from 'float' to 'int', possible loss of data
warning C4244: 'initializing' : conversion from 'double' to 'int', possible loss of data
Я ставил перед собой цель из числа с плавающей точкой выделить его целую часть БЕЗ потери точности.
Как мы видим, float -> int выделяет целую часть нормально, а вот при конверсии douиle -> int целая часть числа бывает меняется, что недопустимо для любой серьезной программы.
Как мы видим, float -> int выделяет целую часть нормально, а вот при конверсии douиle -> int целая часть числа бывает меняется, что недопустимо для любой серьезной программы.
Оба преобразования выполняются абсолютно одинаково: ОТБРАСЫВАНИЕМ ДРОБНОЙ ЧАСТИ
У тебя были разные значения float и double, что естественно, и поэтому получил разные значения int
Что тоже естественно. Работает все так, как написано в MSDN. А искусственные преобразования
double во float и т.п. есть подгонка результата под желаемый, "что недопустимо для любой серьезной программы".
Единственное, что меня удивляет: почему-то большинство форумчан знаю о проблеме потери точности при вычислениях и принимают эти потери как нормальное явление. Я понимаю, что у компьютеров числа с плавающими точками хранятся в особом формате и т.д.
Но разве это нормально, если ваша программа при вычислении целой части от (0.7+0.1)*10 выведет пользователю в результате 7, а не 8?! Ведь любой школьник засмеет такую "правильную программу"?!
Я пытался найти решение этой дурацкой проблемы и нашел его в книге Герберта Шилдта "Полный справочник по C++" на стр. 58 (если кто не верит). Там черным по белому написано, что преобразование float -> int идет нормально, а вот double -> int часто приводит к потере точности в целой части (как и произошло в примере выше). Так что надо делать приведение double так: double -> float -> int. И тогда проблем таких не будет.
Если вы думаете, что автор заблуждается, то приведите хоть один пример, когда бы преобразование float -> int или double -> float -> int привело бы к неправельному выделению целой части из вещественного числа.
Как мы видим, float -> int выделяет целую часть нормально, а вот при конверсии douиle -> int целая часть числа бывает меняется, что недопустимо для любой серьезной программы.
Это называется "коэффициент подгона". Который имеет свойство в самый неподходящий момент сработать не так как надо.
А те, кто использует double, видимо пишут несерьезные программы.
Ладно, давайте не будем сориться. Я ОЧЕНЬ благодарен всем, кто откликнулся на мое сообщение. Спасибо!
Единственное, что меня удивляет: почему-то большинство форумчан знаю о проблеме потери точности при вычислениях и принимают эти потери как нормальное явление.
Как бы там ни было, молодец, подметил. Респект. Я бы, наверное, тоже на этой ерунде когда-нибудь подзавис. Теперь буду знать...
Ладно, давайте не будем сориться. Я ОЧЕНЬ благодарен всем, кто откликнулся на мое сообщение. Спасибо!
Единственное, что меня удивляет: почему-то большинство форумчан знаю о проблеме потери точности при вычислениях и принимают эти потери как нормальное явление. Я понимаю, что у компьютеров числа с плавающими точками хранятся в особом формате и т.д.
Но разве это нормально, если ваша программа при вычислении целой части от (0.7+0.1)*10 выведет пользователю в результате 7, а не 8?! Ведь любой школьник засмеет такую "правильную программу"?!
Да никто и не ссорится.
Чтобы программа правильно выводила рез-т, надо принимать соотв. меры. А именно, либо округлять по правилам математики (EPS = 0.5), либо учитывать точность представления. Например, если интересует точность до целых, используйте, например, EPS = 0.01. Тогда
static_cast<int>(d + 2.0 * EPS);
Да никто и не ссорится.
Чтобы программа правильно выводила рез-т, надо принимать соотв. меры. А именно, либо округлять по правилам математики (EPS = 0.5), либо учитывать точность представления. Например, если интересует точность до целых, используйте, например, EPS = 0.01. Тогда
static_cast<int>(d + 2.0 * EPS);
Это правильно, при работе с не целыми числами, надо устанавливать определенную точность вычисления, необхадимую для получения достаточно точного результата.