I'm currently reading through the MEAP book Tiny C Projects by Dan Gookin. In the opening chapter he presents a small daily greeting program which also reports the phase of the moon. The calculations in the book for this look very strange - he also states outright that he doesn't know where he first got this code from or even what it's doing; it also doesn't have any comments or links to any other explanatory information. This is my cleaned-up rendition of it:
int moonphase_from_tm(struct tm* datetime)
{
int year = datetime->tm_year;
int month = datetime->tm_mon;
int day = datetime->tm_mday;
if (month == 2)
{
day += 31;
}
else if (month > 2)
{
day += 59 + (month - 3) * 30.6 + 0.5;
}
int g = year % 19;
int e = (11 * g + 29) % 30;
if (e == 25 || e == 24)
{
++e;
}
int phase = ((((e + day) * 6 + 5) % 177) / 22 & 7);
return phase;
}
It returns an int used to index a string out of a char* array describing the current moon phase.
Can anybody figure out what this is actually doing? I looked up moon phase calculations on Google and can't find anything that bears more than a passing resemblance to this. It also isn't entirely accurate with the entire thing being calculated with ints (it's out by a day or so, checking against some moon-phase tracking websites), which makes finding something similar a little more difficult.
Below is the original old-style version as reproduced in the book; when calling it, the author adds 1900 to the date, but then this is immediately subtracted again on line 13 when assigning to g, so I just didn't bother doing either:
int moonphase_book_verbatim(int year, int month, int day)
{
int d, g, e;
d = day;
if (month == 2)
{
d += 31;
}
else if (month > 2)
{
d += 59 + (month - 3) * 30.6 + 0.5;
}
g = (year - 1900) % 19;
e = (11 * g + 29) % 30;
if (e == 25 || e == 24)
{
++e;
}
return ((((e + d) * 6 + 5) % 177) / 22 & 7);
}
Related
I'm a beginner in CS50 trying to do the Credit problem from Problem Set 1. We are using C. The problem asks us to validate credit card numbers using Luhn's algorithm. I was going to use an array, but code using one wasn't easy to understand for me.
I'm trying to type out the full Luhn formula to calculate. I don't know where I'm messing up. Every time I run a card number that's supposed to be valid, it's never card_total % 10 = 0 like it should be.
Example Card Number: 4003600000000014 => checksum total (int card_total) = 33
Below is just code that asks for the credit card number, runs it through the algorithm, and prints out the total checksum just for me to see if it's working properly. I know this is really long, but this is just for my understanding of all the pieces. I've written much shorter and nicer code before. I swear!
EDIT: I run my code in the cs50 VS virtual codespace, which uses Linux I think. I will take the advice of changing the other ints to longs and see if this fixes my problem. My formulas are consistent, so this is a formatting issue. Once this is fixed, the rest of the problem is easy to solve.
Someone mentioned my complicated calculations. Sorry. I know this could be more brief, but I just wanted to follow the algorithm so I can see what's going on. The lecture for this week didn't touch on going through arrays or manipulating strings, so I'm just doing what I know so far in C. This would be a lot easier in python.
EDIT 2: All that needed to change was int to long. The code now works perfectly and incorporates properly with the rest of my code checking for Amex, Visa, etc. THANK YOU SO MUCH!
#include <cs50.h>
#include <stdio.h>
int get_number(void);
int main(void)
{
long n = get_number();
//calculating card number with modulo
long a = ((n % 10000000000000000) / 1000000000000000);
long b = ((n % 1000000000000000) / 100000000000000);
long c = ((n % 100000000000000) / 10000000000000);
long d = ((n % 10000000000000) / 1000000000000);
long e = ((n % 1000000000000) / 100000000000);
long f = ((n % 100000000000) / 10000000000);
long g = ((n % 10000000000) / 1000000000);
long h = ((n % 1000000000) / 100000000);
long i = ((n % 100000000) / 10000000);
long j = ((n % 10000000) / 1000000);
long k = ((n % 1000000) / 100000);
long l = ((n % 100000) / 10000);
long m = ((n % 10000) / 1000);
long ene = ((n % 1000) / 100);
long o = ((n % 100) / 10);
long p = ((n % 10) / 1); //The "/ 1" is just for visualization
// multiply odd numbers by 2 //Also for visualization
long q = a * 2;
long r = c * 2;
long s = e * 2;
long t = g * 2;
long u = i * 2;
long v = k * 2;
long w = m * 2;
long x = o * 2;
//process odd products //Luhn's has exceptions for double digit numbers
long qq;
if (q < 10)
{
qq = ((q % 10) + ((q % 100)/10));
}
else if (q > 10)
{
qq = (q % 10) + 1;
}
else if (q == 10)
{
qq = 1;
}
else
{
qq = q;
}
long rr;
if (r < 10)
{
rr = ((r % 10) + ((r % 100) / 10));
}
else if (r > 10)
{
rr = (r % 10) + 1;
}
else if (r == 10)
{
rr = 1;
}
else
{
rr = r;
}
long ss;
if (s < 10)
{
ss = ((s % 10) + ((s % 100) / 10));
}
else if (s > 10)
{
ss = (s % 10) + 1;
}
else if (s == 10)
{
ss = 1;
}
else
{
ss = s;
}
long tt;
if (t < 10)
{
tt = ((t % 10) + ((t % 100) / 10));
}
else if (t > 10)
{
tt = (t % 10) + 1;
}
else if (t == 10)
{
tt = 1;
}
else
{
tt = t;
}
long uu;
if (u < 10)
{
uu = ((u % 10) + ((u % 100) / 10));
}
else if (u > 10)
{
uu = (u % 10) + 1;
}
else if (u == 10)
{
uu = 1;
}
else
{
uu = u;
}
long vv;
if (v < 10)
{
vv = ((v % 10) + ((v % 100) / 10));
}
else if (v > 10)
{
vv = (v % 10) + 1;
}
else if (v == 10)
{
vv = 1;
}
else
{
vv = v;
}
long ww;
if (w < 10)
{
ww = ((w % 10) + ((w % 100) / 10));
}
else if (w > 10)
{
ww = (w % 10) + 1;
}
else if (w == 10)
{
ww = 1;
}
else
{
ww = w;
}
long xx;
if (x < 10)
{
xx = ((x % 10) + ((x % 100) / 10));;
}
else if (x > 10)
{
xx = (x % 10) + 1;
}
else if (x == 10)
{
xx = 1;
}
else
{
xx = x;
}
//Sum processed odd products
long total_odd = qq + rr + ss + tt + uu + vv + ww + xx;
//sum total odd products and even card numbers
long bb = ((b % 10) + ((b % 100) / 10));
long dd = ((d % 10) + ((d % 100) / 10));
long ff = ((f % 10) + ((f % 100) / 10));
long hh = ((h % 10) + ((h % 100) / 10));
long jj = ((j % 10) + ((j % 100) / 10));
long ll = ((l % 10) + ((l % 100) / 10));
long eneene = ((ene % 10) + ((ene % 100) / 10));
long pp = ((p %10) + ((p % 100) / 10));
long total_even = bb + dd + ff + hh+ jj + ll + eneene + pp;
//sum odd & even
long card_total = total_odd + total_even;
printf(" %li\n", card_total);
}
int get_number(void) //Works perfectly
{
long n;
do
{
n = get_long("Number: "); // Records credit card number
}
while (n < 1000000000000 || n > 5599999999999999); // CC at least 13 digits but < 17 digits
return n;
}
Trying out your code and debugging it, the issue I found right away is that the return size element for your "get_number" function is too small.
int get_number(void);
When I debugged the returned value to the main function, the value was a negative number since the integer return value is too small for your long integer.
Changing the function definition to "long get_number(void)" in the prototype and the function, allowed for the entered value to flow back to the main function and get tested properly. Following was the test output on my terminal (FYI, I added a couple of printf statements to illustrate that the entered value was flowing through properly).
#Vera:~/C_Programs/Console/CC_Luhn/bin/Release$ ./CC_Luhn
Number: 4003600000000014
Value stored is: 4003600000000014
Returned number: 4003600000000014
20
Just as a double-check, I ran a Luhn calculator program I had built some time back to answer a similar problem. Following is the output again using your credit card example as input.
#Vera:~/C_Programs/Console/Luhn/bin/Release$ ./Luhn
Enter a number: 4003600000000014
Length of number is: 16
Digit is: 4 Value is: 8 Sum is 8
Digit is: 0 Value is: 0 Sum is 8
Digit is: 0 Value is: 0 Sum is 8
Digit is: 3 Value is: 3 Sum is 11
Digit is: 6 Value is: 3 Sum is 14
Digit is: 0 Value is: 0 Sum is 14
Digit is: 0 Value is: 0 Sum is 14
Digit is: 0 Value is: 0 Sum is 14
Digit is: 0 Value is: 0 Sum is 14
Digit is: 0 Value is: 0 Sum is 14
Digit is: 0 Value is: 0 Sum is 14
Digit is: 0 Value is: 0 Sum is 14
Digit is: 0 Value is: 0 Sum is 14
Digit is: 0 Value is: 0 Sum is 14
Digit is: 1 Value is: 2 Sum is 16
Digit is: 4 Value is: 4 Sum is 20
Valid
The result of the final calculation was the value of "20" which is a number that ends in zero, which indicates a valid number.
This might have been a bit verbose, but the bottom line is be careful mixing your integer sizes in functions and formulas.
Give that a try to see if it meets the spirit of your project.
I have the weirdest exercise I've ever seen: I have to find a leap year by scanning a year in the console and to control if that is a leap year.
I can only use + - / * % as arithmetical operators; I am not allowed to use any other operators or functions.
Here is what I have so far:
int year = 0;
bool b = false;
printf ("Type in a year: ");
int helpVar = 1000;
for (int i = 0; i < 4; i++) {
year += (getchar() - '0') * helpVar;
helpVar = helpVar / 10;
}
b = (((year % 4) + (year % 100) + (year % 400)) + 1) % 2;
So I don't understand what I am doing wrong here. It works so far, the only case that's freaking me out is for year "1900". It shouldn't be a leap year, but appears to, by my code.
What am I missing here?
Here is one possibility (perhaps not the shortest -- obviously only works for the Gregorian calendar):
b = (((year-1)%4)+1)/4 - (((year-1)%100)+1)/100 + (((year-1)%400)+1)/400;
The idea is that ((year-1) % n) + 1 equals n only if year is a multiple of n (for positive year), and is smaller than n otherwise. Thus, if you divide that by n, you get 1 if and only if (year % n == 0).
Since year%100==0 cannot be true if year%4==0 is not, you can subtract that from each other, but add the year%400==0 term at the end.
The problem is that your % operations are done in integer arithmetic and, as such, their results may well be values other than 0 or 1. However, if you cast each of those results to the bool type (assuming that is as defined in the <stdbool.h> header), then your formula will work:
b = (((bool)(year % 4) + (bool)(year % 100) + (bool)(year % 400)) + 1) % 2;
The only modifications I've made to your code is to add the (bool) casts on the results of each of the % operations.
EDIT: As pointed out in the comments, the above solution 'breaks the rules' by using the cast operator. The following modification also works (in a similar way), but it uses the ! operator (twice) on each % result; this also breaks the rules, but it may be nice for posterity:
b = ((!!(year % 4) + !!(year % 100) + !!(year % 400)) + 1) % 2;
A modification of this solution using only implicit type conversions to bool (thus not using cast operators) would appear to be within the rules. This can be done using temporary/intermediate variables to hold the results of each % operation:
bool four = year % 4, hundred = year % 100, fourhund = year % 400;
b = (four + hundred + fourhund + 1) % 2;
Or, you could pre-declare the three intermediate bool variables and then do those implicit type conversions 'inline', like this:
bool m4, m100, m400; // You could also move this to where you declare "b"
b = (((m4 = year % 4) + (m100 = year % 100) + (m400 = year % 400)) + 1) % 2;
From the post above, the definition of a leap year is that: for year n
n should be a multiple of 4, which can be presented as n % 4 == 0
n is a multiple of 400 or n is not a multiple of 100
The problem is that you shouldn't write all those three conditions as a addition operation. You should write them as logical expression.
In your code:
b = (((year % 4) + (year % 100) + (year % 400)) + 1) % 2;
there are 3 conditions, year % 4, year % 100 and year % 400, there are two ways to make b = true after the evaluation:
all three conditions are false
there are one and only one case is false
Okey, let's make it clear
The definition of leap year:
n is multiple of 4 and not a multiple of 100
n is multiple of 400
So you code can be like this:
int year;
if (year % 400) {
printf("Fine it's a leap year\n");
} else if (year % 100 == 0) {
printf("Oh no it's not a leap year\n");
} else if (yaer % 4 == 0) {
printf("It's a leap year\n");
} else {
printf("Not a leap year\n");
}
I've spent a good hour on this, but I can't create a formula that works for my c program. (I have a new programmer).
I have to convert UTC time to its respective time in a particular city. My code works perfectly except here. Here it gives me the wrong answer. I can't wrap my head around it (I created a formula but it makes no sense to me).
In my program, time is entered as 24 hour time. 9AM = 900, 12PM = 1200, 12am = 0 etc.
If we are asked to convert 2359 to Eucla time (UTC +845) my program outputs 804. The correct answer is 844.
I figured out how to calculate 844, but I make no sense of it.
2359 + 845 = 3204 (adding the timezone offset 845 to the UTC time)
3204 - 60 = 3144 (minus 60 for some reason [I followed my time overflow formula]
3144 - 2400 = 2400 (minus 2400 because time cannot be more than 2359)
How my program works
First plus UTC and offset time
calculatedTime = UTC + offset;
Then under that
if (calculatedTime < 2359) {
calculatedTime = calculatedTime - 2400;
}
I also have another function which checks for overflow time underneath
if (((calculatedTime > 59) && (calculatedTime < 99)) || ((calculatedTime > 159) && (calculatedTime < 199))) {
// All the way to 2359
calculatedTime = calculatedTime - 60 + 100;
}
You need to separate the time into hours and minutes. Then add the time zone offsets to the hours and minutes separately. Handle roll-over. Finally, recombine the hours and minutes into the final answer.
Like this:
int main(void)
{
// inputs
int time = 2359;
int zone = 845;
// separate hours and minutes
int timeHours = time / 100;
int timeMinutes = time % 100;
int zoneHours = zone / 100;
int zoneMinutes = zone % 100;
// add the hours and minutes
int hours = timeHours + zoneHours;
int minutes = timeMinutes + zoneMinutes;
// handle the rollover conditions
if (minutes > 60) {
minutes -= 60;
hours++;
}
if (hours > 24) {
hours -= 24;
}
// recombine the hours and minutes
int adjustedTime = hours * 100 + minutes;
printf("%d\n", adjustedTime);
}
Note that this code only works for timezones with positive offsets. You'll need to figure out how to make it work for negative time zones.
OP's code has various off-by-one errors.
// if (((calculatedTime > 59) && (calculatedTime < 99)) ||
// ((calculatedTime > 159) && (calculatedTime < 199))) {
if (((calculatedTime > 59) && (calculatedTime < 100)) ||
((calculatedTime > 159) && (calculatedTime < 200))) {
// if (hours > 24) {
// hours -= 24;
// }
if (hours >= 24) {
hours -= 24;
}
Also code has more clarity using values like 60, 100
if (((calculatedTime >= 60) && (calculatedTime < 100)) ||
((calculatedTime >= 60*2) && (calculatedTime < 100*2))) {
Yet OP's approach fails with negative numbers.
To cope with positive and negative time values, split the "hhmm" time into a hours and minutes. Look for conditions of "minute" overflow. I recommend 2 helper functions to split and combine results.
#include <stdio.h>
#include <stdlib.h>
void hhmm_split(int hhmm, int *hour, int *min) {
*hour = hhmm / 100;
*min = hhmm % 100;
}
/* `min` may be outside the primary range of (-60 ... 60) */
int hhmm_combine(int hour, int min) {
hour += min / 60;
min %= 60;
if (hour < 0 && min > 0) {
min -= 60;
hour++;
} else if (hour > 0 && min < 0) {
min += 60;
hour--;
}
hour %= 24;
return hour * 100 + min;
}
Test code
void hhmm_add(int t1, int t2) {
int t1_hh, t1_mm, t2_hh, t2_mm;
hhmm_split(t1, &t1_hh, &t1_mm);
hhmm_split(t2, &t2_hh, &t2_mm);
int sum = hhmm_combine(t1_hh + t2_hh, t1_mm + t2_mm);
printf("t1:% 05d + t2:% 05d = sum:% 05d\n", t1, t2, sum);
}
int main(void) {
hhmm_add(2359, 845);
hhmm_add(2359, -845);
hhmm_add(-2359, 845);
hhmm_add(-2359, -845);
}
Output:
t1: 2359 + t2: 0845 = sum: 0844
t1: 2359 + t2:-0845 = sum: 1514
t1:-2359 + t2: 0845 = sum:-1514
t1:-2359 + t2:-0845 = sum:-0844
I'm stuck on an assignment for C where I have to convert a Julian Date that is input by the user and convert it to Gregorian. When I run it and I enter a number for the Julian date all it prints out is 1, 0, -12 for the month, day and year. How to fix? I also don't completely understand pointers so maybe that is the problem?
#include <stdio.h>
#include <math.h>
void getDateFromJulian(double jd, int *month, int *day, int *year);
double getDoubleFromUser(char* msg);
int main() {
double jd = 0;
int month, day, year;
jd = getDoubleFromUser("Enter a valid Julian Day: ");
if (jd != -999.0) {
getDateFromJulian(jd, &month, &day, &year);
printf("Month, day, year is: %d, %d, %d \n", month, day, year);
}
return;
}
double getDoubleFromUser(char* msg){
int input;
int term;
//check for valid number
printf("Enter Julian Day: \n");
scanf_s("%1f%c");
if (scanf_s("%1f%c", &input, &term) != 2) {
if (term >= 0 * 41 && term <= 0 * (int)7) {
printf("That's not a valid number!\n");
return -999.0;
}
}
}
void getDateFromJulian(double jd, int *month, int *day, int *year) {
int A, B, C, D, E, alpha;
double Z, F;
int JD = 0;
F = modf(JD, &Z);
if (Z < 2299161) {
A = Z;
}
if (Z >= 2299161) {
alpha = (int)((Z - 1867216.25) / 36524.25);
A = Z + 1 + alpha - (int)(alpha / 4);
}
B = A + 1524;
C = (int)((B - 122.1) / 365.25);
D = (int)(365.25 * C);
E = (int)((B - D) / 30.6001);
day = B - D - (int)(30.6001 * E) + 0.5;
if (E < 14) {
month = E - 1;
}
if (E = 14 || 15) {
month = E - 13;
}
if (month > 2) {
year = C - 4716;
}
if (month = 1 || 2) {
year = C - 5715;
}
return;
}
When you assign to the day, month, or year you should do it as follows:
day[0] = B - D - (int)(30.6001 * E) + 0.5;
month[0] = E - 1;
and so on. Basically, you have to assign to the first element of the pointer (array) which is sometimes a little bit clearer than using an asterisk, which you left out.
The program effectively wasn't doing anything because the output values were essentially set to gibberish.
In addition to assigning integers to pointers (month = newVal; for example), some of your conditional tests are always true.
if (E = 14 || 15) {
month = newVal;
newVal = E - 13;
}
This assigns the value of 14 || 15 (which is 1) to E, and then tests that E is non-zero, which of course, it always is.
This test (and the other one like it) should be:
if (E == 14 || E == 15) {
*month = newVal;
newVal = E - 13;
}
Note that the newVal integer is assigned to the integer that is pointed to by month (by using *month), and not the actual pointer variable month. This remedy should be applied to assignments to the day and year pointers also.
The program is supposed to return what day of the week it is for the entered date. One of the dates that doesn't work is 01012000. Nothing is returned at all. But on some other leap years the first day of March can be calculated. Also sometimes seemingly random dates don't work. I'm not sure how to fix this. Also I'm supposed to write the part that calculates "daynumber" and then call on it later so I'm not sure if I'm doing that right.
Sorry for the beginner questions, this is my first ever C program.
#include<stdio.h>
#include<math.h>
int main()
{
int day, month, year, lastday, dayname, daynumber, input, d;
//Determine the last day of user specified month
printf("Enter date: ddmmyyyy:\n");
scanf("%d", &input);
day = input/1000000;
month = (input/10000) % 100;
year = input % 10000;
if (month == 1 || month == 3 || month == 5 || month == 7 ||
month == 8 || month == 10 || month == 12)
lastday = 31;
else if (month == 4 || month == 6 || month == 9 || month == 11)
lastday = 30;
else if ((year%4 == 0 && year%100 !=0) || year%400 == 0)
lastday = 29;
else
lastday = 28;
//Verify the date
if (year < 0)
return 1;
if (month < 1 || month > 12)
return 2;
if (day < 1 || day > lastday)
return 3;
//Algorithm
{
int m, d, y, c, daynumber;
if (month > 3)
m = month - 2;
else
m = month + 10;
if (m == 11 || m == 12)
year = year - 1;
else
year = year;
d = day;
y = year % 100;
c = year / 100;
daynumber = (((13*m - 1)/5) + d + y + (y/4) + (c/4) - 2*c) % 7;
if (daynumber == 0)
printf("Sunday\n");
if (daynumber == 1)
printf("Monday\n");
if (daynumber == 2)
printf("Tuesday\n");
if (daynumber == 3)
printf("Wednesday\n");
if (daynumber == 4)
printf("Thursday\n");
if (daynumber == 5)
printf("Friday\n");
if (daynumber == 6)
printf("Saturday\n");
}
}
I can't see your bug, but there is no need to scan the whole number and divide, use:
scanf("%2d%2d%4d", &day, &month, &year);
Using Sakamoto's algorithm you can do the same in few lines:
#include <stdio.h>
static int wday(int d, int m, int y)
{
static int offset[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
y -= m < 3;
return (y + y / 4 - y / 100 + y / 400 + offset[m - 1] + d) % 7;
}
int main(void)
{
const char *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
int d, m, y;
printf("Enter date [ddmmyyyy]: ");
scanf("%2d%2d%4d", &d, &m, &y);
printf("%s\n", days[wday(d, m, y)]);
return 0;
}
This
(((13*m - 1)/5) + d + y + (y/4) + (c/4) - 2*c)
can probably be negative. The result of % 7 would then still be negative and nothing is printed.
Just add daynumber = (daynumber + 7) % 7; after the line
daynumber = (((13*m - 1)/5) + d + y + (y/4) + (c/4) - 2*c) % 7;
You are using:
printf("Enter date: ddmmyyyy:\n");
scanf("%d", &input);
Here you are storing the input as a int. If sizeof(int) is 2 byte, its range would be between -32,768 to 32,767 and your input would be out of the range. For this you should use long int.