C - Проблемы с округлением (CS50)

Я гуглил это уже несколько дней, и я потерялся. Так что делаю CS50 онлайн и, кажется, не могу справиться с округлением чисел. Моя программа испортила умножение чисел с плавающей запятой, например 2.10, на целые числа, например 100, она выдала бы 209.xxxxxxxx

Как я уже сказал, я прочитал бесчисленное количество сообщений о том, что мне следует использовать ceilf и включить , но я получаю сообщение об ошибке

greedy.c :(. text + 0x74): undefined ссылка на `ceilf 'collect2: error: ld вернул 1 статус выхода make: *** [greedy] Ошибка 1 adam @ beethoven: ~ / projects / atom / edx / pSet1 / жадный $

Я видел сообщения о -lm и определенном файле, но, честно говоря, я не понимаю, что это значит.

Я никоим образом не ищу прямого решения, я просто ищу рекомендации по улучшению.

Вот мой код, возможно, не такой оптимизированный, как хотелось бы некоторым, но здесь я возвращаюсь к основам;)

#include <stdio.h>
#include <math.h>

int main() {
  // Initialize Variables
  int coinsTotal = 0,
      quarter = 25,
      dime = 10,
      nickel = 5,
      penny = 1,
      cents;
  float changeDue;

  do {
    printf("How much change are you owed? (Format = 0.00)($): ");
    scanf("%f", &changeDue );
    // Convert to cents
    cents = changeDue * 100;
  } while(cents <= 0);

  while (cents >= quarter) {
    cents = cents - quarter;
    coinsTotal = coinsTotal + 1;
  } if (cents == 0) {
      printf("The miminum number of coins is: %d\n", coinsTotal);
  } else {
      while (cents >= dime) {
        cents - dime;
        coinsTotal = coinsTotal + 1;
      } if (cents == 0) {
          printf("The minimum number of coins is: %d\n", coinsTotal);
      } else {
          while (cents >= nickel) {
            cents = cents - nickel;
            coinsTotal = coinsTotal + 1;
          } if (cents == 0) {
              printf("The minimum number of coins is: %d\n", coinsTotal);
          } else {
              while (cents >= penny) {
                cents = cents - penny;
                coinsTotal = coinsTotal + 1;
              } if (cents == 0) {
                  printf("The minimum number of coins is: %d\n", coinsTotal);
                }
        }
      }
    }
}

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

Обновление Пытался скомпилировать GCC с помощью -lm, но все равно не удалось. adam @ beethoven: ~ / projects / atom / edx / pSet1 / greedy $ gcc -o foo -lm greedy.c /tmp/cc3qHAK7.o: в функции main': greedy.c:(.text+0x6e): undefined reference toceilf 'collect2: error: ld вернул 1 статус выхода adam @ beethoven: ~ / проекты / атом / edx / pSet1 / жадный $

РЕШЕНИЕ Вместо использования команды make я использовал gcc и добавил флаг -lm В конце команды gcc -o foo greedy.c -lm


person Adam 'Sacki' Sackfield    schedule 02.09.2015    source источник
comment
Вы знаете, что такое MCVE?   -  person Amit    schedule 03.09.2015
comment
Привет, Амит, я не знал, что это. Я погуглил и нашел страницу на этом сайте. Я ценю, что вы указали на это, и я буду редактировать свой пост соответствующим образом. А   -  person Adam 'Sacki' Sackfield    schedule 03.09.2015
comment
Хорошо, ты примерный гражданин :-)   -  person Amit    schedule 03.09.2015
comment
почему бы просто не проанализировать входную строку вручную, чтобы вам не пришлось использовать какие-либо математические функции, чтобы сделать что-то столь же простое, как чтение строки, такой как 2.10, и преобразование ее в число 210?   -  person mwag    schedule 03.09.2015
comment
Рекомендации: 1) Если код должен использовать деньги с плавающей запятой, используйте double. 2) при преобразовании денег с плавающей запятой в int используйте round(), как в cents = round(changeDue * 100);   -  person chux - Reinstate Monica    schedule 03.09.2015
comment
@mwag - это не цель программы, ей нужно сначала получить его как int, чтобы проделать еще какую-то работу.   -  person Adam 'Sacki' Sackfield    schedule 03.09.2015
comment
@chux - это еще одна функция, страдающая от того же несчастья, что и ceilf.   -  person Adam 'Sacki' Sackfield    schedule 03.09.2015
comment
@ Адам Не согласен. Какой пример несчастья вызван round() использованием?   -  person chux - Reinstate Monica    schedule 03.09.2015
comment
@chux - В основном я делаю этот pset - cdn. cs50.net/2015/x/psets/1/pset1/pset1.html#time_for_change. Теперь, когда ввод, скажем, «1.00» умножается на «100», я получаю «100» как ответ, правильный, я знаю. Скажем, я ввожу «2.10» и делаю то же самое. Я получаю «209.xxxxxxx», а не фактическое «210». Вот почему я выбрал «MATH.H», и в этом проблема. Я сузил его, как покажет ответ 1.   -  person Adam 'Sacki' Sackfield    schedule 03.09.2015
comment
Ваша ссылка говорит И будьте осторожны, округляйте, а не обрезайте свои гроши !. Когда вы получаете «209.xxxxxxx», вы больше похожи на 209.9xxxxxx'. double round(double x) от <math.h> решает эту проблему, поскольку возвращает 210.0. Тем не менее, поддерживайте, что он не страдает той же бедой, что и ceilf, и ищу ваш пример того, как round(double) является проблемой. OTOH - я вижу, что round() в <math.h> имеет ту же проблему, что вам нужно связать математическую библиотеку. Согласитесь с этим. Мое руководство было для ваших следующих проблем, поскольку вы запрашивали только рекомендации по улучшению   -  person chux - Reinstate Monica    schedule 03.09.2015
comment
@ Adam'Sacki'Sackfield да, я понял - похоже, вы либо не прочитали, либо не поняли мой комментарий, что неявно означает, что вы использовали бы scanf () для сканирования STRING, а не float, и затем преобразуйте эту строку в int.   -  person mwag    schedule 03.09.2015


Ответы (2)


Я видел сообщения о -lm и определенном файле, но, честно говоря, я не понимаю, что это значит.

Вы должны подключиться к математической библиотеке, чтобы исправить ошибку. Реализации математических функций обычно помещаются в отдельную библиотеку, математическую библиотеку. Если вы используете gcc, добавьте -lm в команду компоновщика.

person ouah    schedule 02.09.2015
comment
Привет, Здесь я застрял. Думаю, «gcc» похож на «cc», который используется, когда я использую команду «make» для компиляции моего «c». Я буквально понятия не имею, что делает -lm и какой эквивалент был бы с командой make. Возможно, я смогу настроить GCC. - person Adam 'Sacki' Sackfield; 03.09.2015
comment
Добавление -lm говорит о том, что необходимо выполнить ссылку на библиотеку под названием libm. Если вы используете Makefile, найдите команду, используемую для генерации финального двоичного файла, и добавьте к нему -lm. - person ouah; 03.09.2015
comment
@ Adam'Sacki'Sackfield Поскольку вы используете make-файл, вы, вероятно, захотите добавить его в LDFLAGS в начале вашего make-файла или просто использовать make LDFLAGS=-lm greedy, предполагая, что ваша строка cc в make-файле содержит $(LDFLAGS) в конце строки. Если его нет в списке в строке cc, вы можете добавить $(LDFLAGS) в конец строки cc или просто жестко запрограммировать сам -lm в конце строки вместо $(LDFLAGS). Однако $(LDFLAGS) является более гибким, поскольку позволяет перечислить более одной библиотеки, например make LDFLAGS='-lmylib -lXt -lX11 -lm - person ; 03.09.2015
comment
Вы можете использовать следующий таинственный вызов: make myprog LDLIBS=-lm (замените myprog на то, что вы обычно делаете). - person rici; 03.09.2015
comment
@ChronoKitsune: LDLIBS, а не LDFLAGS. LDFLAGS слишком рано появляется в командной строке. - person rici; 03.09.2015
comment
@rici LDFLAGS более распространен - ​​многие скелетные make-файлы (и правила сборки по умолчанию) используют, например, $(CPPFLAGS), $(CFLAGS) и $(LDFLAGS). Я не думаю, что когда-либо видел, чтобы кто-то использовал LDLIBS. - person Alnitak; 03.09.2015
comment
Вау, спасибо, взгляды. Вскоре я попробую эти предложения и посмотрю, как все пойдет. Просто для справки я попробовал то, что сказал @ouah, и после некоторого поиска в Google пошел и скомпилировал его с помощью GCC, но получил ту же ошибку. Я обновлю свой пост с помощью команды, которую использовал. Я также вернусь, как только попробую все другие предложения. - person Adam 'Sacki' Sackfield; 03.09.2015
comment
@alnitak правила по умолчанию для gmake и bsdmake используют LDLIBS. Это очень стандартно. - person rici; 03.09.2015
comment
@ Adam'Sacki'Sackfield -lm должен быть добавлен в конце строки компиляции / компоновки, а не в середине, как вы это делали при редактировании. - person ouah; 03.09.2015
comment
@ouah - Боже, это круто, теперь он компилируется. По-прежнему неправильно, но мне нужно возиться с математическими функциями, которые теперь работают. Очень признателен. - person Adam 'Sacki' Sackfield; 03.09.2015
comment
@Alnitak: Ага, он был добавлен в sys.mk файл FreeBSD 19 лет и 10 месяцев назад (svnweb.freebsd.org/base/head/share/mk/), так что если бы вы не смотрели 20 лет, вы могли бы пропустить это. При всем уважении, это скорее обусловливает ваше заявление: я не думаю, что когда-либо видел такое. :) - person rici; 03.09.2015

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

#define round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5))

Кроме того, вы получаете сообщение об ошибке связывания, потому что вы не соединяетесь с математической библиотекой. Например, используйте:

gcc greedy.c -lm -o greedy
person Manos Nikolaidis    schedule 02.09.2015
comment
Я хочу округлить до двух знаков после запятой. - person Adam 'Sacki' Sackfield; 03.09.2015
comment
Примечание: round(double) из ‹math.h› работает лучше, чем это определение. У определения есть проблемы с такими случаями, как число перед 0,5 и целые числа, которые точно могут быть представлены как long, но не double, и некоторые другие рядом с ним. У него действительно есть то преимущество, что математическая библиотека не нужна, и OP имеет проблемы с линковкой. - person chux - Reinstate Monica; 03.09.2015