You are missing the whole forest for the tree. You didn't learn what this exact lesson was written to teach you, that the float
s 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!