1

I don't think this violates the honor code but if so please let me know and I'll remove it promptly. My question is to check that the input is in monetary form. For example, I'm assuming something like 5.01 is valid input but 5.001 isn't. And yes, I've already read How do I ensure that the user input in greedy is given to two decimal places? Even if my final implementation is different it is bugging me to no end not being able to express my ideas in code correctly so as a learning exercise at least, I would really appreciate some clarity to help me understand what's going wrong here. Here is some code:

do
{
    dollaramt = GetFloat();
}
while ((dollaramt < 0) || (fmodf(dollaramt, 0.01)) != 0.0);

The while loop here continues to execute regardless of the input. I would assume it would work like this for input 5.01 When the while loop executes it checks whether dollaramt (5.01) is less than 0. This evaluates to false. Then we move on to evaluate fmodf(5.01, .01). This should evaluate to 0.0. Then the program should check whether 0.0 is not equal to 0.0. This should evaluate to false. Because both expressions in my "or" statement evaluate to false I would expect the loop to exit. However, it does not, but instead keeps repeating indefinitely. Even if this isn't the correct approach to solve the problem I feel like I could really benefit by understanding whats wrong with my code here. I just want to divide by .01 and if there is anything left over I'll know it's not a dollar formatted input and simply re-prompt the user. Why isn't my code working? I'm tearing my hair out here!!!

3

You are missing the whole forest for the tree. You didn't learn what this exact lesson was written to teach you, that the floats in your computer are stored with imprecision, and there is nothing you can do about that. Take a look at the following program:

float_test.c

#include "stdio.h"
#include "cs50.h"
#include "math.h"

int main(void)
{
    float number;
    number = GetFloat();

    printf("%.25f\n", number);

    printf("%.25f\n", 0.01);

    printf("%.25f\n", fmodf(number, 0.01));

    printf("%d\n", fmodf(number, 0.01) != 0);

}

And now take a look at its output, for input 0.51. (any number would be the same)

night@hawk:~$ ./float_test
0.51
0.5099999904632568359375000
0.0100000000000000002081668
0.0000000018626451492309570
1

See that the 0.51 I gave, is not stored as 0.51, but as 0.5099999904632568359375000 if I print the first 25 decimal digits. And 0.01 is not exactly 0.01 but 0.0.0100000000000000002081668. And the remainder of the float division, is not 0 (since 0.51 / 0.01 == 51, they should divide perfectly, right?) but it's 0.0000000018626451492309570, so it's in fact different than 0. So your program executes exactly as it should, but because the logic behind it is wrong, you don't get the results you expected.

But I will not leave you like this. Here is a possible implementation of what you want to do. (Since it's not requested by the pset, I don't think it violates the honor code. It's just a piece of code showing a functionality.)

floatdec.h

/****************************************************************************
 * CS50 Library 5
 * https://manual.cs50.net/library/
 *
 * Based on Eric Roberts' genlib.c and simpio.c.
 *
 * Copyright (c) 2013,
 * Glenn Holloway <holloway@eecs.harvard.edu>
 * David J. Malan <malan@harvard.edu>
 * All rights reserved.
 *
 * BSD 3-Clause License
 * http://www.opensource.org/licenses/BSD-3-Clause
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * * Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 * * Neither the name of CS50 nor the names of its contributors may be used
 *   to endorse or promote products derived from this software without
 *   specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ***************************************************************************/
#ifndef FLOATDEC_H_INCLUDED
#define FLOATDEC_H_INCLUDED
#include <stdio.h>
#include <cs50.h>
#include <ctype.h>
#include <string.h>

/*
 * Reads a line of text from standard input and returns the equivalent
 * float as precisely as possible; if text does not represent a float,
 * user is prompted to retry; if the represented float has more than
 * @max_decimals decimal places, the user is prompted to retry.  Leading and
 * trailing whitespace is ignored. For simplicity, overflow and underflow are
 * not detected.  If line can't be read, returns FLT_MAX.
 *
 * Modification made by ChrisG
 * christosg8 [at] gmail [dot] com
 *
 */
float GetFloatDec(int decimals);
#endif

floatdec.c

#include "floatdec.h"

float GetFloatDec(int max_decimals)
{
    // try to get a float from user
    while (true)
    {
        // get line of text, returning FLT_MAX on failure
        string line = GetString();
        if (line == NULL)
            return FLT_MAX;

        // return a float if only a float (possibly with leading and/or trailing
        // whitespace) was provided
        char c; float f;
        if (sscanf(line, " %f %c", &f, &c) == 1)
        {
            // if the text represents a float (possibly with leading and/or
            // trailing whitespace), check if there is a decimal point. If there
            // is check if there are more than @max_decimal decimals, else
            // return the int given as float
            char* dec_point = strrchr(line, '.');
            if (dec_point != NULL)
            {
                // count decimal digits
                int decimals = 0;
                for (int i = 0; isdigit(dec_point[i + 1]); i++)
                {
                    decimals++;
                }

                // if more than @max_decimal decimals were read, prompt for
                // retry, else return the float
                if (decimals > max_decimals)
                {
                    free(line);
                    printf("Retry: ");
                }
                else
                {
                    free(line);
                    return f;
                }
            }
            else
            {
                free(line);
                return f;
            }

        }
        else
        {
            free(line);
            printf("Retry: ");
        }
    }
}

And here is the above program again:

float_test.c

#include <stdio.h>
#include <cs50.h>
#include <math.h>
#include "floatdec.h"

int main(void)
{
    float number;
    number = GetFloatDec(2);

    printf("%.25f\n", number);

    printf("%.25f\n", 0.01);

    printf("%.25f\n", fmodf(number, 0.01));

    printf("%d\n", fmodf(number, 0.01) != 0);
}

with output this time:

night@hawk:~$ ./float_test
0.512
Retry: 0.5123
Retry: 0.51
0.5099999904632568359375000
0.0100000000000000002081668
0.0000000018626451492309570
1

See that this function doesn't stop the float imprecision (that's impossible) but as you asked, it will make the user enter a float with at most max_decimals decimals.

Just make sure to have the floatdec.c and floatdec.h in the same folder as your greedy.c and to include floatdec.c in your clang command.


If this answers your question please accept it by clicking the gray check-mark to the left, so that it becomes green. You can also vote it up by pressing the up arrow above the check-mark. And don't forget to keep coding!

2
  • 1
    Thanks a lot ChrisG you're the man! This helped a ton. I very much appreciate you clarifying the point of the lesson and now I understand why the imprecision of floating point numbers was throwing a monkey wrench in my logic. Now I can FINALLY move forward thanks again, so very much!!
    – Alex Winn
    Sep 5 '15 at 21:21
  • Glad I could help! Keep working! And good job trying things out and having questions. It's the best way to learn!
    – ChrisG
    Sep 5 '15 at 22:12

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .