Well, I ended up coding my own functions. Tested with gcc and tcc over the entire double range, gives exactly the same result (except for very small very small values, less than 1E-319)
I publish it if someone finds this useful.
Java:
/** * Returns a double with an adhoc formatting, compatible with its C counterpart * * If the absolute value is not too small or too big (thresholdLow-thresholdHigh) * the floating format is used, elsewhere the scientific. * In addition * - trailing zeros in fractional part are removed * - if the value (or mantisa) is integer, a trailing .0 is always included * - the exponent in sci notation is two or three digits * - positive and negative zero returns "0.0" * - special vals: "NaN" "Infinite" "-Infinite" * * Remember to set Locale.setDefault(Locale.US) in your program. * * @param v double * @param formatFloat floating point format, suggested: "%.5f" * @param formatSci scientific format, must use lowercase 'e' : "%.5e" * @param thresholdLow * @param thresholdHigh * @return formatted string */ public static String sprintfDouble(double v, String formatFloat, String formatSci, double thresholdLow, double thresholdHigh) { if(v==0.0) return "0.0"; //dont care about negative zero if(Double.isInfinite(v) || Double.isNaN(v)) return String.format(formatFloat,v); boolean neg = false; if (v < 0) { v = -v; neg = true; } String e = ""; String res; if (v > thresholdLow && v < thresholdHigh) { res = String.format(formatFloat, v); } else { res = String.format(formatSci, v); int sp = res.indexOf('e'); e = res.substring(sp); res = res.substring(0, sp); } if (res.indexOf('.') < 0) res += "."; // add decimal point if not present res = res.replaceAll("0+$", ""); // trim trailing zeros if (res.endsWith(".")) res += "0"; // add traiing zero if nec res += e; if (neg) res = "-" + res; return res; } public static String sprintfDouble5(double v){ return sprintfDouble(v, "%.5f","%.5e",0.01,1000000.0); }
WITH
char * sprintfDouble(char *buf, double v, const char *floatFormat, const char *sciFormat, double thresholdLow, double thresholdHigh) { char *p; char *pd; char *pe; char *buforig; int trimmed; if(v != v) { sprintf(buf,"NaN"); return buf; } if(v == v && (v - v) != 0.0) { sprintf(buf, v < 0 ? "-Infinity" :"Infinity"); return buf; } if(v==0) { sprintf(buf, "0.0"); return buf; } buforig = buf; if(v <0) { v = -v; buf[0] = '-'; buf++; } if( v > thresholdLow && v < thresholdHigh ) { sprintf(buf,floatFormat, v); pe = buf+strlen(buf); pd = (char *) strchr(buf,'.'); if(pd == NULL) { pd = pe; *pe++ = '.'; *pe++ = '0'; *pe = 0; } } else { sprintf(buf,sciFormat, v); pe = (char *)strchr(buf,'e'); pd = (char *)strchr(buf,'.'); if(pd ==NULL) { p= buf+ strlen(buf); while(p>=pe) { *p = *(p-2); p--; } pd = pe; *pe++ = '.'; *pe++ = '0'; *pe = 0; } if( (*(pe+2) == '0' ) && ( strlen(buf) - (pe-buf))==5) { *(pe+2)=*(pe+3); *(pe+3)=*(pe+4); *(pe+4)=*(pe+5); } } trimmed = 0; p=pe-1; while(*p =='0' ) { p--; trimmed++; } if(*p=='.') { trimmed--;
Test code.
Java
static void test() { Locale.setDefault(Locale.US); double start = 1.0; double x=start; for(int i=0;i<367;i++) { System.out.println(sprintfDouble5(x)); x*= -7.0; } x=start; for(int i=0;i<6;i++) { System.out.println(sprintfDouble5(x)); x/= -5; } for(int i=0;i<200;i++) { System.out.println(sprintfDouble5(x)); x/= -42.01; } x=Math.PI*0.0000001; for(int i=0;i<20;i++) { System.out.println(sprintfDouble5(x)); x*=10; } System.out.println(sprintfDouble5(0.0)); System.out.println(sprintfDouble5(-0.0)); System.out.println(sprintfDouble5(0.0/0.0)); }
WITH
void test1() { char buf[64]; double start,x; int i; start = 1.0; x = start; for(i=0;i<367;i++) { printf("%s\n",sprintfDouble5(buf,x)); x *= -7.0; } x = start; for(i=0;i<6;i++) { printf("%s\n",sprintfDouble5(buf,x)); x /= -5; } for(i=0;i<200;i++) { printf("%s\n",sprintfDouble5(buf,x)); x/= -42.01; } x = atan(1.0) * 4 * 0.0000001; /* PI */ for(i=0;i<20;i++) { printf("%s\n",sprintfDouble5(buf,x)); x *= 10; } printf("%s\n",sprintfDouble5(buf,0.0)); printf("%s\n",sprintfDouble5(buf,-0.0)); printf("%s\n",sprintfDouble5(buf,0.0/0.0)); }