[m-rev.] for review: clean up string.m

Julien Fischer jfischer at opturion.com
Mon Nov 10 10:58:04 AEDT 2014


Hi Zoltan,

On Sun, 9 Nov 2014, Zoltan Somogyi wrote:

> For review by Julien. Can you please have a look at the
> XXX I added for the treatment of -0.0, and tell me whether
> you know of a way to check the sign bit directly (not via < 0.0
> test) on each of our target languages?

For the latter: to check the sign bit in C# and Java you will need to convert
the float to its binary representation and extract the sign bit.
(See the attached program for C# and Java implementations that do this.)
In principle, the same approach will work for Erlang, but my Erlang is
not up to that.  (The floating point support in the erlang grade is pretty
broken at the moment, so it's ok to leave the Erlang case NYI for now.)

For C, using the signbit macro only works for those C compilers that support
C99.  Since we (unfortunately) still need to support some C compilers that
don't (e.g. MSVC prior to 2012) something else will need to be done for them.
For now, I suggest simply checking whether signbit exists in the configure
script as per the other FP macros and calling MR_fatal_error if it doesn't.
(I'll add implementations for older C compilers at a later date.)

I was intending to add signbit and a bunch of other impure low-level float
operations to the float module anyway, so if you are going to use it you
may as well add it there.

I think the bigger question here is: how should string.format (and friends)
handle -0.0 in the first place?  There is an issue because string.format is one
of the functions referred in the comment at the head of the float module that
doesn't obey the axiom `all [F, X, Y] X = Y => F(X) = F(Y)', for instance when
X = 0.0 and Y = -0.0.  For string.format throwing an exception in the zero
case, as suggested by that comment, is not an option though.

> I next intend to move the implementation of string.format
> to a new submodule of string.m, so that string.m doesn't become
> even bigger when I add code for parsing format strings
> *without* the values to be printed being present.
> The idea is for compiler/format_call.m to use this new capability
> to transform *all* calls to string.format and io.format in which

What about calls to stream.string_writer.format?

> the format string is statically known to a sequence of calls to the
> predicates that do the actual formatting, thus compiling away
> all the format string interpretation overhead.
>
> This would require turning off using_sprintf, since (a) we cannot
> target the innards of a C library function, and (b) having the
> interpreted and compiled versions of string.format and io.format
> having different behavior is not a good idea.

That's fine by me, the differences between the sprintfs on different systems
have long made testing things more complicated than they should be.

> Besides enabling compiling away format string interpretation
> overhead, turning it off would also allow us to add new format
> specifiers sprintf *cannot* handle. The obvious one is a specifier
> that does what io.write does.
>
> Independently of the above, I plan also to look into factoring out
> commonalities in the existing formatting code by replacing the
> conversion specifiers d/i/o/x etc, e/f/g etc as just int and float
> specifiers with parameters for things such as octal/decimal/hex base.
>
> Any objections?

Not from me.

The diff itself looks fine except the additions to the require module
need to be mentioned in the NEWS file.

Cheers,
Julien.
-------------- next part --------------
:- module signbit.
:- interface.

:- import_module io.

:- pred main(io::di, io::uo) is det.

:- implementation.

:- import_module float.
:- import_module list.
:- import_module string.

main(!IO) :-
   promise_pure (
        impure T1 = signbit(-1.0), 
	io.format("signbit(-1.0) = %d\n", [i(T1)], !IO),
        
	impure T2 = signbit(1.0),
        io.format("signbit(1.0) = %d\n", [i(T2)], !IO),
	
	impure T3 = signbit(infinity),
        io.format("signbit(infinity) = %d\n", [i(T3)], !IO),
	
	impure T4 = signbit(-infinity),
        io.format("signbit(-infinity) = %d\n", [i(T4)], !IO),
	
	impure T5 = signbit(0.0),
	io.format("signbit(0.0) = %d\n", [i(T5)], !IO),
	
	impure T6 = signbit(-1.0 * 0.0),
	io.format("signbit(-0.0) = %d\n", [i(T6)], !IO),

	NaN = 0.0 * infinity,
        impure T7 = signbit(NaN),
	io.format("signbit(NaN) = %d\n", [i(T7)], !IO)
   ).

:- pragma foreign_decl("C", "#include <math.h>").

:- impure func signbit(float) = int.

:- pragma foreign_proc("C",
    signbit(F::in) = (B::out),
    [will_not_call_mercury, thread_safe, will_not_modify_trail],
"
    B = signbit(F);
").

:- pragma foreign_proc("Java",
    signbit(F::in) = (B::out),
    [will_not_call_mercury, thread_safe],
"
    B = (int)((java.lang.Double.doubleToRawLongBits(F) >>> 63));
").

:- pragma foreign_proc("C#",
    signbit(F::in) = (B::out),
    [will_not_call_mercury, thread_safe],
"
    B = (int)(((ulong) System.BitConverter.DoubleToInt64Bits(F)) >> 63);
").


More information about the reviews mailing list