Форум программистов, компьютерный форум, киберфорум
Наши страницы
C для начинающих
Войти
Регистрация
Восстановить пароль
 
 
Рейтинг 4.82/11: Рейтинг темы: голосов - 11, средняя оценка - 4.82
HitGirl
7 / 7 / 1
Регистрация: 08.10.2015
Сообщений: 264
1

Неправильное преобразование float к int

09.07.2017, 13:56. Просмотров 1960. Ответов 28
Метки нет (Все метки)

Здравствуйте!
Я ввожу число 1.17, умножаю его на 100.0 и присваиваю это переменной типа int.
Но туда записывается не 117, а 116!
Подскажите, пожалуйста, где у меня ошибка?
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <stdio.h>
 
#define MAXVALUE 2147836.47
 
float vvodd(float min,float max);
 
int main()
{
    int i;
float sum;
sum=vvodd(0.0,MAXVALUE);
   printf("%.2f\n",sum);
   i=sum;
   printf("%d\n",i);
 sum*=100.0;
    printf("%.2f\n",sum);
   i=sum;
   printf("%d\n",i);
   return 0;
}
 
float vvodd(float min,float max)
{
    float v;
    fflush(stdin);
    while(scanf("%f", &v)!=1||v<min||v>max)
    {
    fflush(stdin);
    if (v<min||v>max)
        printf("Допустимый диапазон значений от %.3f до %.3f\nПовторите ввод\n",min,max);
    else
        printf("Некорректно введено число\nПовторите ввод\n");
    }
    return v;
}
0
Лучшие ответы (1)
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
09.07.2017, 13:56
Ответы с готовыми решениями:

Преобразование float to int
Подскажите пожалуйста. Преобразование float c = 3.1415; int b = (int)c; VS10 express(настроена под...

Программа подстановки числа в int, short int и float +1
Поступил называется на IT-шную специальность. Основы Си ставят в тупик. Спасибо заранее за ваше...

Неправильное преобразование типа void to char
Стоит задача... написать прогу , которая считает количество введенных символов и выводит...

Умножение float na int
Добрый вечер, форумчане. Нужно найти площадь круга. Вводим с клавиатуры диаметр (целое число от 1...

Unsigned int во float
Всем привет! Я собираю устройство на контроллере, которое получает двухбайтовый код и мне нужно...

28
easybudda
Модератор
Эксперт JavaЭксперт CЭксперт С++
10297 / 6179 / 1555
Регистрация: 25.07.2009
Сообщений: 11,762
09.07.2017, 14:06 2
Цитата Сообщение от HitGirl Посмотреть сообщение
Но туда записывается не 117, а 116!
А Вы float на double поменяйте и ещё раз попробуйте.
А если интересно, почему оно так происходит, почитайте, как вещественные числа в памяти компьютера хранятся. Тип float допускает довольно большую погрешность. В принципе им лучше вообще не пользоваться.
1
HitGirl
7 / 7 / 1
Регистрация: 08.10.2015
Сообщений: 264
09.07.2017, 14:23  [ТС] 3
Цитата Сообщение от easybudda Посмотреть сообщение
А Вы float на double поменяйте и ещё раз попробуйте.
Поменял.
Но если писать так:
C
1
i=vvodd(0.0,MAXVALUE)*100.0;
то в i всё равно 116
0
CoderHuligan
814 / 545 / 201
Регистрация: 30.06.2015
Сообщений: 3,020
Записей в блоге: 14
09.07.2017, 14:28 4
Цитата Сообщение от HitGirl Посмотреть сообщение
Я ввожу число 1.17, умножаю его на 100.0 и присваиваю это переменной типа int.
А почему int, а не double или float?
0
09.07.2017, 14:28
HitGirl
7 / 7 / 1
Регистрация: 08.10.2015
Сообщений: 264
09.07.2017, 14:46  [ТС] 5
Цитата Сообщение от CoderHuligan Посмотреть сообщение
А почему int, а не double или float?
Преобразование долларов в центы.
Нужно чтобы пользователь ввёл число в котором максимум 2 знака после запятой.
int "обрежет" лишние знаки после запятой.
0
easybudda
Модератор
Эксперт JavaЭксперт CЭксперт С++
10297 / 6179 / 1555
Регистрация: 25.07.2009
Сообщений: 11,762
09.07.2017, 16:52 6
C
1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
 
int main(void) {
    float fvar = 1.17;
    double dvar = 1.17;
    
    printf("%d\n%d\n", (int)(fvar * 100.0), (int)(dvar * 100.0));
    
    return 0;
}
Код
$ gcc float_vs_double.c 
$ ./a.out 
116
117

Не по теме:

- Доктор, когда я делаю вот так, у меня вот тут болеть начинает...
- А ты вот так не делай!

1
Evg
Эксперт CАвтор FAQ
19884 / 7514 / 574
Регистрация: 30.03.2009
Сообщений: 20,969
Записей в блоге: 30
09.07.2017, 17:25 7
Лучший ответ Сообщение было отмечено HitGirl как решение

Решение

Потому что число 1.17 представлено в машине не точно: http://codepad.org/QLggj8sy

При этом 1.17 в принципе не представляется точно в двоичной системе счисления, а потому проблема будет всегда: хоть во float'е, хоть в double'е, хоть в long double'е. Т.е. советы из постов #2 и #6 - это в общем-то полная лажа, которая сработает для значения 1.17, но не сработает для другого значения

Конкретно исходная проблема решается путём построения правильной методики округления (например, перед округлением прибавить к числу что-нибудь типа 0.01). Но конкретный метод решения, как мне кажется, зависит от того, а чего конкретно тут вычисляется. И тут я в общем-то не знаю, как правильно делать в общем случае

Добавлено через 4 минуты
Цитата Сообщение от HitGirl Посмотреть сообщение
Преобразование долларов в центы
Тогда тут действительно сойдёт вариант "i = (sum + 0.01f)". Вроде бы в диапазоне реалистических значений должно работать правильно

Хотя, судя по всему, более правильно использовать функции типа rint: https://linux.die.net/man/3/rint. Только надо повтыкать, как это сделать правильно

Добавлено через 2 минуты
Или даже round: https://linux.die.net/man/3/round
4
CoderHuligan
814 / 545 / 201
Регистрация: 30.06.2015
Сообщений: 3,020
Записей в блоге: 14
09.07.2017, 17:36 8
Цитата Сообщение от Evg Посмотреть сообщение
И тут я в общем-то не знаю, как правильно делать в общем случае
Использовать целочисленную арифметику, которая эмулирует числа с плавающей точкой. Других вариантов не вижу.
1
Evg
Эксперт CАвтор FAQ
19884 / 7514 / 574
Регистрация: 30.03.2009
Сообщений: 20,969
Записей в блоге: 30
09.07.2017, 17:40 9
CoderHuligan, я только потом прочитал, что исходная постановка задачи - перевод долларов в центы. Т.е. нужно просто использовать правильную функцию округления (вроде бы это round, просто в этой сфере я плохо разбираюсь). В диапазоне значений, для которых имеет смысл такая постановка задачи, чего-то бОльшего и не требуется
1
easybudda
Модератор
Эксперт JavaЭксперт CЭксперт С++
10297 / 6179 / 1555
Регистрация: 25.07.2009
Сообщений: 11,762
09.07.2017, 18:22 10
Цитата Сообщение от Evg Посмотреть сообщение
Т.е. советы из постов #2 и #6 - это в общем-то полная лажа, которая сработает для значения 1.17, но не сработает для другого значения
Полная лажа - это на ровном месте раздуть проблему, решение которой и сам придумать не сможешь. double - минимально допустимый тип данных для хранения денежных величин, им и сто'ит пользоваться (особенно для решения школьных задач).
0
Evg
Эксперт CАвтор FAQ
19884 / 7514 / 574
Регистрация: 30.03.2009
Сообщений: 20,969
Записей в блоге: 30
09.07.2017, 18:44 11
1. Ко второму посту, в котором ты пообещал, что с double проблем не будет, про денежные величины ещё ничего не говорилось. Не говоря уж о том, что между float, double и long double нет принципиальной разницы. Если проблеме с неточным представлением суждено возникнуть, то она гарантированно возникнет, вопрос только в том, при каких значениях

2. Твой пример из поста #6 - это ты форменно ткнул пальцем в небо. Он у тебя "работает" только потому, что gcc сделал свёртку констант. Сделай переменные глобальными (т.е. устрани свёртку констант, которой в исходной задаче нету) и вуаля - твой совет перестал работать: http://codepad.org/KQbYElQN

3. Лажа - это когда предлагают решение, которое работает здесь и сейчас для конкретных входных значений (к тому же, как оказалось, ещё и не работает), которое базируется не понятно на каких догадках, и которое держится непонятно на каких соплях

Ну и просто как общее наблюдение. На форуме неоднократно видел одни и те же советы. Типа замени float на double или long double и будет тебе щястье. Такие советы даются только в тех случаях, когда афтор совета сам толком не понимает, как устроена вещественная арифметика. Я уже давно скрипя зубами прохожу мимо, ибо надоело по сто раз объяснять одно и то же, при этом зачастую в пустоту. Но когда такой совет даёт человек, которого я не побоюсь назвать экспертом, я уже просто не выдержал

Я прекрасно понимаю, что всё на свете знать нельзя. Тем более, что я и сам неоднократно садился в лужу, будучи изначально на 100% уверенным в своей правоте. Слава яйцам находились люди, которые указывали мне на мои ошибки и спасибо им большое за это. Вот и здесь мне хочется в первую очередь исправить конкретно твои ошибки (пробелы в понимании), чем разводить очередной спор на пустом месте
0
HitGirl
7 / 7 / 1
Регистрация: 08.10.2015
Сообщений: 264
09.07.2017, 20:31  [ТС] 12
Спасибо всем за ответы!
0
easybudda
Модератор
Эксперт JavaЭксперт CЭксперт С++
10297 / 6179 / 1555
Регистрация: 25.07.2009
Сообщений: 11,762
09.07.2017, 20:44 13
Цитата Сообщение от Evg Посмотреть сообщение
Сделай переменные глобальными (т.е. устрани свёртку констант, которой в исходной задаче нету) и вуаля - твой совет перестал работать:
Что-то не получилось
Неправильное преобразование float к int

Может ещё какой кульбит исполнить?

Цитата Сообщение от Evg Посмотреть сообщение
базируется не понятно на каких догадках, и которое держится непонятно на каких соплях
Ну догадка-то простая: тип double даёт более точное представление вещественных чисел (ну или менее неточное, если принципиально). И да, это решает проблему (если речь не о финансовом состоянии Сороса в монгольских тугриках).

Цитата Сообщение от Evg Посмотреть сообщение
Я прекрасно понимаю, что всё на свете знать нельзя. Тем более, что я и сам неоднократно садился в лужу, будучи изначально на 100% уверенным в своей правоте.
Ну да, бывает и такое, но в этом случае вся проблема в неточном представлении вещественных чисел - нет? И да, я принципиально против использования типа float, если только для этого нет действительно веских причин. Но тем не менее, действительно всё знать нельзя. И как пример, вообще не представляю, что происходит в кодепаде и как оптимизации компилятора на точность представления вещественных чисел влияют, разве-что gcc в обоих случаях константу типа double подставил, но тогда вопрос - почему мне это воспроизвести не удалось? Да мало того!
Неправильное преобразование float к int


Ну и просто на всякий случай: да, я знаю, что для некоторых задач не хватит точности ни double, ни long double. И представление денежных сумм целыми числами проблему уж точно не решит ( два рубля на троих - это по сколько на каждого?). Дойдя до фанатизма, можно библиотеку натуральных дробей себе написать (ну или взять где-нибудь) и работать с ними, благо, в денежных расчётах извлечение корня - редкость. Но как-то для такой задачи через чур...
0
Evg
Эксперт CАвтор FAQ
19884 / 7514 / 574
Регистрация: 30.03.2009
Сообщений: 20,969
Записей в блоге: 30
09.07.2017, 21:23 14
Цитата Сообщение от easybudda Посмотреть сообщение
Может ещё какой кульбит исполнить?
С учётом того, что после scanf'а (второй скриншот) ничего не поменялось, надо крепко думать, как такое вообще могло получиться. В моём понимании оно противоречит здравому смыслу - http://codepad.org/mWr25pGm. Я знаю, что на интеле нестандартная работа с плавающей арифметикой, но она не должно приводить к такому результату. Может есть какие-то нестандартные настройки режима округления, но здесь его вроде бы как и не должно быть. Для порядку можно посмотреть ассемблерный код (правда я интеловскую систему команд плохо знаю). Ещё можно попробовать включить оптимизации, чисто посмотреть, как-то влияет или нет. Ну и ещё и версию gcc огласи

Цитата Сообщение от easybudda Посмотреть сообщение
Ну догадка-то простая: тип double даёт более точное представление вещественных чисел
Когда речь идёт о представлении конкретно числа 1.17, то разницы между float'ом и double'ом не будет в пределах требуемой нам погрешности

У float'а мантисса 23 бита (вроде), этого хватает, чтобы представлять довольно большие по значению числа, для которых тем не менее требуется две десятичные цифры после запятой. Методом тыка посмотрел получается что-то порядка ста тыщ. При таких суммах ещё сохраняется возможность хранить их с точностью до копейки, в то время как это на практике уже и не нужно. Понятно, что когда речь идёт о банковском счёте, то там копейки всегда важны, но, думается, там используются другие нанотехнологии

Цитата Сообщение от easybudda Посмотреть сообщение
Ну да, бывает и такое, но в этом случае вся проблема в неточном представлении вещественных чисел - нет?
Да. При этом есть важный момент, о котором я уже написал в посте #11. Если эта проблема возникнет, то она возникнет для любого типа (за исключением отдельно взятых "хороших" значений). В этом смысле нет принципиальной разницы между float, double или long double. Разница только в том, при каких значениях эта проблема будет возникать, а это уже зависит от конкретной задачи

Цитата Сообщение от easybudda Посмотреть сообщение
И да, я принципиально против использования типа float, если только для этого нет действительно веских причин
Тут я уже ничего не могу сказать по существу. Я на практике вещественными вычислениями занимаюсь редко. На уровне ощущений я не могу сказать, для какого класса задач какого типа будет достаточно
0
easybudda
Модератор
Эксперт JavaЭксперт CЭксперт С++
10297 / 6179 / 1555
Регистрация: 25.07.2009
Сообщений: 11,762
09.07.2017, 22:01 15
Цитата Сообщение от Evg Посмотреть сообщение
Ну и ещё и версию gcc огласи
Код
$ gcc -dumpversion
4.9.2
$ clang -dumpversion
4.2.1
Ну и в свете
Цитата Сообщение от Evg Посмотреть сообщение
Я знаю, что на интеле нестандартная работа с плавающей арифметикой, но она не должно приводить к такому результату.
Код
$ cat /proc/cpuinfo 
processor	: 0
cpu		: 7455, altivec supported
clock		: 1249.999995MHz
revision	: 3.3 (pvr 8001 0303)
bogomips	: 83.31

processor	: 1
cpu		: 7455, altivec supported
clock		: 1249.999995MHz
revision	: 3.3 (pvr 8001 0303)
bogomips	: 83.31

total bogomips	: 166.63
timebase	: 41658765
platform	: PowerMac
model		: PowerMac3,6
machine		: PowerMac3,6
motherboard	: PowerMac3,6 MacRISC3 Power Macintosh
detected as	: 129 (PowerMac G4 Windtunnel)
pmac flags	: 00000010
L2 cache	: 256K unified
pmac-generation	: NewWorld
Memory		: 2048 MB
Но сути это не меняет, в винде на интелёвом камне и с мелкомягким компилятором та же песня
Неправильное преобразование float к int

Цитата Сообщение от Evg Посмотреть сообщение
Когда речь идёт о представлении конкретно числа 1.17, то разницы между float'ом и double'ом не будет в пределах требуемой нам погрешности
В теории, но результат на лицо.
Цитата Сообщение от Evg Посмотреть сообщение
Я на практике вещественными вычислениями занимаюсь редко. На уровне ощущений я не могу сказать, для какого класса задач какого типа будет достаточно
А я вот как-то раз нарвался (давно довольно), с тех пор и зарёкся таким макаром память экономить...
0
VTsaregorodtsev
591 / 546 / 79
Регистрация: 19.02.2010
Сообщений: 1,981
09.07.2017, 22:26 16
Цитата Сообщение от Evg Посмотреть сообщение
Понятно, что когда речь идёт о банковском счёте, то там копейки всегда важны, но, думается, там используются другие нанотехнологии
Для финансов давно уже юзают BCD-код, команды для работы с которым у интеловских процессоров имеются начиная с 8086.
0
TheCalligrapher
С чаем беда...
Эксперт CЭксперт С++
4854 / 2497 / 697
Регистрация: 18.10.2014
Сообщений: 4,333
10.07.2017, 02:56 17
Рассуждать о "точности" в контексте данного вопроса - бесполезно. Плавающая арифметика обычно округляет непредставимое значение до ближайшего представимого. Если ближайшее представимое оказалось меньше 1.17, исходное выражение даст 116. Если ближайшее представимое оказалось больше 1.17, исходное выражение даст 117.

Сколько не наращивай точность плавающего типа, построенного на принципах IEEE 754, число 1.17 в нем точно представляться никогда не будет. А с какой стороны от 1.17 окажется ближайшее представимое значение - неизвестно. Оно будет непрерывно прыгать туда-сюда по мере наращивания точности. И получаться в результате будет то 116 то 117.

Хотите получить целое - округляйте: i = (int) (sum * 100 + .5)
4
easybudda
Модератор
Эксперт JavaЭксперт CЭксперт С++
10297 / 6179 / 1555
Регистрация: 25.07.2009
Сообщений: 11,762
10.07.2017, 10:37 18
Да, погорячился, бывают косяки и при хранении в double (наглядно убедился)...

Добавлено через 3 минуты
Но всё-таки интересно, по какой логике число 1.16999995708465576172 умноженное на 100 и без дробной части до 116 округляется, а 1.16999999999999992895 до 117
0
TheCalligrapher
С чаем беда...
Эксперт CЭксперт С++
4854 / 2497 / 697
Регистрация: 18.10.2014
Сообщений: 4,333
10.07.2017, 10:47 19
Цитата Сообщение от easybudda Посмотреть сообщение
Но всё-таки интересно, по какой логике число 1.16999995708465576172 умноженное на 100 и без дробной части до 116 округляется, а 1.16999999999999992895 до 117
Я подозреваю, что дробные части этих чисел после умножения на 100 снова подвергаются перетурбации, ибо в IEEE 754 после умножения на 100 они точно не представимы. 100 - это ведь не степень двойки и не сумма степеней двойки.

После умножения 1.16999995708465576172 на 100 ближайшим представимым числом оказалось все равно какое-то 116.xxxxxxx. А после умножения 1.16999999999999992895 на 100 ближайшим представимым числом оказалось таки 117.0.
4
easybudda
Модератор
Эксперт JavaЭксперт CЭксперт С++
10297 / 6179 / 1555
Регистрация: 25.07.2009
Сообщений: 11,762
10.07.2017, 11:02 20
TheCalligrapher, ага, так и вышло
C
1
2
3
4
5
6
7
#include <stdio.h>
 
int main(void) {
    printf("%f\n%f\n", 1.16999995708465576172 * 100.0, 1.16999999999999992895 * 100.0);
    
    return 0;
}
Код
$ ./a.out 
116.999996
117.000000
2
10.07.2017, 11:02
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
10.07.2017, 11:02

Перевод float в int. Неверный результат
#include &lt;stdio.h&gt; #include &lt;conio.h&gt; #include &lt;string.h&gt; int main() { float D; int Y;...

Неточность при умножении int and float
float x = 4.2; int y = 100; float z = x*y; printf(&quot;%f\n,&quot;, z); Выводит 419.999969 какова...

Преобразование int в char
Всем привет! Мне нужно преобразовать число в строку. Использую sprintf() всё работает, НО...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2019, vBulletin Solutions, Inc.
Рейтинг@Mail.ru