Convert index to sequence from custom alphabet - c

I would like to produce a function which takes an integer x and char array in, and returns a string x steps into the sequence.
For example, consider the alphabet 'abc', which would produce the strings a, b, c, aa, ab, ac, ba, bb, bc, ca, cb, cc, aaa, aab... If the index 0 was passed in, I would expect the output to be 'a'; likewise, if the index 34 was passed in, I would expect the output 'cbb'.
For the alphabet '0123456789' I would expect the strings 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11...
I have written the following thus far, but am getting stuck on cases 21-23, 33-35, 45-47 where the behaviour deviates and I've been staring at this for a number of hours now without a pattern jumping out at me (with respect to the alphabet size and index). At first I didn't notice the issue, using a larger sized alphabet until it created bigger issues further in my program.
I'm not going to pretend the code below is in anyway elegant, following good practice, nor optimised - at this stage I really just want to understand the correct implementation of this pattern and have been changing things all over the place to attempt to resolve the issue. Apologies in advance if the variable names are confusing. Also, is this a common pattern/issue? I have tried to search for similar algorithms but have been unable to find anything with the terms that come to mind.
unsigned long power(int num, int exp)
{
int i;
unsigned long ret = num;
if (exp == 0) return 1;
for (i = 1; i < exp; i++)
{
ret *= num;
}
return ret;
}
unsigned long sumsqr(int base, int exp)
{
unsigned long sum;
for (sum = 0; exp > 0; exp--)
{
sum += power(base, exp);
}
return sum;
}
char * generateStringT(unsigned long index, char * charmap)
{
unsigned long scaler;
unsigned long remainder;
unsigned long divisor;
int base;
int exponent;
int factor;
char * buffer;
char * string;
int i;
buffer = malloc(sizeof(char) * 100);
i = 0;
base = strlen(charmap);
exponent = 0;
divisor = 0;
remainder = index;
while(sumsqr(base, exponent) <= index)
{
exponent++;
}
exponent--;
factor = exponent;
while(factor >= 0)
{
divisor = power(base, factor);
if ((factor > 1) && (exponent > 0))
divisor += power(base, factor-1);
scaler = remainder/divisor;
remainder = remainder - scaler * divisor;
printf("%lu,", scaler);
if ((factor == exponent) && (exponent > 0)) scaler--;
buffer[i++] = charmap[scaler];
factor--;
}
buffer[i++] = '\0';
string = malloc((strlen(buffer) + 1) * sizeof(char));
strcpy(string, buffer);
free(buffer);
return string;
}

What you are trying to do there looks like a base conversion, but actually is slightly different. Any number in any base can be thought as if they have infinitely many preceding zeros (or whatever the least significant digit is at that base) behind the represented number. This is not true in your case.
In your case, you lay importance to the amount of digits on the number you represent, making it slightly more complicated to index them. With bases in maths, it is easy to calculate the index of a represented number in any base b; that is, sum of the rank times the base raised to the power of order for each digit. In your case, the index builds up an additional sum_{k = 1}^{amount.of.digits.on.our.number - 1} base^k. If we subtract that addition from the index, our task becomes rather easy.
That addition can be calculated using your sumsqr function.
Here, I have changed your code just a little, with comments at where I've done changes, which is able to resolve many, just like you expect it to:
// added this
remainder -= sumsqr(base, exponent);
while (factor >= 0)
{
divisor = power(base, factor);
// commented this out
// if ((factor > 1) && (exponent > 0))
// divisor += power(base, factor - 1);
scaler = remainder/divisor;
remainder = remainder - scaler * divisor;
printf("%lu,", scaler);
// commented this out
// if ((factor == exponent) && (exponent > 0))
// scaler--;
buffer[i++] = charmap[scaler];
factor--;
}
I am not exactly sure what you were trying to do with the parts I've commented out. My guess is that you were trying to increase the divisor by that amount of difference I've talked previously, instead of decreasing the index or remainder by that amount.
Hope this helps in any way.

Not a fix (at a glance, your code uses a similar idea -- but more complicated!), but this is the code I used to convert an integer index to an a,b,c-format page number:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *number_alpha (char *dest, int value, char *base)
{
char *ddest = dest, *startdest = dest, swop;
if (value < 0)
{
value = -value;
*dest = '-';
startdest++;
ddest++;
}
value++;
do
{
*ddest = base[((value-1) % strlen(base))];
ddest++;
value = (value-1)/strlen(base);
} while (value > 0);
*ddest = 0;
ddest--;
while (ddest > startdest)
{
swop = *ddest;
*ddest = *startdest;
*startdest = swop;
startdest++;
ddest--;
}
return dest;
}
int main (int argc, char **argv)
{
int number;
char result[256];
if (argc != 3)
{
printf ("usage: [number] [string]\n");
return -1;
}
number = strtol (argv[1], NULL, 10);
number_alpha (result, number, argv[2]);
printf ("%d in 'base' %s yields %s\n", number, argv[2], result);
return 0;
}
It is very similar to the common task 'convert an integer to decimal notation'. By removing the value++ and changing (value-1) twice to just value in number_alpha, you get a bog-standard Int-To-Ascii routine. This one is special because the "wrap" occurs at a different place: for a base of 0123456789, incrementing 9 shows 00, not 10.
Sample outputs:
0 in 'base' abc yields a
34 in 'base' abc yields cbb
34 in 'base' 0123456789 yields 24
-34 in 'base' abc yields -cbb
9 in 'base' 0123456789 yields 9
10 in 'base' 0123456789 yields 00
--
See Translate a column index into an Excel Column Name for a couple of implementations in other languages. They seem to focus on recursive solutions, where mine is linear (for better or worse).

Related

How do I properly convert to any base?

My code compiled successfully, but any time I run the argument ./base.o 42 2, which converts it to base 2 in binary, the output is 0, and anytime I run ./base.o 99 5, the result is 444 repeatings.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main(int argc, char *argv[]) {
int remainder = 0;
int num0 = 0;
int i = 0;
int num1 = atoi(argv[1]);
int num2 = atoi(argv[2]);
int quotient = num1;
while (quotient != 0) {
remainder = num1 % num2;
quotient = quotient / num2;
num0 = (remainder * pow(10, i)) + num0;
i++;
}
printf("%d\n", num0);
return 0;
}
Number base is just for humans to read. A number is a shorthand way of writing polynomial of increasing powers of the radix.
For example, the number 3057 with base (== radix) 10 is:
      3×103  +  0×102  +  5×101  +  7×100
I can totally change the radix:
      B×162  +  F×161  +  1×160
We see that B (11)×16×16 plus F (15)×16 plus 1 is 2816+240+1 = 3057 — the very same number as before.
Text to Integer
Converting from the text representation to a numeric value is easy:
int value = 0;
for (char * s = text_number; *s; s++)
value = (value * radix) + value_of( *s );
That “value of c” thing is just to say that '7' should be transformed to a 7 and things like 'E' should be transformed to 15 (since the ASCII representation of a digit != that digit). This kind of thing is easiest done with a lookup table.
Integer to Text
Going the other way is almost as easy, you just need to print the digits in the right order. It helps to have a string to hold those digits.
char buffer[ N ] = { 0 }; // where N is big enough to hold all the digits
char * s = buffer + N - 1; // start at the back
while (value)
{
*(--s) = digit_for( value % radix );
value /= radix;
}
puts( s );
Again, that “digit for” converts values like 7 to '7' and 15 to 'E'. Again a simple lookup table (the very same one as before, actually) is useful here.
If value is 0 we should just puts( "0" ) and avoid the loop.
Edge Cases
Alas, in computing, life is never as simple as we would like it to be. We need to deal with things like negative numbers and INT_MIN and zeros and stuff.
For what you are doing, I wouldn’t worry about it. But if it matters, you can keep a textual representation of INT_MIN (or whatever data type you are working against) to compare against the input, and assign that value directly if the input matches it.
You can even generate this minimum value representation by converting the largest possible value, adding one to the last character in the string, and slapping a minus sign on the front of it.
Of course, if you are working over an unsigned type, you don’t need to bother.
You still need to be aware of overflow, though. If you get more digits than your resulting type can hold...
And we see where the weeds makes this stuff get kind of tricky. The basic idea is simple, just make sure to handle all the weird edge cases where stuff goes wonky.
There are multiple issues in the code:
the program arguments seem to be expressed in base 10 and the output must be produced in the base corresponding to the second argument.
for this, the conversions int num1 = atoi(argv[1]); and int num2 = atoi(argv[2]); are correct, assuming there are at least 2 arguments: you should test the actual number of arguments received by the program.
but the computation in the loop is incorrect because you use remainder = num1 % num2; instead of remainder = quotient % num2; so you always get the last digit replicated.
your approach is very limited as you can only handle bases up to 10 and numbers with at most 10 digits in the target base.
you should instead store the remainders in an array and output these remainders as digits in reverse order.
Here is a modified version:
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
int remainder[sizeof(int) * CHAR_BIT];
int num1, num2, quotient, i;
const char *digits = "0123456789abcdefghijklmnopqrstuvwxyz";
if (argc > 2) {
num1 = atoi(argv[1]);
num2 = atoi(argv[2]);
if (num2 < 2 || num2 > 36) {
printf("invalid base: %d\n", num2);
return 1;
}
quotient = num1;
i = 0;
/* using a for(;;) loop instead of `while (quotient != 0)` to
produce at least 1 digit for quotient == 0
*/
for (;;) {
/* using abs to handle negative numbers all the way to INT_MIN */
remainder[i++] = abs(quotient % num2);
quotient = quotient / num2;
if (quotient == 0)
break;
}
if (num1 < 0) {
putchar('-');
}
while (i > 0) {
putchar(digits[remainder[--i]]);
}
putchar('\n');
}
return 0;
}
This looked like a fun question to work on, and I see during the time I was refactoring the initial program that other good alternatives have been offered up. To that mix, I offer up this refactored version of the base conversion program.
#include <stdio.h>
#include <stdlib.h>
#define MAX 100
char derive(int digit)
{
char value;
if (digit < 10)
{
value = (char)digit + '0';
}
else
{
switch(digit)
{
case 10:
value = 'A';
break;
case 11:
value = 'B';
break;
case 12:
value = 'C';
break;
case 13:
value = 'D';
break;
case 14:
value = 'E';
break;
case 15:
value = 'F';
break;
default:
value = '*';
break;
}
}
return value;
}
int main(int argc, char *argv[]) {
char cnv[MAX];
int remainder = 0;
int num0 = 0;
int i = 0;
int num1 = atoi(argv[1]);
int num2 = atoi(argv[2]);
int quotient = num1;
if (num2 < 2 || num2 > 16)
{
printf("Currently, only base numbers from 2 to 16 are allowed\n");
return 0;
}
num0 = num2;
for (int j = 0; j < MAX; j++) /* Initialize the character array to terminator values */
{
cnv[j] = '\0';
}
while (1) /* Determine the largest power of the base needed for deriving a converted value */
{
num0 = num0 * num2;
if (num0 > num1)
{
num0 = num0 / num2;
break;
}
}
while (num0 >= num2) { /* Work down from the highest base power to derive the base value */
remainder = num1 % num0;
quotient = quotient / num0;
cnv[i] = derive(quotient);
quotient = remainder;
num0 = num0 / num2;
i++;
}
cnv[i] = derive(remainder); /* Finish by storing the "units" value */
printf("%s\n", cnv);
return 0;
}
A few bits of information to point out as to what I did to make the base conversion function.
Instead of utilizing the power function within the "math.h" include file, the program just keeps multiplying the base value by itself until the largest power of the base is reached that is less than or equal to the number being evaluated.
Once the largest power is determined, that value is used to derive each digit value by performing an integer division and retaining the remainder for subsequent divisions by decreasing powers of the base number.
Each quotient value is converted to its equivalent digit in the base and then stored in a character array and the divisor is divided by the base number until the divisor value is less than the base value.
Once all division has occurred within the "while" loop, the final remainder value is converted to the final digit and placed into the character array and the character array is then printed presenting the converted value.
So instead of working up with the power function, this method derives the needed largest power of the base value needed and works its way back down through the division and decrement process.
Testing out the program against the original values presented in the question produced the following terminal output.
#Vera:~/C_Programs/Console/Base/bin/Release$ ./Base 42 2
101010
#Vera:~/C_Programs/Console/Base/bin/Release$ ./Base 99 5
344
Anyway, you might want to evaluate this refactored version along with the other presented solutions to see if it meets the spirit of your project.

How to print values in reverse without the use of arrays nor pointers in C

I've been working on a code that converts a given number (decimal base) to any other base from 2 to 16.
Clearly, I've come across the issue that the function base_conversion_it (it stands for iterative) prints the values in reverse.
I cannot use arrays nor pointers, and everyone on the internet seems to solve this issue like that. My assignment requires making both an iterative and a recursive function (which I did and works).
void base_conversion_it(unsigned int n, unsigned int b) {
if (n > 0) {
//bases between 2 and 16
if (b >= 2 && b <= 16) {
int r; //r = remainder
int q = 1; //quotient
int num; //saves the remainder
while (q != 0) {
r = n % b;
printf("%X", r);
q = n / b;
n = q;
}
}
}
}
You start converting from the units digit.
Maybe start with the most significant digit instead?
// It's Undefined Behaviour if `b` is outside the range [2...16]
void base_conversion_it(unsigned int n, unsigned int b) {
unsigned highestbase = 1;
while (highestbase * b <= n) highestbase *= b; //possible wrap around and infinite loop
while (highestbase) {
printf("%X", n / highestbase);
n %= highestbase;
highestbase /= b;
}
printf("\n");
}
Sorry missed iterative.
char digits[] = "0123456789ABCDEFGHIJKLMNOP";
void print(unsigned long long val, unsigned base)
{
unsigned long long mask = base;
while(val / mask >= base) mask *= base;
do
{
printf("%c", digits[val / mask]);
val %= mask;
mask /= base;
}while(val);
}
int main(void)
{
print(45654756453, 10); printf("\n");
print(45654756453, 16); printf("\n");
print(45654756453, 24); printf("\n");
print(45654756453, 2); printf("\n");
}
https://godbolt.org/z/W3fGnnhYs
Recursion:
char digits[] = "0123456789ABCDEF";
void print(unsigned long long val, unsigned base)
{
if(base <= 16 && base > 1)
{
if(val >= base) print(val / base, base);
printf("%c", digits[val % base]);
}
}
https://godbolt.org/z/84hYocnjv
If you cannot use either arrays (including strings) or recursion, then I think you need to compute the output digits in most-significant-first order. This is a bit less natural than computing them in the opposite order and reversing the result, but it can be done:
use a loop to find the place value of the most significant non-zero base-b digit of n. For example, check the result of dividing n by successive powers of b until the result is 0, then back off one step.
In a separate loop, read off the base-b digits of n one by one, starting with the one at the discovered most-significant position. For each digit,
Divide the current value of n by the place value pv of the current digit to get a digit value.
Replace n with n % pv.
Be careful to continue all the way down to place value 1, as opposed, say, to stopping when n becomes zero.

C converting 20 digit string to number for IBAN validation

I am doing an IBAN validation in C. For this I have a char* which is something like '2012129431327715102998'.
Now I want to check the IBAN by taken the value modulo 97.
So I want to do 2012129431327715102998 % 97.
I have already tried to convert the char* with strtoull but this gives me an out-of-range error. So my question is: How can I convert this char* to a number where I can do a modulo calculation? Thanks in advance
You can write a custom function for this. Applying the modulo operator on partial sums, you can convert a number of arbitrary length:
#include <stdio.h>
int mod97(const char *s) {
int res = 0;
while (*s >= '0' && *s <= '9') {
res = (res * 10 + (*s++ - '0')) % 97;
}
return res;
}
int main(int argc, char *argv[]) {
for (int i = 1; i < argc; i++) {
printf("%s -> %d\n", argv[i], mod97(argv[i]));
}
return 0;
}
Output:
./mod97 2012129431327715102998
2012129431327715102998 -> 53
This method is simpler and more generic than the one described in the wiki article: computing the modulo 97 of a large number can be achieved by splitting the number in chunks of 9 digits and combining the modulo of these chunks. This splitting is specific to 97 and works because 1000000000 % 97 == 1. The above method works for any modulo value up to INT_MAX / 10.
A simple way without using additional library is to remember that mathematically: mod(a*b, c) == mod(b * mod(a, c), c). So you can process the number in chunks:
// suitable for a 32 bits system, can use 8 for a 64 bits one
#define NB 4
/*********************
* Initial is a string containin only digits representing an arbitrary large number
* div in a number < 10000 (because NB is 4)
* ******************/
int large_mod(char *initial, int div) {
char old[1 + (NB * 2)] = ""; // enough room for a remainder and next chunk
long val;
for (unsigned i=0; i<strlen(initial); i+= NB) {
strncat(old, initial + i, NB); // add the new chunk
val = atol(old) % div; // compute the remainder
sprintf(old, "%ld", val); // keep it for next chunk
// printf("%ld ", val); // uncomment for debugging
}
return (int) val;
}
For 2012129431327715102998 % 97, it gives as expected 53.

Writing IEEE 754-1985 double as ASCII on a limited 16 bytes string

This is a follow-up to my original post. But I'll repeat it for clarity:
As per DICOM standard, a type of floating point can be stored using a Value Representation of Decimal String. See Table 6.2-1. DICOM Value Representations:
Decimal String: A string of characters representing either a fixed
point number or a floating point number. A fixed point number shall
contain only the characters 0-9 with an optional leading "+" or "-"
and an optional "." to mark the decimal point. A floating point number
shall be conveyed as defined in ANSI X3.9, with an "E" or "e" to
indicate the start of the exponent. Decimal Strings may be padded with
leading or trailing spaces. Embedded spaces are not allowed.
"0"-"9", "+", "-", "E", "e", "." and the SPACE character of Default
Character Repertoire. 16 bytes maximum
The standard is saying that the textual representation is fixed point vs. floating point. The standard only refers to how the values are represented within in the DICOM data set itself. As such there is not requirement to load a fixed point textual representation into a fixed-point variable.
So now that this is clear that DICOM standard implicitely recommend double (IEEE 754-1985) for representing a Value Representation of type Decimal String (maximum of 16 significant digits). My question is how do I use the standard C I/O library to convert back this binary representation from memory into ASCII onto this limited sized string ?
From random source on internet, this is non-trivial, but a generally accepted solution is either:
printf("%1.16e\n", d); // Round-trippable double, always with an exponent
or
printf("%.17g\n", d); // Round-trippable double, shortest possible
Of course both expression are invalid in my case since they can produce output much longer than my limited maximum of 16 bytes. So what is the solution to minimizing the loss in precision when writing out an arbitrary double value to a limited 16 bytes string ?
Edit: if this is not clear, I am required to follow the standard. I cannot use hex/uuencode encoding.
Edit 2: I am running the comparison using travis-ci see: here
So far the suggested codes are:
Serge Ballesta
chux
Mark Dickinson
chux
Results I see over here are:
compute1.c leads to a total sum error of: 0.0095729050923877828
compute2.c leads to a total sum error of: 0.21764383725715469
compute3.c leads to a total sum error of: 4.050031792674619
compute4.c leads to a total sum error of: 0.001287056579548422
So compute4.c leads to the best possible precision (0.001287056579548422 < 4.050031792674619), but triple (x3) the overall execution time (only tested in debug mode using time command).
It is trickier than first thought.
Given the various corner cases, it seems best to try at a high precision and then work down as needed.
Any negative number prints the same as a positive number with 1 less precision due to the '-'.
'+' sign not needed at the beginning of the string nor after the 'e'.
'.' not needed.
Dangerous to use anything other than sprintf() to do the mathematical part given so many corner cases. Given various rounding modes, FLT_EVAL_METHOD, etc., leave the heavy coding to well established functions.
When an attempt is too long by more than 1 character, iterations can be saved. E.g. If an attempt, with precision 14, resulted with a width of 20, no need to try precision 13 and 12, just go to 11.
Scaling of the exponent due to the removal of the '.', must be done after sprintf() to 1) avoid injecting computational error 2) decrementing a double to below its minimum exponent.
Maximum relative error is less than 1 part in 2,000,000,000 as with -1.00000000049999e-200. Average relative error about 1 part in 50,000,000,000.
14 digit precision, the highest, occurs with numbers like 12345678901234e1 so start with 16-2 digits.
static size_t shrink(char *fp_buffer) {
int lead, expo;
long long mant;
int n0, n1;
int n = sscanf(fp_buffer, "%d.%n%lld%ne%d", &lead, &n0, &mant, &n1, &expo);
assert(n == 3);
return sprintf(fp_buffer, "%d%0*llde%d", lead, n1 - n0, mant,
expo - (n1 - n0));
}
int x16printf(char *dest, size_t width, double value) {
if (!isfinite(value)) return 1;
if (width < 5) return 2;
if (signbit(value)) {
value = -value;
strcpy(dest++, "-");
width--;
}
int precision = width - 2;
while (precision > 0) {
char buffer[width + 10];
// %.*e prints 1 digit, '.' and then `precision - 1` digits
snprintf(buffer, sizeof buffer, "%.*e", precision - 1, value);
size_t n = shrink(buffer);
if (n <= width) {
strcpy(dest, buffer);
return 0;
}
if (n > width + 1) precision -= n - width - 1;
else precision--;
}
return 3;
}
Test code
double rand_double(void) {
union {
double d;
unsigned char uc[sizeof(double)];
} u;
do {
for (size_t i = 0; i < sizeof(double); i++) {
u.uc[i] = rand();
}
} while (!isfinite(u.d));
return u.d;
}
void x16printf_test(double value) {
printf("%-27.*e", 17, value);
char buf[16+1];
buf[0] = 0;
int y = x16printf(buf, sizeof buf - 1, value);
printf(" %d\n", y);
printf("'%s'\n", buf);
}
int main(void) {
for (int i = 0; i < 10; i++)
x16printf_test(rand_double());
}
Output
-1.55736829786841915e+118 0
'-15573682979e108'
-3.06117209691283956e+125 0
'-30611720969e115'
8.05005611774356367e+175 0
'805005611774e164'
-1.06083057094522472e+132 0
'-10608305709e122'
3.39265065244054607e-209 0
'33926506524e-219'
-2.36818580315246204e-244 0
'-2368185803e-253'
7.91188576978592497e+301 0
'791188576979e290'
-1.40513111051994779e-53 0
'-14051311105e-63'
-1.37897140950449389e-14 0
'-13789714095e-24'
-2.15869805640288206e+125 0
'-21586980564e115'
For finite floating point values the printf() format specifier "%e" well matches
"A floating point number shall be ... with an "E" or "e" to indicate the start of the exponent"
[−]d.ddd...ddde±dd
The sign is present with negative numbers and likely -0.0. The exponent is at least 2 digits.
If we assume DBL_MAX < 1e1000, (safe for IEEE 754-1985 double), then the below works in all cases: 1 optional sign, 1 lead digit, '.', 8 digits, 'e', sign, up to 3 digits.
(Note: the "16 bytes maximum" does not seem to refer to C string null character termination. Adjust by 1 if needed.)
// Room for 16 printable characters.
char buf[16+1];
int n = snprintf(buf, sizeof buf, "%.*e", 8, x);
assert(n >= 0 && n < sizeof buf);
puts(buf);
But this reserves room for the optional sign and 2 to 3 exponent digits.
The trick is the boundary, due to rounding, of when a number uses 2 or uses 3 exponent digits is fuzzy. Even testing for negative numbers, the -0.0 is an issue.
[Edit] Also needed test for very small numbers.
Candidate:
// Room for 16 printable characters.
char buf[16+1];
assert(isfinite(x)); // for now, only address finite numbers
int precision = 8+1+1;
if (signbit(x)) precision--; // Or simply `if (x <= 0.0) precision--;`
if (fabs(x) >= 9.99999999e99) precision--; // some refinement possible here.
else if (fabs(x) <= 1.0e-99) precision--;
int n = snprintf(buf, sizeof buf, "%.*e", precision, x);
assert(n >= 0 && n < sizeof buf);
puts(buf);
Additional concerns:
Some compilers print at least 3 exponent digits.
The maximum number of decimal significant digits for IEEE 754-1985 double needed varies on definition of need, but likely about 15-17. Printf width specifier to maintain precision of floating-point value
Candidate 2: One time test for too long an output
// Room for N printable characters.
#define N 16
char buf[N+1];
assert(isfinite(x)); // for now, only address finite numbers
int precision = N - 2 - 4; // 1.xxxxxxxxxxe-dd
if (signbit(x)) precision--;
int n = snprintf(buf, sizeof buf, "%.*e", precision, x);
if (n >= sizeof buf) {
n = snprintf(buf, sizeof buf, "%.*e", precision - (n - sizeof buf) - 1, x);
}
assert(n >= 0 && n < sizeof buf);
puts(buf);
C library formatter has no direct format for your requirement. At a simple level, if you can accept the waste of characters of the standard %g format (e20 is written e+020: 2 chars wasted), you can:
generate the output for the %.17g format
if it is greater the 16 characters, compute the precision that would lead to 16
generate the output for that format.
Code could look like:
void encode(double f, char *buf) {
char line[40];
char format[8];
int prec;
int l;
l = sprintf(line, "%.17g", f);
if (l > 16) {
prec = 33 - strlen(line);
l = sprintf(line, "%.*g", prec, f);
while(l > 16) {
/* putc('.', stdout);*/
prec -=1;
l = sprintf(line, "%.*g", prec, f);
}
}
strcpy(buf, line);
}
If you really try to be optimal (meaning write e30 instead of e+030), you could try to use %1.16e format and post-process the output. Rationale (for positive numbers):
the %1.16e format allows you to separate the mantissa and the exponent (base 10)
if the exponenent is between size-2 (included) and size (excluded): just correctly round the mantissa to the int part and display it
if the exponent is between 0 and size-2 (both included): display the rounded mantissa with the dot correctly placed
if the exponent is between -1 and -3 (both included): start with a dot, add eventual 0 and fill with rounded mantissa
else use a e format with minimal size for the exponent part and fill with the rounded mantissa
Corner cases:
for negative numbers, put a starting - and add the display for the opposite number and size-1
rounding : if first rejected digit is >=5, increase preceding number and iterate if it was a 9. Process 9.9999999999... as a special case rounded to 10
Possible code:
void clean(char *mant) {
char *ix = mant + strlen(mant) - 1;
while(('0' == *ix) && (ix > mant)) {
*ix-- = '\0';
}
if ('.' == *ix) {
*ix = '\0';
}
}
int add1(char *buf, int n) {
if (n < 0) return 1;
if (buf[n] == '9') {
buf[n] = '0';
return add1(buf, n-1);
}
else {
buf[n] += 1;
}
return 0;
}
int doround(char *buf, unsigned int n) {
char c;
if (n >= strlen(buf)) return 0;
c = buf[n];
buf[n] = 0;
if ((c >= '5') && (c <= '9')) return add1(buf, n-1);
return 0;
}
int roundat(char *buf, unsigned int i, int iexp) {
if (doround(buf, i) != 0) {
iexp += 1;
switch(iexp) {
case -2:
strcpy(buf, ".01");
break;
case -1:
strcpy(buf, ".1");
break;
case 0:
strcpy(buf, "1.");
break;
case 1:
strcpy(buf, "10");
break;
case 2:
strcpy(buf, "100");
break;
default:
sprintf(buf, "1e%d", iexp);
}
return 1;
}
return 0;
}
void encode(double f, char *buf, int size) {
char line[40];
char *mant = line + 1;
int iexp, lexp, i;
char exp[6];
if (f < 0) {
f = -f;
size -= 1;
*buf++ = '-';
}
sprintf(line, "%1.16e", f);
if (line[0] == '-') {
f = -f;
size -= 1;
*buf++ = '-';
sprintf(line, "%1.16e", f);
}
*mant = line[0];
i = strcspn(mant, "eE");
mant[i] = '\0';
iexp = strtol(mant + i + 1, NULL, 10);
lexp = sprintf(exp, "e%d", iexp);
if ((iexp >= size) || (iexp < -3)) {
i = roundat(mant, size - 1 -lexp, iexp);
if(i == 1) {
strcpy(buf, mant);
return;
}
buf[0] = mant[0];
buf[1] = '.';
strncpy(buf + i + 2, mant + 1, size - 2 - lexp);
buf[size-lexp] = 0;
clean(buf);
strcat(buf, exp);
}
else if (iexp >= size - 2) {
roundat(mant, iexp + 1, iexp);
strcpy(buf, mant);
}
else if (iexp >= 0) {
i = roundat(mant, size - 1, iexp);
if (i == 1) {
strcpy(buf, mant);
return;
}
strncpy(buf, mant, iexp + 1);
buf[iexp + 1] = '.';
strncpy(buf + iexp + 2, mant + iexp + 1, size - iexp - 1);
buf[size] = 0;
clean(buf);
}
else {
int j;
i = roundat(mant, size + 1 + iexp, iexp);
if (i == 1) {
strcpy(buf, mant);
return;
}
buf[0] = '.';
for(j=0; j< -1 - iexp; j++) {
buf[j+1] = '0';
}
if ((i == 1) && (iexp != -1)) {
buf[-iexp] = '1';
buf++;
}
strncpy(buf - iexp, mant, size + 1 + iexp);
buf[size] = 0;
clean(buf);
}
}
I think your best option is to use printf("%.17g\n", d); to generate an initial answer and then trim it. The simplest way to trim it is to drop digits from the end of the mantissa until it fits. This actually works very well but will not minimize the error because you are truncating instead of rounding to nearest.
A better solution would be to examine the digits to be removed, treating them as an n-digit number between 0.0 and 1.0, so '49' would be 0.49. If their value is less than 0.5 then just remove them. If their value is greater than 0.50 then increment the printed value in its decimal form. That is, add one to the last digit, with wrap-around and carry as needed. Any trailing zeroes that are created should be trimmed.
The only time this becomes a problem is if the carry propagates all the way to the first digit and overflows it from 9 to zero. This might be impossible, but I don't know for sure. In this case (+9.99999e17) the answer would be +1e18, so as long as you have tests for that case you should be fine.
So, print the number, split it into sign/mantissa strings and an exponent integer, and string manipulate them to get your result.
Printing in decimal cannot work because for some numbers a 17 digit mantissa is needed which uses up all of your space without printing the exponent. To be more precise, printing a double in decimal sometimes requires more than 16 characters to guarantee accurate round-tripping.
Instead you should print the underlying binary representation using hexadecimal. This will use exactly 16 bytes, assuming that a null-terminator isn't needed.
If you want to print the results using fewer than 16 bytes then you can basically uuencode it. That is, use more than 16 digits so that you can squeeze more bits into each digit. If you use 64 different characters (six bits) then a 64-bit double can be printed in eleven characters. Not very readable, but tradeoffs must be made.

Iterating through digits in integer in C

I have an integer like 1191223
and I want to iterate over the digits. I am not sure how to do this in C, is there any easy way to do this?
Thanks.
Forwards, or backwards?
Assuming a positive integer:
unsigned int n = 1191223;
while (n != 0) {
doSomething (n % 10);
n /= 10;
}
…will work smallest to largest, or…
EDIT I'd forgotten all about this non-working solution I had here. Note that Very Smart People™ seem to use the smallest-to-largest iteration consistently (both Linux kernel and GLibC's printf, for example, just iterate backwards) but here's a lousy way to do it if you really don't want to use snprintf for some reason…
int left_to_right (unsigned int n) {
unsigned int digit = 0;
if (0 == n) {
doSomething (0);
} else {
digit = pow(10, 1.0+ floor(log10(n)));
while (digit /= 10) {
doSomething ( (n / digit) % 10 );
}
}
}
I assume that it's very silly to assume that you have log10 and pow but not snprintf, so an alternate plan would be
int left_to_right_fixed_max (unsigned int n) {
unsigned int digit = 1000000000; /* make this very big */
unsigned int n10 = 10 * n;
if (0 == n) {
doSomething (0);
} else {
while (digit > n10) { digit /= 10; }
while (digit /= 10) {
doSomething ( (n / digit) % 10 );
}
}
}
… or, if you really don't have hardware multiply/divide, you can resort to using a table of powers of ten.
int left_to_right (unsigned int n) {
static const unsigned int digit [] =
{ 1,
10,
100,
1000,
10000,
100000,
1000000,
10000000,
100000000,
1000000000 /* make this very big */
};
static const unsigned char max_place = 10;
/* length of the above array */
unsigned char decimal;
unsigned char place;
unsigned char significant = 0; /* boolean */
if (0 == n) {
doSomething (0);
} else {
place = max_place;
while (place--) {
decimal = 0;
while (n >= digit[place]) {
decimal++;
n -= digit[place];
}
if (decimal | significant) {
doSomething (decimal);
significant |= decimal;
}
}
}
}
…which I have adapted from http://www.piclist.com/techref/language/ccpp/convertbase.htm into a somewhat more general-purpose version.
In the following I assume you mean decimal digits (base 10). Probably you are able to adapt the solutions to other numeral systems by substituting the 10s.
Note, that the modulo operation is a tricky thing concerning negative operands. Therefore I have chosen the data type to be an unsigned integer.
If you want to process the least significant digit first, you could try the following untested approach:
uint32_t n = 1191223;
do {
uint32_t digit = n%10;
// do something with digit
}
while (n/=10);
If you prefer to walk through the digits starting from the most significant digit, you could try to adapt the following untested code:
uint32_t n = 1191223;
#define MAX_DIGITS 10 // log10((double)UINT32_MAX)+1
uint32_t div = pow(10, MAX_DIGITS);
// skip the leading zero digits
while ( div && !(n/div) ) div/=10;
if ( !div ) div = 10; // allow n being zero
do {
uint32_t digit = (n/div)%10;
// do something with digit
}
while (div/=10);
You want to iterate over base-10 digits, but an integer has no concept of arabic notation and digits. Convert it to a string first:
int i = 1191223;
char buffer[16];
char *j;
snprintf(buffer, 16, "%i", i);
for ( j = buffer; *j; ++j ) { /* digit is in *j - '0' */ }
You can use sprintf() to convert it into a char array, and then iterate through that, like so (untested, just to get you started):
int a = 1191223;
char arr[16];
int rc = sprintf(arr, "%d", a);
if (rc < 0) {
// error
}
for (int i = 0; i < rc; i++) {
printf("digit %d = %d\n", i, arr[i]);
}
void access_digits(int n)
{
int digit;
if (n < 0) n = -n;
do {
digit = n % 10;
/* Here you can do whatever you
want to do with the digit */
} while ((n/=10) > 0);
}
Something like this:
char data[128];
int digits = 1191223;
sprintf(data, "%d", digits);
int length = strlen(data);
for(int i = 0; i < length; i++) {
// iterate through each character representing a digit
}
Notice that if you use an octal number like 0100 you also need to change the sprintf(data, "%d", digits); to sprintf(data, "%o", digits);.
For my purposes the following short code did the trick.
Having a an integer variable the_integer, and an integer variable sum_of_digits initialized. (line 1) You could do the following:
1) Convert the integer variable to a variable of type string with use of the std::to_string(int) function.
2) Iterate of the characters of the resulting string. for(char& c: str::to_string(the_integer))
3) To convert the characters back to integers use c -'0' . For this solution take a look at the discussion in (Convert char to int in C and C++).
4) .. and adding them the digits together: sum_of_digits += c-'0'
*) you can then print your variables: lines 3 and 4.
int the_integer = 123456789; int sum_of_digits;
for (char& c: std::to_string(the_integer)) {sum_of_digits += c-'0';}
std::cout << "Integer: " << the_integer << std::endl;
std::cout << "Sum of Digits << sum_of_digits << std::endl;
Note that std::to_string() has some notes, please consult the c++ references to see if the code is still relevant for your purposes.
A hackish way is to convert this to string (see strtol) and then reconvert this to a number.
you could use something like character you want - '0'
Off the top of my head: "i % 100000", "i % 100000", ...
A recursive solution would let you start from "i%10".

Resources