Modified the code in ext2spice.c that prints parameter values;

the former code attempted to determine the precision and generate
an output without unnecessary trailing zeros.  Unfortunately there
were counterexamples that fail to be formatted correctly, as found
by Mark Martin, and which generate output that has roundoff error.
Reimplemented the method using code found on StackOverflow, which
appears to solve the problem more robustly.
This commit is contained in:
Tim Edwards 2025-01-06 11:49:29 -05:00
parent 32138ccbc7
commit 3df2aaaa16
2 changed files with 86 additions and 17 deletions

View File

@ -1 +1 @@
8.3.510
8.3.511

View File

@ -3123,6 +3123,85 @@ FILE *outf;
}
}
/*
* ----------------------------------------------------------------------------
*
* nDecimals() ---
*
* Code taken from discussion at
* https://stackoverflow.com/questions/277772/avoid-trailing-zeroes-in-printf
*
* Modifies value "d" by truncating it to precision "n", writing the result
* into string "s".
*
* ----------------------------------------------------------------------------
*/
void
nDecimals(char *s, double d, int n)
{
int sz;
double d2;
// Allow for negative.
d2 = (d >= 0) ? d : -d;
sz = (d >= 0) ? 0 : 1;
// Add one for each whole digit (0.xx special case).
if (d2 < 1) sz++;
while (d2 >= 1)
{
d2 /= 10.0;
sz++;
}
// Adjust for decimal point and fractionals.
sz += 1 + n;
// Create formatted string then use it.
sprintf(s, "%*.*f", sz, n, d);
}
/*
* ----------------------------------------------------------------------------
*
* morphNumericString() ---
*
* Code taken from discussion at
* https://stackoverflow.com/questions/277772/avoid-trailing-zeroes-in-printf
*
* Remove trailing zeros from the number represented in string "s".
* Assume a precision (defined as precision after the decimal point) of "n".
*
* ----------------------------------------------------------------------------
*/
void
morphNumericString(char *s, int n)
{
char *p;
int count;
p = strchr (s, '.'); // Find decimal point, if any.
if (p != NULL)
{
count = n; // Adjust for more or less decimals.
while (count >= 0) // Maximum decimals allowed.
{
count--;
if (*p == '\0') // If there's less than desired.
break;
p++; // Next character.
}
*p-- = '\0'; // Truncate string.
while (*p == '0') // Remove trailing zeros.
*p-- = '\0';
if (*p == '.') // If all decimals were zeros, remove ".".
*p = '\0';
}
}
/*
* ----------------------------------------------------------------------------
*
@ -3145,6 +3224,7 @@ esSIvalue(file, value)
FILE *file;
float value;
{
char vstr[32];
char suffix = '\0';
int precision;
float avalue;
@ -3195,27 +3275,16 @@ esSIvalue(file, value)
/* Note that "%g" is preferred because it produces more readable
* output. However, it changes the definition of the precision
* from significant digits after the radix to total significant
* digits. Determine the proper precision to use by reading
* back the formatted value and comparing to the original value.
* digits. Using a solution provided in StackOverflow (see above).
*/
for (precision = 3; precision < 9; precision++)
{
int vtrunc, ptrunc;
char ptest[32];
float pvalue;
sprintf(ptest, "%.*g", precision, value);
sscanf(ptest, "%f", &pvalue);
vtrunc = (int)(0.5 + (value * 1e6));
ptrunc = (int)(0.5 + (pvalue * 1e6));
if (vtrunc == ptrunc) break;
}
nDecimals(vstr, (double)value, 5);
morphNumericString(vstr, 5);
if (suffix == '\0')
fprintf(file, "%.*g", precision, value);
fprintf(file, "%s", vstr);
else
fprintf(file, "%.*g%c", precision, value, suffix);
fprintf(file, "%s%c", vstr, suffix);
}
/*