Challenge

[code]/***************************** Challenge ***************************/

/In metersToFeetAndInches(), you used floor () and subtraction to break rawFeet into its integer and fractional parts. Change metersToFeetAndInches() to use modf() instead./

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

void metersToFeetAndInches(double meters, unsigned int *ftPtr, double *inPtr)
{
// This function assumes meters is non-negative.

// Convert the number of meters into a floating-point number of feet
double rawFeet = meters * 3.281; // e.g. 2.4536

/***********How many complete feet as an unsigned int?****************/

// Double data type variables to break the number of feet into it's integer and modulus.
double integerPartFeet;
double fractionPartFeet;
// Passing the address of integerPartFeet as an argument
fractionPartFeet =  modf(rawFeet, &integerPartFeet);
// Reference http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1570.pdf page 264 of 701

// Find the integer part by subtracting the fraction from the total number of feet.
integerPartFeet = rawFeet - fractionPartFeet;

// Check for a non-null pointer
if (ftPtr) {
    // Store the number of feet at the supplied address
    printf("Storing %.2f ft to the address %p\n", integerPartFeet, ftPtr);
    *ftPtr = integerPartFeet;
}


// Calculate inches from the modulus

double inches = fractionPartFeet * 12.0;

// Check for a non-null pointer
if (inPtr) {
    // Store the number of inches at the supplied address
    printf("Storing %.2f in to the address %p\n", inches, inPtr);
    *inPtr = inches;
}

}
int main(int argc, const char * argv[])
{
// Arguments that will be needed int the function metersToFeetAndInches
double meters = 3.0;
unsigned int integerPartFeet;
double inches;

// Call the function meterToFeetAndInches
metersToFeetAndInches(meters, &integerPartFeet, &inches);
// Display the results of the conversion in a readable format
// %.1f will be a float data type with one decimal place and %u is for the unsign integer.
printf("%.1f meters is equal to %u feet and %.1f inches.\n", meters, integerPartFeet, inches);

return 0;

}[/code]

Here’s my take. Makes the code a bit smaller. Please criticize the hell out of it.

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

void metersToFeetAndInches(double meters, double *ftPtr, double *inPtr) {
    // This function assumes meters in non-negative
    
    // Convert the number of meters into a floating-point number of feet
    double rawFeet = meters * 3.281;
    
    // modf() returns inches to inPtr and assigns feet to where ftPtr points to
    if (ftPtr && inPtr) {
        *inPtr = modf(rawFeet, ftPtr);
        *inPtr *= 12.0; // since inches is now a fraction of a foot, need to convert into actual inches
    }
}

int main(int argc, const char * argv[]) {
    double meters = 3.0;
    double feet; // changed this from book example of "unsigned int" to "double" to work with modf()
    double inches;
    
    metersToFeetAndInches(meters, &feet, &inches);
    printf("%.1f meters is equal to %.0f feet and %.1f inches.\n", meters, feet, inches);
    
    return 0;
}

Hello mikeyshin.

I went over you succinct code. It was short and sweet. And it worked!!

I noticed that you used modf() in a way not done by others.

othersdouble fractionalFoot = modf(rawFeet, &feet);I understand this, modf() takes the integer part of rawFeet and stores in in the address of feet for safe keeping and takes the fraction part and places it into fractionalFoot. From this I thought you had to have an address (&feet) in the last part of the line of code when using modf().

But what you did.*inPtr = modf(rawFeet, ftPtr) I haven’t seen this way of using modf() on the net and I am not sure what you did here exactly. It looks like modf() took the integer part of rawFeet and stores it in the double float foot and stores the fraction and places it into the pointer *inPtr. WHAT!?!

I like it but it is puzzling. Can you explain what you did here or did I already do that?

[color=#004040]JR[/color]

I took a similar approach to mikeyshin to minimize the code. Here’s my finished blob that works:

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

void metersToFeetAndInches (double meters, unsigned int *ftPtr, double *inPtr) {
    // Make sure meters is non-negative
    if (meters < 0) {
        printf("Meters cannot be less than 0\n");
        return;
    } else {
        // Convert
        double feet;
        
        if (inPtr && ftPtr) {
            *inPtr = 12.0 * modf((meters * 3.281), &feet);
            *ftPtr = (unsigned int)feet;
        }
    }
}

int main(int argc, const char * argv[]) {
    
    double meters = 10.0;
    unsigned int feet;
    double inches;
    
    metersToFeetAndInches(meters, &feet, &inches);
    printf("%.1f meters is equal to %d feet and %.1f inches.\n", meters, feet, inches);
    
    return 0;
}

first question: is there a way to cast ftPtr so you can do it in a single line without changing the declarations like mikeyshin did?

second question: At one point I had this code for metersToFeetAndInches which failed:

void metersToFeetAndInches (double meters, unsigned int *ftPtr, double *inPtr) {
    // Make sure meters is non-negative
    if (meters < 0) {
        printf("Meters cannot be less than 0\n");
        return;
    } else {
        // Convert
        double *feetPtr;
        
        if (inPtr && ftPtr) {
            *inPtr = 12.0 * modf((meters * 3.281), feetPtr);
            *ftPtr = (unsigned int)*feetPtr;
        }
    }
}

It failed on the modf line with “Thread 1: EXC_BAD_ACCESS (code=2, address = 0x100000000)”. 0x100000000 is the address of feetPtr according to the debugger. Here’s the creepy part that I cannot explain: At first I thought for some reason it didn’t like the “meters * 3.281”, so I declared a new variable:

// Convert
        double *feetPtr;
        double feet = meters * 3.281;
        
        if (inPtr && ftPtr) {
            *inPtr = 12.0 * modf(feet, feetPtr);
            *ftPtr = (unsigned int)*feetPtr;
        }

This also failed. BUT if I declare feet before feetPtr, it succeeds. and actually if I declare ANYTHING before feetPtr, it succeeds. I can even remove the feet variable entirely and it works. For example, this works just fine:

// Convert
        int x = 0;  //completely not used
        double *feetPtr;
        
        if (inPtr && ftPtr) {
            *inPtr = 12.0 * modf((meters * 3.281), feetPtr);
            *ftPtr = (unsigned int)*feetPtr;
        }

Is this a compiler problem or is there something just really weird with the function?

[quote][code]// Convert
int x = 0; //completely not used
double *feetPtr;

    if (inPtr && ftPtr) {
        *inPtr = 12.0 * modf((meters * 3.281), feetPtr);
        *ftPtr = (unsigned int)*feetPtr;
    }[/code]

Is this a compiler problem or is there something just really weird with the function?
[/quote]
Neither of those. The problem is caused by the second argument passed to modf.

The second argument of modf should be the address of a variable of type double:

double modf (double value, double * iptr);

Here is an example of the proper use of modf:

// Print fractional and integral parts of PI

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[]) {
    
    const double myPI = 4.0 * atan (1.0);
    
    double intPart = 0;
    double fracPart = modf (myPI, &intPart);
    
    NSLog (@"%f %f %f", myPI, intPart, fracPart);
    
    return 0;
}

I realized the way I did it was a bit weird, which I ultimately corrected, but that example is using a pointer to a double, so it should have worked… and just changing the order of the assignment causes it to fail or succeed.

This succeeds:

[code]// Convert
int x = 0; //completely not used
double *feetPtr;

    if (inPtr && ftPtr) {
        *inPtr = 12.0 * modf((meters * 3.281), feetPtr);
        *ftPtr = (unsigned int)*feetPtr;
    }[/code]

This fails:

[code]// Convert
double *feetPtr;
int x = 0; //completely not used

    if (inPtr && ftPtr) {
        *inPtr = 12.0 * modf((meters * 3.281), feetPtr);
        *ftPtr = (unsigned int)*feetPtr;
    }[/code]

The only thing that changed is where x = 0 (which isn’t even used!) gets declared/assigned. Any ideas?

[quote]This succeeds:

[code]// Convert
int x = 0; //completely not used
double *feetPtr;

    if (inPtr && ftPtr) {
        *inPtr = 12.0 * modf((meters * 3.281), feetPtr);
        *ftPtr = (unsigned int)*feetPtr;
    }[/code]

The only thing that changed is where x = 0 (which isn’t even used!) gets declared/assigned. Any ideas?
[/quote]
That code is incorrect because you are passing an intialized feetPtr to modf function.

The order of declaration does not matter.

That is, x can be declared before feetPtr:

        int x = 0;  //completely not used
        double * feetPtr;

Or the other way around:

        double * feetPtr;
        int x = 0;  //completely not used

What really matters is the value of feetPtr variable you pass to modf:

Correct code:

// Convert
       
assert (inPtr && ftPtr);

double feet = 0;
double * integralPart = &feet;
* inPtr = 12.0 * modf ((meters * 3.281), integralPart);
* ftPtr = (unsigned int) * integralPart; // same: * ftPtr = (unsigned int)feet;

Or simply:

// Convert
       
assert (inPtr && ftPtr);

double feet = 0;
* inPtr = 12.0 * modf ((meters * 3.281), &feet);
* ftPtr = (unsigned int)feet;

I don’t mean to push, and I apologize if I’m frustrating you ibex, (or anyone for that matter). I’m just trying to understand what’s happening, and this is completely baffling me, as well as the developers I’ve shown this to (to be fair, none of them are C programmers, so they are not necessarily the perfect people to ask). So with that said:

modf() says it takes a double, and a double * (an address of a double), and returns a double.

So, with that, using this example:

[code]// Convert
//int x = 0;
double *feetPtr;

    printf("feetPtr address = %p, feetPtr value = %f\n", feetPtr, *feetPtr);
   
    if (inPtr && ftPtr) {          // I set a breakpoint here
        *inPtr = 12.0 * modf((meters * 3.281), feetPtr); 
        *ftPtr = (unsigned int)*feetPtr;
    }[/code]

When I run this, in the output window, I see “feetPtr address = 0x100000000, feetPtr value = 0.00000” is printed. According to the debug window, the address is the same, and value is 7.2911776173293662E-304 (effectively 0). So if the addresses and values are the same, whether you declared it as double *feetPtr or double feetPtr and then passed it to modf with &, why would it fail? Also why does having “int x = 0;” at the top cause the function to work properly when run as written above without the breakpoint, but when you comment it out it fails?

I feel like I’m not explaining this as well as I think I am, so if you have questions, let me know. And thanks so much for looking at this!

There is a serious coding error in the above example: you are passing uninitialised arguments to printf and modf functions. You have declared the feetPtr variable as double * but did not assign a valid address to it.

Using an uninitialised variable is a crime when writing code in languages such as C, C++, and Objective-C. :slight_smile:

I am surprised that your code is not crashing and burning, but that’s probably because the body of if (inPtr && ftPtr) {…} is not being executed; that is, modf is not being invoked at all. Even before that, the printf statement should cause the program to crash.

Are you actually using that piece of code above? Are you doing this stuff on a Mac?

Run the following program, and let us know what you are getting:

//  main.m

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{
    // int x = 0;
    double *feetPtr;   // breaking bad
    printf ("feetPtr address = %p, feetPtr value = %f\n", feetPtr, *feetPtr);

    return 0;
}

Those numbers you are seeing in the output are random numbers. Therefore, I would not worry to much about making sense of the random output produced by an incorrect piece of code. If you pass garbage to a function, it may produce garbage at worst or it may cause your program to crash and burn at best.

Languages such as C, C++, and even Objective-C are good for writing high-performance applications, but you need to master them before you can produce masterpieces. :slight_smile:

But for the time being, repeat the following mantra 10 times:
Before using it, I must initialise a variable properly.

Before using it, I must initialize a variable properly.
Before using it, I must initialize a variable properly.
Before using it, I must initialize a variable properly.

:slight_smile:

So the plot thickens…

I cut and pasted eveything inside your main function into my ConvertMetricToImperial program since that is the project I had open. Here’s my output:

It behaved the same why my bad code behaved. I then cut it out and pasted the same code into a new project called TestFail. That same code died at the printf with EXC_BAD_ACCESS, exactly what you would expect, no matter what declarations I put beforehand. I think we are finding our communication breakdown…

Therefore here’s my entire ConvertMetricToImperial program. Please pull it into xcode on your machine and see what you get. I copied/pasted your code from my function to a new one called badFunction(). It behaves exactly like you would expect, crashing on the printf. Within my metersToFeetAndInches function however, it runs happily as long as “int x = 0;” is not commented out until it crashes at return 0; in main. I also added a modf and printf to further use the bad variable. I would like to understand what is special about this function that allows the bad code to execute (until the main function returns that is), while badFunction() crashes immediately.

//
//  main.c
//  ConvertMetricToImperial
//

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

void badFunction() {
    // int x = 0;
    double *feetPtr;   // breaking bad
    printf ("feetPtr address = %p, feetPtr value = %f\n", feetPtr, *feetPtr);
}

void metersToFeetAndInches (double meters, unsigned int *ftPtr, double *inPtr) {
    // Make sure meters is non-negative
    if (meters < 0) {
        printf("Meters cannot be less than 0\n");
        return;
    } else {
        
        //badFunction();
        
        int x = 0;
        double *feetPtr;   // breaking bad
        printf ("feetPtr address = %p, feetPtr value = %f\n", feetPtr, *feetPtr);
        
        double result = modf(3.14, feetPtr);
        printf ("feetPtr address = %p, feetPtr value = %f\nresult address = %p, result value = %f\n", feetPtr, *feetPtr, &result, result);
        
        /*  WORKING ALGORITHM
        // Convert
        double feet = 0.0;
        
        if (inPtr && ftPtr) {
            *inPtr = 12.0 * modf((meters * 3.281), &feet);
            *ftPtr = (unsigned int)feet;
        }
        */
    }
}

int main(int argc, const char * argv[]) {
    
    double meters = 10.0;
    unsigned int feet;
    double inches;
    
    metersToFeetAndInches(meters, &feet, &inches);
    //printf("%.1f meters is equal to %d feet and %.1f inches.\n", meters, feet, inches);
    
    return 0;
}

I know I won’t be making masterpieces anytime soon… That’s why I’m going through this book - gotta start somewhere. :slight_smile: The last time I looked at C and pointers/addresses was freshman year computer science classes 15 years ago. I don’t remember anything, and this book is actually teaching me concepts I never fully understood even back then. I’m quite happy with it, despite my atrocious coding practices (one must make mistakes to learn though!).

Okay, here is what I have done in an effort to help you understand what happens if you write code that leaves you at the mercy of Compiler’s default code generation policies.

Compiled your code (without any modifications):

       ...
       // badFunction();

       int x = 0;
       double *feetPtr;   // breaking bad
       printf ("feetPtr address = %p, feetPtr value = %f\n", feetPtr, *feetPtr);
       ...

And ran it, and here is the output:

feetPtr address = 0x7fff5fbff870, feetPtr value = 0.000000
feetPtr address = 0x7fff5fbff870, feetPtr value = 3.000000
result address = 0x7fff5fbff7f0, result value = 0.140000

As you can see, 0x7fff5fbff870 is a random address value because feetPtr is not initialised, and this random address value seems to lie within the address space the program is allowed to write. That’s why the program is not crashing - very scary because in a larger program this will definitely cause some mysterious behaviour. (We are at the mercy of the Compiler’s default code generation policy here because the feetPtr variable has been left uninitialised.)

I then made a minor change (comment out int x = 0):

       ...
       // badFunction();

       // int x = 0;
       double *feetPtr;   // breaking bad
       printf ("feetPtr address = %p, feetPtr value = %f\n", feetPtr, *feetPtr);
       ...

The program crashed on the printf statement.

I then made one more change (invoke badFunction):

       ...
       badFunction();

       // int x = 0;
       double *feetPtr;   // breaking bad
       printf ("feetPtr address = %p, feetPtr value = %f\n", feetPtr, *feetPtr);
       ...

This time, the program crashed on the printf statement inside the badFunction.

This is a good example of how one uninitialised variable can wreak havoc if it manages to escape scrutiny.

Here is the moral of the story. If you leave variables uninitialised, you are at the mercy of the compiler’s default code generation policies.

So, what it boils down to is, I got lucky there just happened to be nothing else at that random address. That’s a good enough explanation for me - thanks for your help and patience!

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

void metersToFeetAndInches(double meters, unsigned int *ftPtr, double *inPtr){
    double rawFeet = meters * 3.281;
    
    double convertedFeet;
    if (ftPtr) {
        convertedFeet = (double)*ftPtr;
    }
    
    double fractionalFoot = modf(rawFeet, &convertedFeet);
    
    *ftPtr = (unsigned int)convertedFeet;
    
    double inches = fractionalFoot * 12;
    
    if (inPtr) {
        *inPtr = inches;
    }
}

int main(int argc, const char * argv[]) {
    double meters = 3.0;
    unsigned int feet;
    double inches;
    metersToFeetAndInches(meters, &feet, &inches);
    
    printf("feet: %d\ninches: %.2f\n", feet, inches);
    
    return 0;
}

output:

feet: 9
inches: 10.12
Program ended with exit code: 0

I came up with the following for the challenge.

[code]#include <stdio.h>
#include <math.h>

void metersToFeetAndInches(double meters, unsigned int *ftPtr, double *inPtr)
{
// Convert meters to feet
double rawFeet = meters * 3.281;
double feet;
double inches;

// Stash feet
inches = modf(rawFeet, &feet);

// Store feet at address
printf("Storing %.0f to the address %p\n", feet, ftPtr);
*ftPtr = feet;

// Calculate inches from stash
double fractionalFoot = rawFeet - feet;
inches = fractionalFoot * 12.0;

// Store inches at address
printf("Storing %.2f to the address %p\n", inches, inPtr);
*inPtr = inches;

}

int main(int argc, const char * argv[])
{
double meters = 3.0;
unsigned int feet;
double inches;

metersToFeetAndInches(meters, &feet, &inches);
printf("%.1f meters is equal to %d feet and %.2f inches.\n", meters, feet, inches);

return 0;

}
[/code]

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

void metersToFeetAndInches(double meters, unsigned int *ftPtr, double *inPtr){
    double rawFeet = meters * 3.281;
    
    double convertedFeet;
    if (ftPtr) {
        convertedFeet = (double)*ftPtr;
    }
    
    double fractionalFoot = modf(rawFeet, &convertedFeet);
    
    *ftPtr = (unsigned int)convertedFeet;
    
    double inches = fractionalFoot * 12;
    
    if (inPtr) {
        *inPtr = inches;
    }
}

Just found this forum. Here’s my working solution to the modf() challenge in Chapter 10. I didn’t actually implement the non-NULL checks (bad programmer!)


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

void metersToFeetAndInches(double meters, double *ftPtr, double *inPtr) {
    
    *inPtr = modf(meters * 3.281, ftPtr) * 12.0;
    
}

int main(int argc, const char * argv[]) {
    
    double meters = 20.0;
    double feet, inches;
    
    metersToFeetAndInches(meters, &feet, &inches);
    printf("%.1f meters is equal to %.0f feet and %.2f inches. \n", meters, feet, inches);
    
    return 0;
}

Reading thru the thread, I saw a programmer use this:

assert (inPtr && ftPtr);

I have to go read up on what that is, as I haven’t seen it before.

You can just write assert (inPtr && ftPtr) for that.

That means kill the program if the condition expression inPtr && ftPtr (or inPtr != 0 && ftPtr != 0) evaluates as false.

Elenex,

Looking at your solution, it seems that you have some extra code that is redundant.

modf() returns the integer and the fractional part of your conversion. Therefore you can find the inches by simply multiplying the fractional result of your call to modf() by 12. No need to calculate fractionalFoot again, as it should be identical. Two ways to find the same result, in other words.

Chris