[m-rev.] for review: fix float_to_string so that roundtripping works

Peter Ross pro at missioncriticalit.com
Thu Nov 21 03:57:11 AEDT 2002


On Thu, Nov 21, 2002 at 02:26:50AM +1100, Fergus Henderson wrote:
> On 20-Nov-2002, Peter Ross <pro at missioncriticalit.com> wrote:
> > Estimated hours taken: 8
> > Branches: main
> > 
> > Fix the I/O routines for floats so that can roundtrip.
> > 
> > library/string.m:
> >     Ensure that string__float_to_string returns a float that is
> >     round-trippable.  On the C backend we do that by starting at the min
> >     precision required and increasing it until the float roundtrips.  On
> >     the IL backend the R flag guarantees that a double will be
> >     round-trippable, so we just use that.
> >     Change string__to_float so that it uses the format string for the
> >     precision float that we are using, and export this predicate for use
> >     by the trace system.
> >     Delete the unused string__float_to_f_string.
> > 
> > library/io.m:
> >     Call string__float_to_string to determine the float to output so
> >     that the float output is round-trippable.
> 
> This change will decrease efficiency of writing out floats, unfortunately,
> for two reasons:
> 
> 	(1) rather than just formatting the float once,
> 	    we format the float,
> 	    then scan it back in again,
> 	    then check if the two are equal,
> 	    and in some cases we may have to repeat the whole process
> 	    two or three times.
> 	    
> 	(2) io__write_float allocates space on the Mercury heap
> 	    and does not reclaim it
> 
> (1) is acceptable, since it seems to be the minimum necessary to fix the bug.
> But I'd rather avoid (2).
> 
> For formatting floats with "%.*g" format, a dynamically sized
> heap-allocated buffer is overkill, since the size needed is
> bounded by the precision plus a small constant; a fixed-size,
> stack-allocated buffer should be sufficient.
> 
I have to go to my french lesson now, so I will send you the interdiff
that is bootchecking now.

All I have identified is that I need to add a comment to
ML_sprintf_float to describe what it does.

diff -u library/io.m library/io.m
--- library/io.m	20 Nov 2002 10:43:29 -0000
+++ library/io.m	20 Nov 2002 16:25:52 -0000
@@ -4405,6 +4405,18 @@
 ").
 
 :- pragma foreign_proc("C",
+	io__write_float(Val::in, IO0::di, IO::uo),
+		[may_call_mercury, promise_pure, tabled_for_io, thread_safe],
+"
+	char buf[ML_SPRINTF_FLOAT_BUF_SIZE];
+	ML_sprintf_float(buf, Val);
+	if (ML_fprintf(mercury_current_text_output, ""%s"", buf) < 0) {
+		mercury_output_error(mercury_current_text_output);
+	}
+	MR_update_io(IO0, IO);
+").
+
+:- pragma foreign_proc("C",
 	io__write_byte(Byte::in, IO0::di, IO::uo),
 		[may_call_mercury, promise_pure, tabled_for_io, thread_safe],
 "
@@ -4659,6 +4671,19 @@
 }").
 
 :- pragma foreign_proc("C",
+	io__write_float(Stream::in, Val::in, IO0::di, IO::uo),
+		[may_call_mercury, promise_pure, tabled_for_io, thread_safe],
+"{
+	MercuryFile *stream = (MercuryFile *) Stream;
+	char buf[ML_SPRINTF_FLOAT_BUF_SIZE];
+	ML_sprintf_float(buf, Val);
+	if (ML_fprintf(stream, ""%s"", buf) < 0) {
+		mercury_output_error(stream);
+	}
+	MR_update_io(IO0, IO);
+}").
+
+:- pragma foreign_proc("C",
 	io__write_byte(Stream::in, Byte::in, IO0::di, IO::uo),
 		[may_call_mercury, promise_pure, tabled_for_io, thread_safe],
 "{
diff -u library/string.m library/string.m
--- library/string.m	20 Nov 2002 13:46:46 -0000
+++ library/string.m	20 Nov 2002 16:42:19 -0000
@@ -1826,45 +1826,21 @@
 :- pragma foreign_proc("C",
 	string__float_to_string(Flt::in, Str::uo),
 		[will_not_call_mercury, promise_pure, thread_safe], "{
-#ifdef MR_USE_SINGLE_PREC_FLOAT
-	#define MIN_PRECISION	7
-	const char *format = ""%f"";
-#else
-	#define MIN_PRECISION	15
-	const char *format = ""%lf"";
-#endif
-	int i = MIN_PRECISION;
-	MR_Float round;
-
-		/*
-		 * Round-trip the float to ensure the precision was 
-		 * sufficient, and if not then try with the next precision.
-		*/
-	Str = MR_make_string(MR_PROC_LABEL, ""%#.*g"", i, Flt);
-	sscanf(Str, format, &round);
-
-	while (round != Flt) {
-		i++;
-		Str = MR_make_string(MR_PROC_LABEL, ""#%.*g"", i, Flt);
-		sscanf(Str, format, &round);
-	}
+	char buf[ML_SPRINTF_FLOAT_BUF_SIZE];
+	ML_sprintf_float(buf, Flt);
+	MR_make_aligned_string(Str, buf);
 }").
 
-:- pragma foreign_proc("il",
+:- pragma foreign_proc("C#",
 	string__float_to_string(FloatVal::in, FloatString::uo),
-	[will_not_call_mercury, promise_pure, thread_safe, max_stack_size(1)], "
-
-	ldloca	'FloatVal'
-	ldstr	""R""
+	[will_not_call_mercury, promise_pure, thread_safe], "
 
 		// The R format string prints the double out such that it
 		// can be round-tripped.
 		// XXX According to the documentation it tries the 15 digits of
 		// precision, then 17 digits skipping 16 digits of precision.
 		// unlike what we do for the C backend.
-	call instance string [mscorlib]System.Double::ToString(string)
-
-	stloc	'FloatString'
+	FloatString = FloatVal.ToString(""R"");
 ").
 
 string__float_to_string(_, _) :-
@@ -1872,21 +1848,67 @@
 	% matching foreign_proc version.
 	private_builtin__sorry("string__float_to_string").
 
+:- pragma foreign_decl(c, "
+#ifdef MR_USE_SINGLE_PREC_FLOAT
+  #define ML_MIN_PRECISION	7
+  #define ML_FMT		""%f""
+#else
+  #define ML_MIN_PRECISION	15
+  #define ML_FMT		""%lf""
+#endif
+#define ML_MAX_PRECISION	(ML_MIN_PRECISION + 2)
+
+/*
+** The size of the buffer to pass to ML_sprintf_float.
+**
+** Longest possible string for %.*g format is `-n.nnnnnnE-mmmm', which
+** has size  PRECISION + MAX_EXPONENT_DIGITS + 5 (for the `-', `.', `E',
+** '-', and '\\0').  PRECISION is at most 20, and MAX_EXPONENT_DIGITS is
+** at most 5, so we need at most 30 chars.  80 is way more than enough.
+*/
+#define ML_SPRINTF_FLOAT_BUF_SIZE	80
+
+void ML_sprintf_float(char *buf, MR_Float f);
+").
+
+:- pragma foreign_code(c, "
+void
+ML_sprintf_float(char *buf, MR_Float f)
+{
+	MR_Float round;
+	int 	 i = ML_MIN_PRECISION;
+
+	do {
+		sprintf(buf, ""%#.*g"", i, f);
+		if (i >= ML_MAX_PRECISION) {
+			/*
+			** This should be sufficient precision to
+			** round-trip any value.  Don't bother checking
+			** whether it can actually be round-tripped,
+			** since if it can't, this is a bug in the C
+			** implementation.
+			*/
+			break;
+		}
+		sscanf(buf, ML_FMT, &round);
+		i++;
+	} while (round != f);
+}
+").
 
 :- pragma export(string__to_float(in, out), "ML_string_to_float").
 :- pragma foreign_proc("C",
 	string__to_float(FloatString::in, FloatVal::out),
 		[will_not_call_mercury, promise_pure, thread_safe], "{
-#ifdef MR_USE_SINGLE_PREC_FLOAT
-  #define FMT    """"
-#else
-  #define FMT    ""l""
-#endif
-
+	/*
+	** The %c checks for any erroneous characters appearing after
+	** the float; if there are then sscanf() will return 2 rather
+	** than 1.
+	*/
 	char   	tmpc;
 	SUCCESS_INDICATOR =
 		(!MR_isspace(FloatString[0])) &&
-		(sscanf(FloatString, ""%"" FMT ""f%c"", &FloatVal, &tmpc) == 1);
+		(sscanf(FloatString, ML_FMT ""%c"", &FloatVal, &tmpc) == 1);
 		/* MR_TRUE if sscanf succeeds, MR_FALSE otherwise */
 }").
 
--------------------------------------------------------------------------
mercury-reviews mailing list
post:  mercury-reviews at cs.mu.oz.au
administrative address: owner-mercury-reviews at cs.mu.oz.au
unsubscribe: Address: mercury-reviews-request at cs.mu.oz.au Message: unsubscribe
subscribe:   Address: mercury-reviews-request at cs.mu.oz.au Message: subscribe
--------------------------------------------------------------------------



More information about the reviews mailing list