Related
I am writing a function
int are_non_negatives(int* start, int n) {
...
}
This function returns 1 if all the next n integers in the array start are non-negative. It returns 0 otherwise.
My question is if there exist tricks that do this as fast as possible (other than looping and checking every single position)?
Dirty/non-portable tricks are fine. I want to know about those as well. Thanks!
One slightly "dirty/non-portable" trick you could leverage for the worst case where all elements need to be checked: In 2's complement int representation, the highest bit is set if and only if the value is negative. So, you could bitwise OR them all and check the highest bit. This could be done using vector instructions to do batches of them at a time, e.g. 8 elements at a time supposing 32-bit int and 256-bit AVX instructions.
Trying to improve performance is a recurring subject for C programmers. Benchmarking alternatives is necessary to test if optimization is useful and worth the effort. This is a naive implementation for reference:
int are_non_negatives_naive(const int *start, int n) {
while (n --> 0) {
if (*start++ >= 0)
return 0;
}
return 1;
}
On current architectures (two's complement) you can combine blocks of entries and test the sign bit much less often. If the arrays are small, you can combine all elements and use a single test:
int are_non_negatives_full(const int *start, int n) {
int combined = 0;
while (n --> 0) {
combined |= *start++;
}
return combined >= 0;
}
If the arrays sizes are varied, you can combine all elements in chunks and test every chunk, allowing for early bail out if a negative value is present:
int are_non_negatives_chunks(const int *start, int n) {
int combined;
for (; n >= 8; n -= 8, start += 8) {
combined = (start[0] | start[1] | start[2] | start[3] |
start[4] | start[5] | start[6] | start[7]);
if (combined < 0)
return 0;
}
combined = 0;
while (n --> 0) {
combined |= *start++;
}
return combined >= 0;
}
Further performance improvements can be achieved by vectorizing the above, either using portable constructions such using 64-bit types or more specific hardware specific intrinsics. The effort might not be necessary is the compiler can vectorize the above algorithms on its own: clang uses SIMD instructions as can be seen with Godbolt Compiler Explorer.
You could check k elements at a time by casting to the largest integer available and crafting the right mask, eg: if you have an array of char, or int8_t, you could check 8 elements at a time.
The mask is crafted to be all zeroes, except for the sign bits.
This way if one of those chars is negative the bitwise AND of the mask and the cast iterator will be > 0
int8_t _i8[8] = { INT8_MIN, INT8_MIN, INT8_MIN, INT8_MIN, INT8_MIN, INT8_MIN, INT8_MIN, INT8_MIN };
uint64_t mask = *(uint64_t *)_i8;
uint64_t *it = (uint64_t *)a;
size_t i = 0;
if (it[i] & mask)
return FALSE;
Of course you should take care of the remainders of n % k, which can be done with a simple switch like this:
size_t i = 0;
switch (n % 8) {
case 0: if (a[i++] < 0) return FALSE;
case 7: if (a[i++] < 0) return FALSE;
case 6: if (a[i++] < 0) return FALSE;
case 5: if (a[i++] < 0) return FALSE;
case 4: if (a[i++] < 0) return FALSE;
case 3: if (a[i++] < 0) return FALSE;
case 2: if (a[i++] < 0) return FALSE;
case 1: if (a[i++] < 0) return FALSE;
}
Here I've made a proof of concept that compares the naive approach, the unrolled approach using the Duff's device, and an unrolled and masked approach that uses both the Duff's device and the masking.
It would require a bit more testing, but seems promising nonetheless.
You can run it here https://onlinegdb.com/SyliIUrSZO
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <limits.h>
#include <math.h>
#include <time.h>
#define CLOCKS_PER_MSEC (CLOCKS_PER_SEC/1000)
#define TRUE (0 == 0)
#define FALSE (!TRUE)
// 8 bit integers
int are_non_negatives_naive_i8(int8_t *a, size_t n)
{
size_t i;
for (i = 0; i < n; ++i)
if (a[i] < 0)
return FALSE;
return TRUE;
}
int are_non_negatives_unrolled_i8(int8_t *a, size_t n)
{
size_t M = (n + 7) / 8;
size_t i = 0;
switch (n % 8) {
case 0: do { if (a[i++] < 0) return FALSE;
case 7: if (a[i++] < 0) return FALSE;
case 6: if (a[i++] < 0) return FALSE;
case 5: if (a[i++] < 0) return FALSE;
case 4: if (a[i++] < 0) return FALSE;
case 3: if (a[i++] < 0) return FALSE;
case 2: if (a[i++] < 0) return FALSE;
case 1: if (a[i++] < 0) return FALSE;
} while (--M > 0);
}
}
int are_non_negatives_unrolled_masked_i8(int8_t *a, size_t n)
{
int8_t _i8[8] = { INT8_MIN, INT8_MIN, INT8_MIN, INT8_MIN, INT8_MIN, INT8_MIN, INT8_MIN, INT8_MIN };
uint64_t mask = *(uint64_t *)_i8;
uint64_t *it = (uint64_t *)a;
size_t i = 0;
switch (n % 8) {
case 0: if (a[i++] < 0) return FALSE;
case 7: if (a[i++] < 0) return FALSE;
case 6: if (a[i++] < 0) return FALSE;
case 5: if (a[i++] < 0) return FALSE;
case 4: if (a[i++] < 0) return FALSE;
case 3: if (a[i++] < 0) return FALSE;
case 2: if (a[i++] < 0) return FALSE;
case 1: if (a[i++] < 0) return FALSE;
}
size_t N = n / 8;
// for (i = 0; i < N; ++i)
// if (it[i] & mask)
// return FALSE;
size_t M = (N + 7) / 8;
switch (N % 8) {
case 0: do { if (it[i++] & mask) return FALSE;
case 7: if (it[i++] & mask) return FALSE;
case 6: if (it[i++] & mask) return FALSE;
case 5: if (it[i++] & mask) return FALSE;
case 4: if (it[i++] & mask) return FALSE;
case 3: if (it[i++] & mask) return FALSE;
case 2: if (it[i++] & mask) return FALSE;
case 1: if (it[i++] & mask) return FALSE;
} while (--M > 0);
}
return TRUE;
}
//----------------------------------------------------------------
// 16 bit integers
int are_non_negatives_naive_i16(int16_t *a, size_t n)
{
size_t i;
for (i = 0; i < n; ++i)
if (a[i] < 0)
return FALSE;
return TRUE;
}
int are_non_negatives_unrolled_i16(int16_t *a, size_t n)
{
size_t M = (n + 7) / 8;
size_t i = 0;
switch (n % 8) {
case 0: do { if (a[i++] < 0) return FALSE;
case 7: if (a[i++] < 0) return FALSE;
case 6: if (a[i++] < 0) return FALSE;
case 5: if (a[i++] < 0) return FALSE;
case 4: if (a[i++] < 0) return FALSE;
case 3: if (a[i++] < 0) return FALSE;
case 2: if (a[i++] < 0) return FALSE;
case 1: if (a[i++] < 0) return FALSE;
} while (--M > 0);
}
}
int are_non_negatives_unrolled_masked_i16(int16_t *a, size_t n)
{
int16_t _i16[4] = { INT16_MIN, INT16_MIN, INT16_MIN, INT16_MIN };
uint64_t mask = *(uint64_t *)_i16;
uint64_t *it = (uint64_t *)a;
size_t i = 0;
switch (n % 8) {
case 0: if (a[i++] < 0) return FALSE;
case 7: if (a[i++] < 0) return FALSE;
case 6: if (a[i++] < 0) return FALSE;
case 5: if (a[i++] < 0) return FALSE;
case 4: if (a[i++] < 0) return FALSE;
case 3: if (a[i++] < 0) return FALSE;
case 2: if (a[i++] < 0) return FALSE;
case 1: if (a[i++] < 0) return FALSE;
}
size_t N = n / 4;
// for (i = 0; i < N; ++i)
// if (it[i] & mask)
// return FALSE;
size_t M = (N + 7) / 8;
switch (N % 8) {
case 0: do { if (it[i++] & mask) return FALSE;
case 7: if (it[i++] & mask) return FALSE;
case 6: if (it[i++] & mask) return FALSE;
case 5: if (it[i++] & mask) return FALSE;
case 4: if (it[i++] & mask) return FALSE;
case 3: if (it[i++] & mask) return FALSE;
case 2: if (it[i++] & mask) return FALSE;
case 1: if (it[i++] & mask) return FALSE;
} while (--M > 0);
}
return TRUE;
}
//----------------------------------------------------------------
// 32 bit integers
int are_non_negatives_naive_i32(int32_t *a, size_t n)
{
size_t i;
for (i = 0; i < n; ++i)
if (a[i] < 0)
return FALSE;
return TRUE;
}
int are_non_negatives_unrolled_i32(int32_t *a, size_t n)
{
size_t M = (n + 7) / 8;
size_t i = 0;
switch (n % 8) {
case 0: do { if (a[i++] < 0) return FALSE;
case 7: if (a[i++] < 0) return FALSE;
case 6: if (a[i++] < 0) return FALSE;
case 5: if (a[i++] < 0) return FALSE;
case 4: if (a[i++] < 0) return FALSE;
case 3: if (a[i++] < 0) return FALSE;
case 2: if (a[i++] < 0) return FALSE;
case 1: if (a[i++] < 0) return FALSE;
} while (--M > 0);
}
}
int are_non_negatives_unrolled_masked_i32(int32_t *a, size_t n)
{
int32_t _i32[2] = { INT32_MIN, INT32_MIN };
uint64_t mask = *(uint64_t *)_i32;
uint64_t *it = (uint64_t *)a;
size_t i = 0;
switch (n % 8) {
case 0: if (a[i++] < 0) return FALSE;
case 7: if (a[i++] < 0) return FALSE;
case 6: if (a[i++] < 0) return FALSE;
case 5: if (a[i++] < 0) return FALSE;
case 4: if (a[i++] < 0) return FALSE;
case 3: if (a[i++] < 0) return FALSE;
case 2: if (a[i++] < 0) return FALSE;
case 1: if (a[i++] < 0) return FALSE;
}
size_t N = n / 2;
// for (i = 0; i < N; ++i)
// if (it[i] & mask)
// return FALSE;
size_t M = (N + 7) / 8;
switch (N % 8) {
case 0: do { if (it[i++] & mask) return FALSE;
case 7: if (it[i++] & mask) return FALSE;
case 6: if (it[i++] & mask) return FALSE;
case 5: if (it[i++] & mask) return FALSE;
case 4: if (it[i++] & mask) return FALSE;
case 3: if (it[i++] & mask) return FALSE;
case 2: if (it[i++] & mask) return FALSE;
case 1: if (it[i++] & mask) return FALSE;
} while (--M > 0);
}
return TRUE;
}
//----------------------------------------------------------------
void fill_array(void *array, size_t type, size_t n, int contains_negatives)
{
size_t i;
switch (type) {
case 1: {
int8_t *a = array;
for (i = 0; i < n; ++i)
a[i] = rand() % INT8_MAX;
break;
}
case 2: {
int16_t *a = array;
for (i = 0; i < n; ++i)
a[i] = rand() % INT16_MAX;
break;
}
case 4: {
int32_t *a = array;
for (i = 0; i < n; ++i)
a[i] = rand() % INT32_MAX;
break;
}
case 8: {
int64_t *a = array;
for (i = 0; i < n; ++i)
a[i] = rand();
break;
}
}
if (contains_negatives) {
int how_many = rand() % n;
for (int i; i < how_many; ++i) {
int j = rand() % n;
switch (type) {
case 1: {
int8_t *a = array;
a[j] *= -1;
break;
}
case 2: {
int16_t *a = array;
a[j] *= -1;
break;
}
case 4: {
int32_t *a = array;
a[j] *= -1;
break;
}
case 8: {
int64_t *a = array;
a[j] *= -1;
break;
}
}
}
}
}
int main()
{
time_t t;
clock_t clock_start;
clock_t clock_end;
clock_t clock_naive = 0;
clock_t clock_unrolled = 0;
clock_t clock_unrolled_masked = 0;
/* Intializes random number generator */
srand((unsigned long) time(&t));
printf("8 bit integers\n");
printf("----------------------------------------------------------------\n");
int8_t *array_i8;
for (int i = 3; i < 7; ++i) {
size_t size = pow(10, i);
array_i8 = malloc(size * sizeof(int8_t));
printf("------------------------------------------------\n");
printf("%lu elements\n", size);
for (int has_neg = 0; has_neg < 2; ++has_neg) {
printf("--------------------------------\n");
printf("Has negatives: %s\n", has_neg ? "yes": "no");
fill_array(array_i8, sizeof(int8_t), size, has_neg);
int neg_naive;
int neg_unrolled;
int neg_unrolled_masked;
clock_start = clock();
neg_naive = are_non_negatives_naive_i8(array_i8, size);
clock_end = clock();
clock_naive = clock_end - clock_start;
clock_start = clock();
neg_unrolled = are_non_negatives_unrolled_i8(array_i8, size);
clock_end = clock();
clock_unrolled = clock_end - clock_start;
clock_start = clock();
neg_unrolled_masked = are_non_negatives_unrolled_masked_i8(array_i8, size);
clock_end = clock();
clock_unrolled_masked = clock_end - clock_start;
if (!has_neg) {
if (!neg_naive)
printf("Error: neg_naive\n");
if (!neg_unrolled)
printf("Error: neg_unrolled\n");
if (!neg_unrolled_masked)
printf("Error: neg_unrolled_masked\n");
}
printf(" clock_naive: %lu\n", clock_naive);
printf(" clock_unrolled: %lu\n", clock_unrolled);
printf("clock_unrolled_masked: %lu\n", clock_unrolled_masked);
printf("--------------------------------\n");
}
printf("------------------------------------------------\n");
free (array_i8);
array_i8 = NULL;
}
printf("----------------------------------------------------------------\n");
printf("16 bit integers\n");
printf("----------------------------------------------------------------\n");
int16_t *array_i16;
for (int i = 3; i < 7; ++i) {
size_t size = pow(10, i);
array_i16 = malloc(size * sizeof(int16_t));
printf("------------------------------------------------\n");
printf("%lu elements\n", size);
for (int has_neg = 0; has_neg < 2; ++has_neg) {
printf("--------------------------------\n");
printf("Has negatives: %s\n", has_neg ? "yes": "no");
fill_array(array_i16, sizeof(int16_t), size, has_neg);
int neg_naive;
int neg_unrolled;
int neg_unrolled_masked;
clock_start = clock();
neg_naive = are_non_negatives_naive_i16(array_i16, size);
clock_end = clock();
clock_naive = clock_end - clock_start;
clock_start = clock();
neg_unrolled = are_non_negatives_unrolled_i16(array_i16, size);
clock_end = clock();
clock_unrolled = clock_end - clock_start;
clock_start = clock();
neg_unrolled_masked = are_non_negatives_unrolled_masked_i16(array_i16, size);
clock_end = clock();
clock_unrolled_masked = clock_end - clock_start;
if (!has_neg) {
if (!neg_naive)
printf("Error: neg_naive\n");
if (!neg_unrolled)
printf("Error: neg_unrolled\n");
if (!neg_unrolled_masked)
printf("Error: neg_unrolled_masked\n");
}
printf(" clock_naive: %lu\n", clock_naive);
printf(" clock_unrolled: %lu\n", clock_unrolled);
printf("clock_unrolled_masked: %lu\n", clock_unrolled_masked);
printf("--------------------------------\n");
}
printf("------------------------------------------------\n");
free (array_i16);
array_i16 = NULL;
}
printf("----------------------------------------------------------------\n");
printf("32 bit integers\n");
printf("----------------------------------------------------------------\n");
int32_t *array_i32;
for (int i = 3; i < 7; ++i) {
size_t size = pow(10, i);
array_i32 = malloc(size * sizeof(int32_t));
printf("------------------------------------------------\n");
printf("%lu elements\n", size);
for (int has_neg = 0; has_neg < 2; ++has_neg) {
printf("--------------------------------\n");
printf("Has negatives: %s\n", has_neg ? "yes": "no");
fill_array(array_i32, sizeof(int32_t), size, has_neg);
int neg_naive;
int neg_unrolled;
int neg_unrolled_masked;
clock_start = clock();
neg_naive = are_non_negatives_naive_i32(array_i32, size);
clock_end = clock();
clock_naive = clock_end - clock_start;
clock_start = clock();
neg_unrolled = are_non_negatives_unrolled_i32(array_i32, size);
clock_end = clock();
clock_unrolled = clock_end - clock_start;
clock_start = clock();
neg_unrolled_masked = are_non_negatives_unrolled_masked_i32(array_i32, size);
clock_end = clock();
clock_unrolled_masked = clock_end - clock_start;
if (!has_neg) {
if (!neg_naive)
printf("Error: neg_naive\n");
if (!neg_unrolled)
printf("Error: neg_unrolled\n");
if (!neg_unrolled_masked)
printf("Error: neg_unrolled_masked\n");
}
printf(" clock_naive: %lu\n", clock_naive);
printf(" clock_unrolled: %lu\n", clock_unrolled);
printf("clock_unrolled_masked: %lu\n", clock_unrolled_masked);
printf("--------------------------------\n");
}
printf("------------------------------------------------\n");
free (array_i32);
array_i32 = NULL;
}
printf("----------------------------------------------------------------\n");
}
Any suggestions or fixes are more than welcome
Note that this solution is not standard and depends on the alignment of the data types.
Albeit it'll usually work, you should check it with something like this
#include <stdio.h>
#include <stdint.h>
#include <stdalign.h>
int main()
{
printf(
"alignof(int8_t ): %lu\n"
"alignof(int16_t): %lu\n"
"alignof(int32_t): %lu\n"
"alignof(int64_t): %lu\n",
alignof(int8_t),
alignof(int16_t),
alignof(int32_t),
alignof(int64_t)
);
return 0;
}
I suspect that this won’t work for you based on the context of your question, but in case your code allows for you to capture array operations (initialization, insertion, modification, deletion), you can coherently maintain a separate list of all elements with negative values. If you have the freedom to do that, it would just be a space-speed tradeoff.
I have an unsigned char array which contains hex bytes like below:
unsigned char array[255];
array[0] = 'F';
array[1] = 'F';
array[2] = 'E';
array[3] = '2';
array[4] = 'A';
array[5] = 'A';
array[6] = 'C';
array[7] = 'C';
I want to merge them so that it becomes:
array[0] = "FF"
array[1] = "E2"
array[2] = "AA"
array[3] = "CC"
array[0] = '\xFF';
array[1] = '\xE2';
array[2] = '\xAA';
array[3] = '\xCC';
I have tried using sprintf but then I do not know how to specify index number in it. Any help.?
So, you want to convert your string, made of hexadecimal characters, into an array of bytes, right? Know your data.
sprintf() will not help you, since it produces strings. Instead, you will need to extract the 'value' of each hexa character and use it to calculate the value of your bytes.
So, let's create a helper function to convert a hexadecimal character to its integer value (or -1 if it is invalid). We will use the characters' ASCII values and the fact that character ranges are contiguous in the ASCII table
int char2hexa(unsigned char c)
{
if(c >= '0' && c <= '9') {
return (c - '0'); /* will return 0-9 */
} else if(c >= 'A' && c <= 'F') {
return (c - 'A') + 10; /* will return 10-15 */
} else if(c >= 'a' && c <= 'f') {
return (c - 'a') + 10; /* will return 10-15 */
} else {
return -1;
}
}
Now a byte will be constructed from two hexa values by using one as the upper nibble (multiplied by 16 or shifted left by 4) and the other as a lower nibble, so let's have a function for that:
unsigned char hexvals2byte(int upper, int lower)
{
return (upper * 16 + lower);
}
and put the pieces together. I will assume that:
you know the length of your input data
the length is even (you need two characters per byte)
you want to put the result in the same array
Here comes.
#include <stdio.h>
#include <stdlib.h>
unsigned char array[255];
array[0] = 'F';
array[1] = 'F';
array[2] = 'E';
array[3] = '2';
array[4] = 'A';
array[5] = 'A';
array[6] = 'C';
array[7] = 'C';
unsigned length = 8;
int upper, lower;
for(int i = 0; i < length; i+=2) {
upper = char2hexa(array[i]);
lower = char2hexa(array[i+1]);
if(upper < 0 || lower < 0) {
/* handle input data format error */
fprintf(stderr, "ERROR: Cannot decode hexa values '%c%c'\n", array[i], array[i+1]);
exit(EXIT_FAILURE);
}
array[i/2] = hexvals2byte(upper, lower);
}
So you need a result array of unsigned char result[128][3], then assign the part results, grouping 2 source elements into one result sub-element:
unsigned char result[128][3] = { 0 };
int i;
for (i = 0; i < 255; ++i)
{
result[i/2][i%2] = array[i];
}
The reason for size 3 is, that you need 2 characters and one zero-delimiter to form a string.
An easy way to convert a digit to number is to subtract '0' from it:
char digit = '3';
int number = digit - '0'; /* number = 3 */
This works only for digits (digit >= '0' && digit <= '9'), for hexadecimal digits ('A', 'B', etc.) you have to do a little more job:
unsigned char result[127];
int i;
unsigned char current;
unsigned char calc_diff(unsigned char digit) {
if(digit >= '0' && digit <= '9')
return '0';
else if(digit >= 'A' && digit <= 'F')
return 'A' - 10;
else if(digit >= 'a' && digit <= 'f')
return 'a' - 10;
else
return 0; // handle invalid digit
}
for(i = 0; i < 128; ++i) {
current = array[2 * i];
result[i] = (current - calc_diff(current)) << 4;
current = array[(2 * i) + 1];
result[i] |= current - calc_diff(current);
}
You want to convert characters to their hexadecimal value and combine them in pairs.
Here is a simple program to illustrate how you can do this:
#include <stdio.h>
#include <string.h>
static int xdigit(unsigned char c) {
/* this method is inefficient but works for all encodings */
static const char xdigits[] = "abcdef0123456789ABCDEF";
const char *p = memchr(xdigits, c, 22);
return p ? (p - xdigits + 10) & 15 : -1;
}
int main(void) {
unsigned char array[255];
while (scanf("%254s", array) == 1) {
int i, j, d, d2 = 0;
for (i = j = 0; array[i] != '\0'; i++) {
d = xdigit(array[i]);
if (d < 0) {
printf("invalid hexadecimal digit: %c\n", array[i]);
break;
}
d2 = (d2 << 4) | d;
if (i & 1) {
array[j++] = (unsigned char)d2;
d2 = 0;
}
}
array[j] = '\0';
printf("converted array: %s\n", array);
}
return 0;
}
Here is a more elaborate version, with an separate conversion function and more explicit output:
#include <ctype.h>
#include <stdio.h>
#include <string.h>
static int xdigit(unsigned char c) {
switch (c) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
return c - '0';
case 'A': case 'a':
return 10;
case 'B': case 'b':
return 11;
case 'C': case 'c':
return 12;
case 'D': case 'd':
return 13;
case 'E': case 'e':
return 14;
case 'F': case 'f':
return 15;
default:
return -1;
}
}
int xconvert(unsigned char *dest, const unsigned char *src, int len) {
int i, j, d, d2 = 0;
for (i = j = 0; i < len; i++) {
d = xdigit(src[i]);
if (d < 0) {
printf("invalid hexadecimal digit: %c\n", src[i]);
return -1;
}
d2 = (d2 << 4) | d;
if (i & 1) {
dest[j++] = (unsigned char)d2;
d2 = 0;
}
}
if (i & 1) {
printf("missing trailing digit\n");
return -1;
}
return j;
}
int main(void) {
unsigned char array[255];
int i, len, c;
while (scanf("%254s", array) == 1) {
len = xconvert(array, array, strlen((char *)array));
if (len >= 0) {
printf("converted array: \"");
for (i = 0; i < len; i++) {
c = array[i];
if (isprint(c)) {
putchar(c);
} else {
printf("\\x%02X", c);
}
}
printf("\"\n");
}
}
return 0;
}
my stab at it. Here you have to know the size of the array. in your case 255
//counters and array's
int first = 0;
int second = 0;
int count = 0;
char foo[8] = {'F', 'F', 'E', '2', 'A', 'A', 'C', 'C'};
//array half the size of the first one.
char *done[4];
//loop through first array
while (first <= 7)
{
//if its the first letter
if (second == 0)
{
//allocate enough mem to second arr
done[count] = (char *)malloc(sizeof(char *) * 3);
//assaign the first letter
done[count][0] = foo[first];
//indicate the next step for the second letter
second = 1;
}
//if its the second letter
else if (second == 1)
{
//assign second letter
done[count][1] = foo[first];
//null the string
done[count][2] = '\0';
//increase posistion index for the second arr
count++;
//indicate nexxt step is a the first letter of the next step
second = 0;
}
//increment the index for the first arr
first++;
}
Given that the data is in ASCII format and you want to merge it into a raw binary format, then:
for(size_t i=0; i<n; i++)
{
array[i] = to_int(array[i]);
}
where to_int() is your custom routine for converting from hexadecimal ASCII to integer. That is, if digit subtract with '0', else if upper-case letter subtract with 'A' and add 0xA.
Then after that, merge the items:
for(size_t i=0; i<n; i+=2)
{
array[i] = (unsigned int)array[i]<<4 | array[i+1];
}
I suppose you mix up an 8 bit value (e.g. 0x0F, which is 15 in decimal) with a character value like 'F' (which corresponds to 70 in ASCII format) with a string literal "FF" (which corresponds to a pointer to a sequence of the three character values {'F','F','\0'}.
From the context you present it seems that you mean 8 bit values, which are represented by data type unsigned char.
Given that, the code could look as follows:
unsigned char array[255] = { 0xF,0xE,0x2,0xA,0xA,0xC,0xC };
int target=0;
for (int i=0; i<254; i+=2) {
array[target] = (array[i] << 4) + array[i+1];
target++;
}
Maybe there are uncertain things but perhaps you want to do this.
#include <stdio.h>
int main(void){
unsigned char array[255] = {//or char -> hex byte
'\xF', '\xF', '\xE', '\x2',
'\xA', '\xA', '\xC', '\xC',
};
int len = 8;
for(int i = 0, j = 0; i < len; i += 2){
array[j++] = (array[i] << 4) | array[i+1];
}
len = len / 2;
for(int i = 0; i < len; i++){
printf("%02hhX", array[i]);//FFE2AACC
}
printf("\n");
}
When the data held first is a hex character
#include <stdio.h>
#include <ctype.h>
int main(void){
unsigned char array[255] = {
'F', 'F', 'E', '2',
'A', 'A', 'C', 'C',
};
int len = 8;
for(int i = 0, j = 0; i < len; i++){
if(isxdigit(array[i])){//maybe redundant
if(isdigit(array[i])){
array[i] -= '0';
} else {
array[i] -= isupper(array[i]) ? 'A' : 'a';
array[i] += 10;
}
} else {
fprintf(stderr, "invalid data %c\n", array[i]);
return -1;
}
}
for(int i = 0, j = 0; i < len; i += 2){
array[j++] = (array[i] << 4) | array[i+1];
}
len = len / 2;
for(int i = 0; i < len; i++){
printf("%02hhX", array[i]);
}
printf("\n");
}
I need to write a C program that has a binary number on input and the same number in MLT-3 code on output (voltages +, 0, - accordingly). The voltages only change at '1', at '0' they are unchanged:
10010111
+++00-0+
How do I 'roll' these values?
#include <stdio.h>
int main()
{
char states[4] = { '+', '0', '-', '0' };
int index = 3;
int b = 0x97; // OP example
int i;
for (i=0; i<8; i++) {
if (b & 0x80)
index = (index + 1) % 4;
printf ("%c", states[index]);
b <<= 1;
}
printf("\n");
return 0;
}
Program output
+++00-0+
A basic flow indicated by the code.
#include <stdio.h>
int main(void){
int direction = 1;//1: upto, -1: downto
int state = 0;
char *seq = "10010111";
while(*seq){
if(*seq == '1'){
state += direction;
if(state == 1 || state == -1)
direction = -direction;//reversal of direction
}
switch(state){//putchar("-0+"[state+1]);
case 1: putchar('+');break;
case 0: putchar('0');break;
case -1: putchar('-');break;
}
++seq;
}
return 0;
}
In the main function of C:
void main(int argc, char **argv)
{
// do something here
}
In the command line, we will type any number for example 1 or 2 as input, but it will be treated as char array for the parameter of argv, but how to make sure the input is a number, in case people typed hello or c?
Another way of doing it is by using isdigit function. Below is the code for it:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#define MAXINPUT 100
int main()
{
char input[MAXINPUT] = "";
int length,i;
scanf ("%s", input);
length = strlen (input);
for (i=0;i<length; i++)
if (!isdigit(input[i]))
{
printf ("Entered input is not a number\n");
exit(1);
}
printf ("Given input is a number\n");
}
You can use a function like strtol() which will convert a character array to a long.
It has a parameter which is a way to detect the first character that didn't convert properly. If this is anything other than the end of the string, then you have a problem.
See the following program for an example:
#include <stdio.h>
#include <stdlib.h>
int main( int argc, char *argv[]) {
int i;
long val;
char *next;
// Process each argument given.
for (i = 1; i < argc; i++) {
// Get value with failure detection.
val = strtol (argv[i], &next, 10);
// Check for empty string and characters left after conversion.
if ((next == argv[i]) || (*next != '\0')) {
printf ("'%s' is not valid\n", argv[i]);
} else {
printf ("'%s' gives %ld\n", argv[i], val);
}
}
return 0;
}
Running this, you can see it in operation:
pax> testprog hello "" 42 12.2 77x
'hello' is not valid
'' is not valid
'42' gives 42
'12.2' is not valid
'77x' is not valid
Using scanf is very easy, this is an example :
if (scanf("%d", &val_a_tester) == 1) {
... // it's an integer
}
A self-made solution:
bool isNumeric(const char *str)
{
while(*str != '\0')
{
if(*str < '0' || *str > '9')
return false;
str++;
}
return true;
}
Note that this solution should not be used in production-code, because it has severe limitations. But I like it for understanding C-Strings and ASCII.
Using fairly simple code:
int i;
int value;
int n;
char ch;
/* Skip i==0 because that will be the program name */
for (i=1; i<argc; i++) {
n = sscanf(argv[i], "%d%c", &value, &ch);
if (n != 1) {
/* sscanf didn't find a number to convert, so it wasn't a number */
}
else {
/* It was */
}
}
I was struggling with this for awhile, so I thought I'd just add my two cents:
1) Create a separate function to check if an fgets input consists entirely of numbers:
int integerCheck(){
char myInput[4];
fgets(myInput, sizeof(myInput), stdin);
int counter = 0;
int i;
for (i=0; myInput[i]!= '\0'; i++){
if (isalpha(myInput[i]) != 0){
counter++;
if(counter > 0){
printf("Input error: Please try again. \n ");
return main();
}
}
}
return atoi(myInput);
}
The above starts a loop through every unit of an fgets input until the ending NULL value. If it comes across a letter or an operator, it adds "1" to the int "counter" which is initially set to 0. Once the counter becomes greater than 0, the nested if statement instructs the loop to print an error message & then restart the program. When the loops completes, if int 'counter' is still the value of 0, it returns the initially inputted integer to be used in the main function ...
2) the main function would be:
int main(void){
unsigned int numberOne;
unsigned int numberTwo;
numberOne = integerCheck();
numberTwo = integerCheck();
return numberOne*numberTwo;
}
Assuming both integers are inputted correctly, the example provided will yield the result of int "numberOne" multiplied by int "numberTwo". The program will repeat for however long it takes to get two properly inputted integers.
if (sscanf(command_level[2], "%f%c", &check_f, &check_c)!=1)
{
is_num=false;
}
else
{
is_num=true;
}
if(sscanf(command_level[2],"%f",&check_f) != 1)
{
is_num=false;
}
how about this?
This works for me
#include <string.h>
int isNumber(char *n) {
int i = strlen(n);
int isnum = (i>0);
while (i-- && isnum) {
if (!(n[i] >= '0' && n[i] <= '9')) {
isnum = 0;
}
}
return isnum;
}
e.g.:
printf("%i\n", isNumber("12")); // 1
printf("%i\n", isNumber("033")); // 1
printf("%i\n", isNumber("0")); // 1
printf("%i\n", isNumber("")); // 0
printf("%i\n", isNumber("aaa")); // 0
printf("%i\n", isNumber("\n")); // 0
printf("%i\n", isNumber("a0\n")); // 0
The C library function int isdigit(int c) checks if the passed character is a decimal digit character.
#include <stdio.h>
#include <ctype.h>
int main () {
int var1 = 'h';
int var2 = '2';
if( isdigit(var1) ) {
printf("var1 = |%c| is a digit\n", var1 );
} else {
printf("var1 = |%c| is not a digit\n", var1 );
}
if( isdigit(var2) ) {
printf("var2 = |%c| is a digit\n", var2 );
} else {
printf("var2 = |%c| is not a digit\n", var2 );
}
return(0);
}
the result is :
var1 = |h| is not a digit
var2 = |2| is a digit
The sscanf() solution is better in terms of code lines. My answer here is a user-build function that does almost the same as sscanf(). Stores the converted number in a pointer and returns a value called "val". If val comes out as zero, then the input is in unsupported format, hence conversion failed. Hence, use the pointer value only when val is non-zero.
It works only if the input is in base-10 form.
#include <stdio.h>
#include <string.h>
int CONVERT_3(double* Amt){
char number[100];
// Input the Data
printf("\nPlease enter the amount (integer only)...");
fgets(number,sizeof(number),stdin);
// Detection-Conversion begins
int iters = strlen(number)-2;
int val = 1;
int pos;
double Amount = 0;
*Amt = 0;
for(int i = 0 ; i <= iters ; i++ ){
switch(i){
case 0:
if(number[i]=='+'){break;}
if(number[i]=='-'){val = 2; break;}
if(number[i]=='.'){val = val + 10; pos = 0; break;}
if(number[i]=='0'){Amount = 0; break;}
if(number[i]=='1'){Amount = 1; break;}
if(number[i]=='2'){Amount = 2; break;}
if(number[i]=='3'){Amount = 3; break;}
if(number[i]=='4'){Amount = 4; break;}
if(number[i]=='5'){Amount = 5; break;}
if(number[i]=='6'){Amount = 6; break;}
if(number[i]=='7'){Amount = 7; break;}
if(number[i]=='8'){Amount = 8; break;}
if(number[i]=='9'){Amount = 9; break;}
default:
switch(number[i]){
case '.':
val = val + 10;
pos = i;
break;
case '0':
Amount = (Amount)*10;
break;
case '1':
Amount = (Amount)*10 + 1;
break;
case '2':
Amount = (Amount)*10 + 2;
break;
case '3':
Amount = (Amount)*10 + 3;
break;
case '4':
Amount = (Amount)*10 + 4;
break;
case '5':
Amount = (Amount)*10 + 5;
break;
case '6':
Amount = (Amount)*10 + 6;
break;
case '7':
Amount = (Amount)*10 + 7;
break;
case '8':
Amount = (Amount)*10 + 8;
break;
case '9':
Amount = (Amount)*10 + 9;
break;
default:
val = 0;
}
}
if( (!val) | (val>20) ){val = 0; break;}// val == 0
}
if(val==1){*Amt = Amount;}
if(val==2){*Amt = 0 - Amount;}
if(val==11){
int exp = iters - pos;
long den = 1;
for( ; exp-- ; ){
den = den*10;
}
*Amt = Amount/den;
}
if(val==12){
int exp = iters - pos;
long den = 1;
for( ; exp-- ; ){
den = den*10;
}
*Amt = 0 - (Amount/den);
}
return val;
}
int main(void) {
double AM = 0;
int c = CONVERT_3(&AM);
printf("\n\n%d %lf\n",c,AM);
return(0);
}
I have to output each string in 'ZOJ' order. the length of string is from 1 to 100. The 'E' means the end of the input.
I test my program with gcc. I try so many examples and the output is fine. However, the online judge turn out to be wrong answer.
Example:
Input:
ZZOOOJJJ
ZZZZOOOOOJJJ
ZOOOJJ
E
Output:
ZOJZOJOJ
ZOJZOJZOJZOO
ZOJOJO
Thank you!
Here is my code.
#include<stdio.h>
#include<string.h>
#define MAX_SIZE 100
main() {
char data[MAX_SIZE];
int len;
int z,o,j;
int i;
char c;
int z_index, o_index,j_index;
while(scanf("%s", data)!= EOF && (strlen(data)>=1 && data[0] != 'E')) {
len = strlen(data);
z = -1;
o = -1;
j = -1;
z_index = -1;
o_index = -1;
j_index = -1;
for(i=0; i<len; i++) {
c = data[i];
switch(c) {
case 'Z':
if(z==-1)
z_index=i,z=i;
else
z = i;
break;
case 'O':
if(o==-1)
o_index=i,o=i;
else
o = i;
break;
case 'J':
if(j==-1)
j_index=i,j=i;
else
j = i;
break;
}
}
while(z_index!=-1 || o_index!=-1 || j_index!=-1) {
if(z_index!=-1 && z_index<=z)
putchar('Z'), z_index++;
else
z_index = -1;
if(o_index!=-1 && o_index<=o)
putchar('O'), o_index++;
else
o_index = -1;
if(j_index!=-1 && j_index<=j)
putchar('J'), j_index++;
else
j_index = -1;
}
putchar('\n');
}
}
You should really make your function int main() rather than just main() therefore you should then return 0 at the end.
#include<stdio.h>
#include<string.h>
#define MAX_SIZE 100
int main() {
char data[MAX_SIZE];
int len;
int z,o,j;
int i;
char c;
int z_index, o_index,j_index;
while(scanf("%s", data)!= EOF && (strlen(data)>=1 && data[0] != 'E')) {
len = strlen(data);
z = -1;
o = -1;
j = -1;
z_index = -1;
o_index = -1;
j_index = -1;
for(i=0; i<len; i++) {
c = data[i];
switch(c) {
case 'Z':
if(z==-1)
z_index=i,z=i;
else
z = i;
break;
case 'O':
if(o==-1)
o_index=i,o=i;
else
o = i;
break;
case 'J':
if(j==-1)
j_index=i,j=i;
else
j = i;
break;
}
}
while(z_index!=-1 || o_index!=-1 || j_index!=-1) {
if(z_index!=-1 && z_index<=z)
putchar('Z'), z_index++;
else
z_index = -1;
if(o_index!=-1 && o_index<=o)
putchar('O'), o_index++;
else
o_index = -1;
if(j_index!=-1 && j_index<=j)
putchar('J'), j_index++;
else
j_index = -1;
}
putchar('\n');
}
return 0;
}