Я хочу умножить каждую вторую цифру на 2, начиная со предпоследней цифры числа, а затем сложить вместе цифры этих продуктов, но первые напечатанные значения кажутся совершенно бессмысленными.
#include <stdio.h>
#include <math.h>
int len(long li);
int main(void)
{
// Get credit card number
long credit_card = 378282246310005;
// Adding every second digit's double's digits, starting with the second to last
int sum = 0;
int digit_doubled;
for (int i = 0; i < len(credit_card); i++)
{
if (i % 2 == 0)
{
continue;
}
digit_doubled = (int) (credit_card / pow(10, i)) % 10 * 2;
printf("Digit doubled %i: %i\n", i, digit_doubled);
for (int j = 0; j < len(digit_doubled); j++)
{
sum += (int) (digit_doubled / pow(10, j)) % 10;
}
}
}
int len(long li)
{
if (li == 0)
{
return 1;
}
return floor(log10(li)) + 1;
}
Я попытался изменить выражение, чтобы посмотреть, какие результаты я получу. Когда я удалил
% 10 * 2
с конца digit_doubled = (int) (credit_card / pow(10, i)) % 10 * 2;
я получил результаты, которые указывают на какое-то целочисленное переполнение, но я совершенно не понимаю, откуда оно могло взяться, поскольку моя программа на самом деле нигде не выдает никаких высоких значений.
Банальное упрощение: 2*5 + 2*3 + 2*7
== 2 * (5 + 3 + 7)
... Не усложняйте себе жизнь.
@EricPostpischil, извините за имя параметра. Это было редактирование в последнюю минуту, и в моем реальном коде такого не было. Я попытался напечатать переменную «credit_card» после ее инициализации, и кажется, что информация не теряется. Я должен использовать длинное целое из-за моих инструкций к курсам. Почему вы не должны использовать pow
для целочисленных степеней и что такое целочисленная процедура?
@ Fe2O3, если результат умножения больше 10, я также должен добавить две цифры результата, так что это не сработает.
Ах! так что вы действительно хотите только if ( x >= 10 )
вместо внутреннего loop
. Опять же, зачем усложнять? 2 * 3 = 6
так что никаких "складываний"... 2 * 8 = 16
так что вы хотите 1+6=7
в качестве значения, кажется... Вызов цикла и pow()
кажется излишним для такого простого и ограниченного вычисления... ПОЦЕЛУЙ... Удвоение каждого однозначного значения 0-9
приводит к однозначному значению... Попробуйте вместо этого простую "таблицу поиска", возможно...
@Alan: Некоторые реализации pow
возвращают числа, такие как 99.9999999999999857891452847979962825775146484375 для pow(10, 2)
или аналогичные случаи, а затем преобразование в целое число дает 99, а не 100. «Целая процедура» — это процедура с целочисленными параметрами и целочисленным типом возвращаемого значения, которая выполняет целочисленную арифметику. .
@EricPostpischil, я новичок в C. Не могли бы вы объяснить, как эти процедуры будут работать?
@Alan Обязательно примите лучший ответ, нажав на галочку, чтобы мы знали, что у вас все готово.
Обрабатывать цифры в CCN (номере кредитной карты) намного проще, если рассматривать его как строку, а не как число. У вас уже есть цифры, разделенные для вас.
В первом цикле, когда i=1
вы приводите значение, превышающее максимальное значение, которое может быть сохранено в целом числе (2147483647), это приводит к его усечению и в моей системе становится отрицательным:
digit_doubled = (int) (credit_card / pow(10, i)) % 10 * 2; // =>
digit_doubled = (int) 37828224631000 % 10 * 2; / =>
digit_doubled = -1847312168 % 10 * 2; // =>
digit_doubled = -16;
Я предлагаю вам включить stdint.h
и использовать uint64_t
вместо того, чтобы молча предполагать, что тип long равен 64 битам. В качестве альтернативы рассмотрите возможность использования строки в качестве номера кредита, который является идентификатором, а не числом (даже если он записывается как единица).
Функции смешивания, которые работают с значениями типа double для целочисленного типа, откроют вам доступ к ошибкам округления с плавающей запятой. Вы можете использовать uint64_t
версии этих функций вместо double
. Ниже я реализовал для вас функции len()
и my_pow10()
. Поскольку значение digit_doubled
не превышает 18, вы можете просто встроить это вычисление вместо использования цикла.
#include <stdio.h>
#include <stdint.h>
uint64_t my_pow10(uint64_t x) {
if (x == 0) return 1;
size_t v = 10;
for(size_t i = 1; i < x; i++, v *= 10);
return v;
}
size_t len(uint64_t v) {
size_t i = 0;
for(; v; i++, v /= 10);
return i;
}
int main(void) {
const uint64_t credit_card = 378282246310005;
const uint8_t credit_card_len = len(credit_card);
uint8_t sum = 0;
for (uint8_t i = 1; i < credit_card_len; i += 2) {
uint8_t digit_doubled = credit_card / my_pow10(i) % 10 * 2;
printf("Digit doubled %hhu: %hhu\n", i, digit_doubled);
sum += digit_doubled / 10 + digit_doubled % 10;
}
printf("sum = %u\n", sum);
}
Тьфу, почему кредит_карта не строка? Это оскорбляет мои чувства.
@Joshua Джошуа, потому что вы выбрали это, однако, я думаю, вам следует привести более сильный аргумент. Как будто это идентификатор, а не номер.
Я очень новичок в C. Я не понимаю, что вы имеете в виду. Не могли бы вы просто сказать мне, что заставляет мой код возвращать совершенно бессмысленные значения?
@ Алан, я расширил объяснение. Это помогло?
@AllanWind Приведение к (int)
перед выполнением % 10
— одна из основных проблем; другой использует pow
@М.М. Я согласен. Что бы вы хотели, чтобы я разъяснил?
Извините, это было предназначено для пометки OP
Предложив использовать LUT для доставки однозначного значения, требуемого вопросом OP, вот фрагмент кода, чтобы сделать именно это:
unsigned long long copy = longValue; // perhaps correct on OP's compiler??
copy /= 10; // dispose of the checksum digit (rightmost)
// The following line depends on the OP's problem statement
// Is this digit odd (checksum digit being digit zero)
// or is this digit even? (checksum digit being disregarded.)
// Incl-/excl- next line to suit problem statement.
copy /= 10; // dispose of the rightmost "odd" digit
while( copy ) {
int digit = copy % 10; // get "even" digit from the right end.
// Crafted LUT to return correctly "doubled & folded" value of this digit.
sum += "0246813579"[digit] - '0';
copy /= 100; // "shift" value down by 100 (ie: 2 digits of value)
}
Я оставляю это в качестве упражнения для ОП, чтобы определить, что характеризует «четное» и «нечетное» в последовательности цифр «номера» кредитной карты. Считается ли цифра контрольной суммы «1» или нет? Чтобы ОП работал...
Ради интереса, вот цикл после сжатия.
for( /**/; copy; copy /= 100 )
sum += "0246813579"[ copy % 10 ] - '0';
И более продвинутая версия просто вычислит желаемое значение из предоставленного значения:
for( /**/; copy; copy /= 100 )
sum += copy%5 * 2 + copy%10/5;
Для тех, кто (разумно) сомневается в правильности приведенной выше формулы:
#include <stdio.h>
int main( void ) {
for( int i = 0; i < 10; i++ ) {
int j = 2 * i;
printf( "Digit Value: %d ", i );
printf( " x2 = %02d ", j );
printf( "==> %d + %d ", j/10, j%10 );
printf( "==> %d ", j/10 + j%10 );
printf( "==== %d\n", i%5 * 2 + i%10/5 ); // <== Formula version
}
return 0;
}
Digit Value: 0 x2 = 00 ==> 0 + 0 ==> 0 ==== 0
Digit Value: 1 x2 = 02 ==> 0 + 2 ==> 2 ==== 2
Digit Value: 2 x2 = 04 ==> 0 + 4 ==> 4 ==== 4
Digit Value: 3 x2 = 06 ==> 0 + 6 ==> 6 ==== 6
Digit Value: 4 x2 = 08 ==> 0 + 8 ==> 8 ==== 8
Digit Value: 5 x2 = 10 ==> 1 + 0 ==> 1 ==== 1
Digit Value: 6 x2 = 12 ==> 1 + 2 ==> 3 ==== 3
Digit Value: 7 x2 = 14 ==> 1 + 4 ==> 5 ==== 5
Digit Value: 8 x2 = 16 ==> 1 + 6 ==> 7 ==== 7
Digit Value: 9 x2 = 18 ==> 1 + 8 ==> 9 ==== 9
Здесь вам вообще не нужна арифметика с плавающей запятой, журналы или pows. Вы можете использовать % 10
, чтобы извлечь следующую цифру, и / 10
, чтобы отбросить ее. Так:
#include <stdint.h>
#include <stdio.h>
int main(void)
{
uint64_t credit_card = 378282246310005ULL;
int sum = 0;
while (credit_card != 0)
{
credit_card /= 10; // Discard rightmost digit
int double_digit = 2 * (credit_card % 10);
credit_card /= 10;
sum += double_digit % 10;
double_digit /= 10;
sum += double_digit % 10;
}
printf ("%d\n", sum);
}
@ Fe2O3: спасибо, что указали на это! Я исправил это сейчас.
При запросе помощи по отладке, включая минимально воспроизводимый пример.
u
не объявлен в функцииlen
, поэтому этот код не скомпилируется. Похоже, чтоlong li
должно бытьlong u
, как будто этот код является результатом применения OCR к тексту.378282246310005
, вероятно, слишком велик дляlong
в вашей реализации C. Не используйте целочисленные типы для кредитных карт; обрабатывать их как строки символов (символы, которые являются цифрами). Избегайте использованияpow
(поскольку некоторые реализацииpow
неадекватны) или других операций с плавающей запятой для целочисленной арифметики; напишите свою собственную целочисленную процедуру или спроектируйте код, не требующий возведения в степень.