view lphobos/std/conv.d @ 650:aa6a0b7968f7

Added test case for bug #100 Removed dubious check for not emitting static private global in other modules without access. This should be handled properly somewhere else, it's causing unresolved global errors for stuff that should work (in MiniD)
author Tomas Lindquist Olsen <tomas.l.olsen@gmail.com>
date Sun, 05 Oct 2008 17:28:15 +0200
parents 5825d48b27d1
children
line wrap: on
line source


// Written in the D programming language.

/*
 *  Copyright (C) 2002-2006 by Digital Mars, www.digitalmars.com
 *  Written by Walter Bright
 *  Some parts contributed by David L. Davis
 *
 *  This software is provided 'as-is', without any express or implied
 *  warranty. In no event will the authors be held liable for any damages
 *  arising from the use of this software.
 *
 *  Permission is granted to anyone to use this software for any purpose,
 *  including commercial applications, and to alter it and redistribute it
 *  freely, subject to the following restrictions:
 *
 *  o  The origin of this software must not be misrepresented; you must not
 *     claim that you wrote the original software. If you use this software
 *     in a product, an acknowledgment in the product documentation would be
 *     appreciated but is not required.
 *  o  Altered source versions must be plainly marked as such, and must not
 *     be misrepresented as being the original software.
 *  o  This notice may not be removed or altered from any source
 *     distribution.
 */

/***********
 * Conversion building blocks. These differ from the C equivalents
 * <tt>atoi()</tt> and <tt>atol()</tt> by
 * checking for overflow and not allowing whitespace.
 *
 * For conversion to signed types, the grammar recognized is:
 * <pre>
$(I Integer):
    $(I Sign UnsignedInteger)
    $(I UnsignedInteger)

$(I Sign):
    $(B +)
    $(B -)
 * </pre>
 * For conversion to signed types, the grammar recognized is:
 * <pre>
$(I UnsignedInteger):
    $(I DecimalDigit)
    $(I DecimalDigit) $(I UnsignedInteger)
 * </pre>
 * Macros:
 *	WIKI=Phobos/StdConv
 */

module std.conv;

private import std.string;  // for atof(), toString()
private import std.c.stdlib;
private import std.math;  // for fabs(), isnan()
private import std.stdio; // for writefln() and printf()


//debug=conv;		// uncomment to turn on debugging printf's

/* ************* Exceptions *************** */

/**
 * Thrown on conversion errors, which happens on deviation from the grammar.
 */
class ConvError : Error
{
    this(char[] s)
    {
	super("conversion " ~ s);
    }
}

private void conv_error(char[] s)
{
    throw new ConvError(s);
}

/**
 * Thrown on conversion overflow errors.
 */
class ConvOverflowError : Error
{
    this(char[] s)
    {
	super("Error: overflow " ~ s);
    }
}

private void conv_overflow(char[] s)
{
    throw new ConvOverflowError(s);
}

/***************************************************************
 * Convert character string to the return type.
 */

int toInt(char[] s)
{
    int length = s.length;

    if (!length)
	goto Lerr;

    int sign = 0;
    int v = 0;

    for (int i = 0; i < length; i++)
    {
	char c = s[i];
	if (c >= '0' && c <= '9')
	{
	    if (v < int.max/10 || (v == int.max/10 && c + sign <= '7'))
		v = v * 10 + (c - '0');
	    else
		goto Loverflow;
	}
	else if (c == '-' && i == 0)
	{
	    sign = -1;
	    if (length == 1)
		goto Lerr;
	}
	else if (c == '+' && i == 0)
	{
	    if (length == 1)
		goto Lerr;
	}
	else
	    goto Lerr;
    }
    if (sign == -1)
    {
	if (cast(uint)v > 0x80000000)
	    goto Loverflow;
	v = -v;
    }
    else
    {
	if (cast(uint)v > 0x7FFFFFFF)
	    goto Loverflow;
    }
    return v;

Loverflow:
    conv_overflow(s);

Lerr:
    conv_error(s);
    return 0;
}

unittest
{
    debug(conv) printf("conv.toInt.unittest\n");

    int i;

    i = toInt("0");
    assert(i == 0);

    i = toInt("+0");
    assert(i == 0);

    i = toInt("-0");
    assert(i == 0);

    i = toInt("6");
    assert(i == 6);

    i = toInt("+23");
    assert(i == 23);

    i = toInt("-468");
    assert(i == -468);

    i = toInt("2147483647");
    assert(i == 0x7FFFFFFF);

    i = toInt("-2147483648");
    assert(i == 0x80000000);

    static char[][] errors =
    [
	"",
	"-",
	"+",
	"-+",
	" ",
	" 0",
	"0 ",
	"- 0",
	"1-",
	"xx",
	"123h",
	"2147483648",
	"-2147483649",
	"5656566565",
    ];

    for (int j = 0; j < errors.length; j++)
    {
	i = 47;
	try
	{
	    i = toInt(errors[j]);
	    printf("i = %d\n", i);
	}
	catch (Error e)
	{
	    debug(conv) e.print();
	    i = 3;
	}
	assert(i == 3);
    }
}


/*******************************************************
 * ditto
 */

uint toUint(char[] s)
{
    int length = s.length;

    if (!length)
	goto Lerr;

    uint v = 0;

    for (int i = 0; i < length; i++)
    {
	char c = s[i];
	if (c >= '0' && c <= '9')
	{
	    if (v < uint.max/10 || (v == uint.max/10 && c <= '5'))
		v = v * 10 + (c - '0');
	    else
		goto Loverflow;
	}
	else
	    goto Lerr;
    }
    return v;

Loverflow:
    conv_overflow(s);

Lerr:
    conv_error(s);
    return 0;
}

unittest
{
    debug(conv) printf("conv.toUint.unittest\n");

    uint i;

    i = toUint("0");
    assert(i == 0);

    i = toUint("6");
    assert(i == 6);

    i = toUint("23");
    assert(i == 23);

    i = toUint("468");
    assert(i == 468);

    i = toUint("2147483647");
    assert(i == 0x7FFFFFFF);

    i = toUint("4294967295");
    assert(i == 0xFFFFFFFF);

    static char[][] errors =
    [
	"",
	"-",
	"+",
	"-+",
	" ",
	" 0",
	"0 ",
	"- 0",
	"1-",
	"+5",
	"-78",
	"xx",
	"123h",
	"4294967296",
    ];

    for (int j = 0; j < errors.length; j++)
    {
	i = 47;
	try
	{
	    i = toUint(errors[j]);
	    printf("i = %d\n", i);
	}
	catch (Error e)
	{
	    debug(conv) e.print();
	    i = 3;
	}
	assert(i == 3);
    }
}

/*******************************************************
 * ditto
 */

long toLong(char[] s)
{
    int length = s.length;

    if (!length)
	goto Lerr;

    int sign = 0;
    long v = 0;

    for (int i = 0; i < length; i++)
    {
	char c = s[i];
	if (c >= '0' && c <= '9')
	{
	    if (v < long.max/10 || (v == long.max/10 && c + sign <= '7'))
		v = v * 10 + (c - '0');
	    else
		goto Loverflow;
	}
	else if (c == '-' && i == 0)
	{
	    sign = -1;
	    if (length == 1)
		goto Lerr;
	}
	else if (c == '+' && i == 0)
	{
	    if (length == 1)
		goto Lerr;
	}
	else
	    goto Lerr;
    }
    if (sign == -1)
    {
	if (cast(ulong)v > 0x8000000000000000)
	    goto Loverflow;
	v = -v;
    }
    else
    {
	if (cast(ulong)v > 0x7FFFFFFFFFFFFFFF)
	    goto Loverflow;
    }
    return v;

Loverflow:
    conv_overflow(s);

Lerr:
    conv_error(s);
    return 0;
}

unittest
{
    debug(conv) printf("conv.toLong.unittest\n");

    long i;

    i = toLong("0");
    assert(i == 0);

    i = toLong("+0");
    assert(i == 0);

    i = toLong("-0");
    assert(i == 0);

    i = toLong("6");
    assert(i == 6);

    i = toLong("+23");
    assert(i == 23);

    i = toLong("-468");
    assert(i == -468);

    i = toLong("2147483647");
    assert(i == 0x7FFFFFFF);

    i = toLong("-2147483648");
    assert(i == -0x80000000L);

    i = toLong("9223372036854775807");
    assert(i == 0x7FFFFFFFFFFFFFFF);

    i = toLong("-9223372036854775808");
    assert(i == 0x8000000000000000);

    static char[][] errors =
    [
	"",
	"-",
	"+",
	"-+",
	" ",
	" 0",
	"0 ",
	"- 0",
	"1-",
	"xx",
	"123h",
	"9223372036854775808",
	"-9223372036854775809",
    ];

    for (int j = 0; j < errors.length; j++)
    {
	i = 47;
	try
	{
	    i = toLong(errors[j]);
	    printf("l = %d\n", i);
	}
	catch (Error e)
	{
	    debug(conv) e.print();
	    i = 3;
	}
	assert(i == 3);
    }
}


/*******************************************************
 * ditto
 */

ulong toUlong(char[] s)
{
    int length = s.length;

    if (!length)
	goto Lerr;

    ulong v = 0;

    for (int i = 0; i < length; i++)
    {
	char c = s[i];
	if (c >= '0' && c <= '9')
	{
	    if (v < ulong.max/10 || (v == ulong.max/10 && c <= '5'))
		v = v * 10 + (c - '0');
	    else
		goto Loverflow;
	}
	else
	    goto Lerr;
    }
    return v;

Loverflow:
    conv_overflow(s);

Lerr:
    conv_error(s);
    return 0;
}

unittest
{
    debug(conv) printf("conv.toUlong.unittest\n");

    ulong i;

    i = toUlong("0");
    assert(i == 0);

    i = toUlong("6");
    assert(i == 6);

    i = toUlong("23");
    assert(i == 23);

    i = toUlong("468");
    assert(i == 468);

    i = toUlong("2147483647");
    assert(i == 0x7FFFFFFF);

    i = toUlong("4294967295");
    assert(i == 0xFFFFFFFF);

    i = toUlong("9223372036854775807");
    assert(i == 0x7FFFFFFFFFFFFFFF);

    i = toUlong("18446744073709551615");
    assert(i == 0xFFFFFFFFFFFFFFFF);


    static char[][] errors =
    [
	"",
	"-",
	"+",
	"-+",
	" ",
	" 0",
	"0 ",
	"- 0",
	"1-",
	"+5",
	"-78",
	"xx",
	"123h",
	"18446744073709551616",
    ];

    for (int j = 0; j < errors.length; j++)
    {
	i = 47;
	try
	{
	    i = toUlong(errors[j]);
	    printf("i = %d\n", i);
	}
	catch (Error e)
	{
	    debug(conv) e.print();
	    i = 3;
	}
	assert(i == 3);
    }
}


/*******************************************************
 * ditto
 */

short toShort(char[] s)
{
    int v = toInt(s);

    if (v != cast(short)v)
	goto Loverflow;

    return cast(short)v;

Loverflow:
    conv_overflow(s);
    return 0;
}

unittest
{
    debug(conv) printf("conv.toShort.unittest\n");

    short i;

    i = toShort("0");
    assert(i == 0);

    i = toShort("+0");
    assert(i == 0);

    i = toShort("-0");
    assert(i == 0);

    i = toShort("6");
    assert(i == 6);

    i = toShort("+23");
    assert(i == 23);

    i = toShort("-468");
    assert(i == -468);

    i = toShort("32767");
    assert(i == 0x7FFF);

    i = toShort("-32768");
    assert(i == cast(short)0x8000);

    static char[][] errors =
    [
	"",
	"-",
	"+",
	"-+",
	" ",
	" 0",
	"0 ",
	"- 0",
	"1-",
	"xx",
	"123h",
	"32768",
	"-32769",
    ];

    for (int j = 0; j < errors.length; j++)
    {
	i = 47;
	try
	{
	    i = toShort(errors[j]);
	    printf("i = %d\n", i);
	}
	catch (Error e)
	{
	    debug(conv) e.print();
	    i = 3;
	}
	assert(i == 3);
    }
}


/*******************************************************
 * ditto
 */

ushort toUshort(char[] s)
{
    uint v = toUint(s);

    if (v != cast(ushort)v)
	goto Loverflow;

    return cast(ushort)v;

Loverflow:
    conv_overflow(s);
    return 0;
}

unittest
{
    debug(conv) printf("conv.toUshort.unittest\n");

    ushort i;

    i = toUshort("0");
    assert(i == 0);

    i = toUshort("6");
    assert(i == 6);

    i = toUshort("23");
    assert(i == 23);

    i = toUshort("468");
    assert(i == 468);

    i = toUshort("32767");
    assert(i == 0x7FFF);

    i = toUshort("65535");
    assert(i == 0xFFFF);

    static char[][] errors =
    [
	"",
	"-",
	"+",
	"-+",
	" ",
	" 0",
	"0 ",
	"- 0",
	"1-",
	"+5",
	"-78",
	"xx",
	"123h",
	"65536",
    ];

    for (int j = 0; j < errors.length; j++)
    {
	i = 47;
	try
	{
	    i = toUshort(errors[j]);
	    printf("i = %d\n", i);
	}
	catch (Error e)
	{
	    debug(conv) e.print();
	    i = 3;
	}
	assert(i == 3);
    }
}


/*******************************************************
 * ditto
 */

byte toByte(char[] s)
{
    int v = toInt(s);

    if (v != cast(byte)v)
	goto Loverflow;

    return cast(byte)v;

Loverflow:
    conv_overflow(s);
    return 0;
}

unittest
{
    debug(conv) printf("conv.toByte.unittest\n");

    byte i;

    i = toByte("0");
    assert(i == 0);

    i = toByte("+0");
    assert(i == 0);

    i = toByte("-0");
    assert(i == 0);

    i = toByte("6");
    assert(i == 6);

    i = toByte("+23");
    assert(i == 23);

    i = toByte("-68");
    assert(i == -68);

    i = toByte("127");
    assert(i == 0x7F);

    i = toByte("-128");
    assert(i == cast(byte)0x80);

    static char[][] errors =
    [
	"",
	"-",
	"+",
	"-+",
	" ",
	" 0",
	"0 ",
	"- 0",
	"1-",
	"xx",
	"123h",
	"128",
	"-129",
    ];

    for (int j = 0; j < errors.length; j++)
    {
	i = 47;
	try
	{
	    i = toByte(errors[j]);
	    printf("i = %d\n", i);
	}
	catch (Error e)
	{
	    debug(conv) e.print();
	    i = 3;
	}
	assert(i == 3);
    }
}


/*******************************************************
 * ditto
 */

ubyte toUbyte(char[] s)
{
    uint v = toUint(s);

    if (v != cast(ubyte)v)
	goto Loverflow;

    return cast(ubyte)v;

Loverflow:
    conv_overflow(s);
    return 0;
}

unittest
{
    debug(conv) printf("conv.toUbyte.unittest\n");

    ubyte i;

    i = toUbyte("0");
    assert(i == 0);

    i = toUbyte("6");
    assert(i == 6);

    i = toUbyte("23");
    assert(i == 23);

    i = toUbyte("68");
    assert(i == 68);

    i = toUbyte("127");
    assert(i == 0x7F);

    i = toUbyte("255");
    assert(i == 0xFF);

    static char[][] errors =
    [
	"",
	"-",
	"+",
	"-+",
	" ",
	" 0",
	"0 ",
	"- 0",
	"1-",
	"+5",
	"-78",
	"xx",
	"123h",
	"256",
    ];

    for (int j = 0; j < errors.length; j++)
    {
	i = 47;
	try
	{
	    i = toUbyte(errors[j]);
	    printf("i = %d\n", i);
	}
	catch (Error e)
	{
	    debug(conv) e.print();
	    i = 3;
	}
	assert(i == 3);
    }
}


/*******************************************************
 * ditto
 */

float toFloat(in char[] s)
{
    float f;
    char* endptr;
    char* sz;

    //writefln("toFloat('%s')", s);
    sz = toStringz(s);
    if (std.ctype.isspace(*sz))
	goto Lerr;

    // BUG: should set __locale_decpoint to "." for DMC

    setErrno(0);
    f = strtof(sz, &endptr);
    if (getErrno() == ERANGE)
	goto Lerr;
    if (endptr && (endptr == s.ptr || *endptr != 0))
	goto Lerr;

    return f;
        
  Lerr:
    conv_error(s ~ " not representable as a float");
    assert(0);
}
 
unittest
{
    debug( conv ) writefln( "conv.toFloat.unittest" );
    float f;
    
    f = toFloat( "123" );
    assert( f == 123f );
    f = toFloat( "+123" );
    assert( f == +123f );
    f = toFloat( "-123" );
    assert( f == -123f );
    f = toFloat( "123e+2" );
    assert( f == 123e+2f );

    f = toFloat( "123e-2" );
    assert( f == 123e-2f );
    f = toFloat( "123." );
    assert( f == 123.f );
    f = toFloat( ".456" );
    assert( f == .456f );
    
    // min and max
    f = toFloat("1.17549e-38");
    assert(feq(cast(real)f, cast(real)1.17549e-38));
    assert(feq(cast(real)f, cast(real)float.min));
    f = toFloat("3.40282e+38");
    assert(toString(f) == toString(3.40282e+38));

    // nan
    f = toFloat("nan");
    assert(toString(f) == toString(float.nan));
}

/*******************************************************
 * ditto
 */

double toDouble(in char[] s)
{
    double f;
    char* endptr;
    char* sz;

    //writefln("toDouble('%s')", s);
    sz = toStringz(s);
    if (std.ctype.isspace(*sz))
	goto Lerr;

    // BUG: should set __locale_decpoint to "." for DMC

    setErrno(0);
    f = strtod(sz, &endptr);
    if (getErrno() == ERANGE)
	goto Lerr;
    if (endptr && (endptr == s.ptr || *endptr != 0))
	goto Lerr;

    return f;
        
  Lerr:
    conv_error(s ~ " not representable as a double");
    assert(0);
}

unittest
{
    debug( conv ) writefln( "conv.toDouble.unittest" );
    double d;

    d = toDouble( "123" );
    assert( d == 123 );
    d = toDouble( "+123" );
    assert( d == +123 );
    d = toDouble( "-123" );
    assert( d == -123 );
    d = toDouble( "123e2" );
    assert( d == 123e2);
    d = toDouble( "123e-2" );
    assert( d == 123e-2 );
    d = toDouble( "123." );
    assert( d == 123. );
    d = toDouble( ".456" );
    assert( d == .456 );
    d = toDouble( "1.23456E+2" );
    assert( d == 1.23456E+2 );

    // min and max
    d = toDouble("2.22507e-308");
    assert(feq(cast(real)d, cast(real)2.22507e-308));
    assert(feq(cast(real)d, cast(real)double.min));
    d = toDouble("1.79769e+308");
    assert(toString(d) == toString(1.79769e+308));
    assert(toString(d) == toString(double.max));

    // nan
    d = toDouble("nan");
    assert(toString(d) == toString(double.nan));
    //assert(cast(real)d == cast(real)double.nan);
}

/*******************************************************
 * ditto
 */
real toReal(in char[] s)
{
    real f;
    char* endptr;
    char* sz;

    //writefln("toReal('%s')", s);
    sz = toStringz(s);
    if (std.ctype.isspace(*sz))
	goto Lerr;

    // BUG: should set __locale_decpoint to "." for DMC

    setErrno(0);
    f = strtold(sz, &endptr);
    if (getErrno() == ERANGE)
	goto Lerr;
    if (endptr && (endptr == s.ptr || *endptr != 0))
	goto Lerr;

    return f;
        
  Lerr:
    conv_error(s ~ " not representable as a real");
    assert(0);
}

unittest
{
    debug(conv) writefln("conv.toReal.unittest");
    real r;

    r = toReal("123");
    assert(r == 123L);
    r = toReal("+123");
    assert(r == 123L);
    r = toReal("-123");
    assert(r == -123L);
    r = toReal("123e2");
    assert(feq(r, 123e2L));
    r = toReal("123e-2");
    assert(feq(r, 1.23L));
    r = toReal("123.");
    assert(r == 123L);
    r = toReal(".456");
    assert(r == .456L);

    r = toReal("1.23456e+2");
    assert(feq(r,  1.23456e+2L));
    r = toReal(toString(real.max / 2L));
    assert(toString(r) == toString(real.max / 2L));

    // min and max
    r = toReal(toString(real.min));
    assert(toString(r) == toString(real.min));
    r = toReal(toString(real.max));
    assert(toString(r) == toString(real.max));

    // nan
    r = toReal("nan");
    assert(toString(r) == toString(real.nan));
    //assert(r == real.nan);

    r = toReal(toString(real.nan));
    assert(toString(r) == toString(real.nan));
    //assert(r == real.nan);
}

version (none)
{   /* These are removed for the moment because of concern about
     * what to do about the 'i' suffix. Should it be there?
     * Should it not? What about 'nan', should it be 'nani'?
     * 'infinity' or 'infinityi'?
     * Should it match what toString(ifloat) does with the 'i' suffix?
     */

/*******************************************************
 * ditto
 */

ifloat toIfloat(in char[] s)
{
    return toFloat(s) * 1.0i;
}

unittest
{
    debug(conv) writefln("conv.toIfloat.unittest");
    ifloat ift;
    
    ift = toIfloat(toString(123.45));
    assert(toString(ift) == toString(123.45i));

    ift = toIfloat(toString(456.77i));
    assert(toString(ift) == toString(456.77i));

    // min and max
    ift = toIfloat(toString(ifloat.min));
    assert(toString(ift) == toString(ifloat.min) );
    assert(feq(cast(ireal)ift, cast(ireal)ifloat.min));

    ift = toIfloat(toString(ifloat.max));
    assert(toString(ift) == toString(ifloat.max));
    assert(feq(cast(ireal)ift, cast(ireal)ifloat.max));
   
    // nan
    ift = toIfloat("nani");
    assert(cast(real)ift == cast(real)ifloat.nan);

    ift = toIfloat(toString(ifloat.nan));
    assert(toString(ift) == toString(ifloat.nan));
    assert(feq(cast(ireal)ift, cast(ireal)ifloat.nan));
}

/*******************************************************
 * ditto
 */

idouble toIdouble(in char[] s)
{
    return toDouble(s) * 1.0i;
}

unittest
{
    debug(conv) writefln("conv.toIdouble.unittest");
    idouble id;

    id = toIdouble(toString("123.45"));
    assert(id == 123.45i);

    id = toIdouble(toString("123.45e+302i"));
    assert(id == 123.45e+302i);

    // min and max
    id = toIdouble(toString(idouble.min));
    assert(toString( id ) == toString(idouble.min));
    assert(feq(cast(ireal)id.re, cast(ireal)idouble.min.re));
    assert(feq(cast(ireal)id.im, cast(ireal)idouble.min.im));
    
    id = toIdouble(toString(idouble.max));
    assert(toString(id) == toString(idouble.max));
    assert(feq(cast(ireal)id.re, cast(ireal)idouble.max.re));
    assert(feq(cast(ireal)id.im, cast(ireal)idouble.max.im));
    
    // nan
    id = toIdouble("nani");
    assert(cast(real)id == cast(real)idouble.nan);

    id = toIdouble(toString(idouble.nan));
    assert(toString(id) == toString(idouble.nan));
}

/*******************************************************
 * ditto
 */

ireal toIreal(in char[] s)
{
    return toReal(s) * 1.0i;
}

unittest
{
    debug(conv) writefln("conv.toIreal.unittest");
    ireal ir;

    ir = toIreal(toString("123.45"));
    assert(feq(cast(real)ir.re, cast(real)123.45i)); 

    ir = toIreal(toString("123.45e+82i"));
    assert(toString(ir) == toString(123.45e+82i));
    //assert(ir == 123.45e+82i);

    // min and max
    ir = toIreal(toString(ireal.min));
    assert(toString(ir) == toString(ireal.min));
    assert(feq(cast(real)ir.re, cast(real)ireal.min.re));
    assert(feq(cast(real)ir.im, cast(real)ireal.min.im));

    ir = toIreal(toString(ireal.max));
    assert(toString(ir) == toString(ireal.max));
    assert(feq(cast(real)ir.re, cast(real)ireal.max.re));
    //assert(feq(cast(real)ir.im, cast(real)ireal.max.im));

    // nan
    ir = toIreal("nani");
    assert(cast(real)ir == cast(real)ireal.nan);

    ir = toIreal(toString(ireal.nan));
    assert(toString(ir) == toString(ireal.nan));
}


/*******************************************************
 * ditto
 */
cfloat toCfloat(in char[] s)
{
    char[] s1;
    char[] s2;
    real   r1;
    real   r2;
    cfloat cf;
    bool    b = 0;
    char*  endptr;

    if (!s.length)
        goto Lerr;
    
    b = getComplexStrings(s, s1, s2);

    if (!b)
        goto Lerr;
    
    // atof(s1);
    endptr = &s1[s1.length - 1];
    r1 = strtold(s1, &endptr); 

    // atof(s2);
    endptr = &s2[s2.length - 1];
    r2 = strtold(s2, &endptr); 

    cf = cast(cfloat)(r1 + (r2 * 1.0i));

    //writefln( "toCfloat() r1=%g, r2=%g, cf=%g, max=%g", 
    //           r1, r2, cf, cfloat.max);
    // Currently disabled due to a posted bug where a 
    // complex float greater-than compare to .max compares 
    // incorrectly.
    //if (cf > cfloat.max)
    //    goto Loverflow;

    return cf;

    Loverflow:
        conv_overflow(s);
        
    Lerr:
        conv_error(s);
        return cast(cfloat)0.0e-0+0i;   
}

unittest
{
    debug(conv) writefln("conv.toCfloat.unittest");
    cfloat cf;

    cf = toCfloat(toString("1.2345e-5+0i"));
    assert(toString(cf) == toString(1.2345e-5+0i));
    assert(feq(cf, 1.2345e-5+0i));

    // min and max
    cf = toCfloat(toString(cfloat.min));
    assert(toString(cf) == toString(cfloat.min));

    cf = toCfloat(toString(cfloat.max));
    assert(toString(cf) == toString(cfloat.max));
   
    // nan ( nan+nani )
    cf = toCfloat("nani");
    //writefln("toCfloat() cf=%g, cf=\"%s\", nan=%s", 
    //         cf, toString(cf), toString(cfloat.nan));
    assert(toString(cf) == toString(cfloat.nan));

    cf = toCdouble("nan+nani");
    assert(toString(cf) == toString(cfloat.nan));

    cf = toCfloat(toString(cfloat.nan));
    assert(toString(cf) == toString(cfloat.nan));
    assert(feq(cast(creal)cf, cast(creal)cfloat.nan));
}

/*******************************************************
 * ditto
 */
cdouble toCdouble(in char[] s)
{
    char[]  s1;
    char[]  s2;
    real    r1;
    real    r2;
    cdouble cd;
    bool     b = 0;
    char*   endptr;

    if (!s.length)
        goto Lerr;
    
    b = getComplexStrings(s, s1, s2);

    if (!b)
        goto Lerr;

    // atof(s1);
    endptr = &s1[s1.length - 1];
    r1 = strtold(s1, &endptr); 

    // atof(s2);
    endptr = &s2[s2.length - 1];
    r2 = strtold(s2, &endptr); //atof(s2);

    cd = cast(cdouble)(r1 + (r2 * 1.0i));
 
    //Disabled, waiting on a bug fix.
    //if (cd > cdouble.max)  //same problem the toCfloat() having
    //    goto Loverflow;

    return cd;

    Loverflow:
        conv_overflow(s);
        
    Lerr:
        conv_error(s);
        return cast(cdouble)0.0e-0+0i; 
}

unittest
{
    debug(conv) writefln("conv.toCdouble.unittest");
    cdouble cd;

    cd = toCdouble(toString("1.2345e-5+0i"));
    assert(toString( cd ) == toString(1.2345e-5+0i));
    assert(feq(cd, 1.2345e-5+0i));

    // min and max
    cd = toCdouble(toString(cdouble.min));
    assert(toString(cd) == toString(cdouble.min));
    assert(feq(cast(creal)cd, cast(creal)cdouble.min));

    cd = toCdouble(toString(cdouble.max));
    assert(toString( cd ) == toString(cdouble.max));
    assert(feq(cast(creal)cd, cast(creal)cdouble.max));

    // nan ( nan+nani )
    cd = toCdouble("nani");
    assert(toString(cd) == toString(cdouble.nan));

    cd = toCdouble("nan+nani");
    assert(toString(cd) == toString(cdouble.nan));

    cd = toCdouble(toString(cdouble.nan));
    assert(toString(cd) == toString(cdouble.nan));
    assert(feq(cast(creal)cd, cast(creal)cdouble.nan));
}

/*******************************************************
 * ditto
 */
creal toCreal(in char[] s)
{
    char[] s1;
    char[] s2;
    real   r1;
    real   r2;
    creal  cr;
    bool    b = 0;
    char*  endptr;

    if (!s.length)
        goto Lerr;

    b = getComplexStrings(s, s1, s2);

    if (!b)
        goto Lerr;
 
    // atof(s1);
    endptr = &s1[s1.length - 1];
    r1 = strtold(s1, &endptr); 

    // atof(s2);
    endptr = &s2[s2.length - 1];
    r2 = strtold(s2, &endptr); //atof(s2);

    //writefln("toCreal() r1=%g, r2=%g, s1=\"%s\", s2=\"%s\", nan=%g", 
    //          r1, r2, s1, s2, creal.nan);
   
    if (s1 =="nan" && s2 == "nani")
        cr = creal.nan;
    else if (r2 != 0.0)
        cr = cast(creal)(r1 + (r2 * 1.0i));
    else
        cr = cast(creal)(r1 + 0.0i);    
    
    return cr;

    Lerr:
        conv_error(s);
        return cast(creal)0.0e-0+0i;    
}

unittest
{
    debug(conv) writefln("conv.toCreal.unittest");
    creal cr;

    cr = toCreal(toString("1.2345e-5+0i"));
    assert(toString(cr) == toString(1.2345e-5+0i));
    assert(feq(cr, 1.2345e-5+0i));

    cr = toCreal(toString("0.0e-0+0i"));
    assert(toString(cr) == toString(0.0e-0+0i));
    assert(cr == 0.0e-0+0i);
    assert(feq(cr, 0.0e-0+0i));
    
    cr = toCreal("123");
    assert(cr == 123);

    cr = toCreal("+5");
    assert(cr == 5);
 
    cr = toCreal("-78");
    assert(cr == -78);

    // min and max
    cr = toCreal(toString(creal.min));
    assert(toString(cr) == toString(creal.min));
    assert(feq(cr, creal.min));
    
    cr = toCreal(toString(creal.max));
    assert(toString(cr) == toString(creal.max));
    assert(feq(cr, creal.max));

    // nan ( nan+nani )
    cr = toCreal("nani");
    assert(toString(cr) == toString(creal.nan));

    cr = toCreal("nan+nani");
    assert(toString(cr) == toString(creal.nan));

    cr = toCreal(toString(cdouble.nan));
    assert(toString(cr) == toString(creal.nan));
    assert(feq(cr, creal.nan));
}

}

/* **************************************************************
 * Splits a complex float (cfloat, cdouble, and creal) into two workable strings.
 * Grammar:
 * ['+'|'-'] string floating-point digit {digit}
 */
private bool getComplexStrings(in char[] s, out char[] s1, out char[] s2)
{
    int len = s.length;

    if (!len) 
        goto Lerr;

    // When "nan" or "nani" just return them.
    if (s == "nan" || s == "nani" || s == "nan+nani")
    {
        s1 = "nan";
        s2 = "nani";
        return 1;
    }
    
    // Split the original string out into two strings.
    for (int i = 1; i < len; i++)
        if ((s[i - 1] != 'e' && s[i - 1] != 'E') && s[i] == '+')
        {
            s1 = s[0..i];
            if (i + 1 < len - 1)
                s2 = s[i + 1..len - 1];
            else 
                s2 = "0e+0i";
            
            break;
        }   

    // Handle the case when there's only a single value 
    // to work with, and set the other string to zero.
    if (!s1.length)
    {
        s1 = s;
        s2 = "0e+0i";
    }
 
    //writefln( "getComplexStrings() s=\"%s\", s1=\"%s\", s2=\"%s\", len=%d", 
    //           s, s1, s2, len );
   
    return 1;

    Lerr:
        // Display the original string in the error message.
        conv_error("getComplexStrings() \"" ~ s ~ "\"" ~ " s1=\"" ~ s1 ~ "\"" ~ " s2=\"" ~ s2 ~ "\"");
        return 0;
}

// feq() functions now used only in unittesting

/* ***************************************
 * Main function to compare reals with given precision
 */
private bool feq(in real rx, in real ry, in real precision)
{
    if (rx == ry)
        return 1;
    
    if (isnan(rx))
        return cast(bool)isnan(ry);

    if (isnan(ry))
        return 0;
       
    return cast(bool)(fabs(rx - ry) <= precision);
}

/* ***************************************
 * (Note: Copied here from std.math's mfeq() function for unittesting)
 * Simple function to compare two floating point values
 * to a specified precision.
 * Returns:
 *  1   match
 *  0   nomatch
 */
private bool feq(in real r1, in real r2)
{
    if (r1 == r2)
        return 1;
    
    if (isnan(r1))
        return cast(bool)isnan(r2);

    if (isnan(r2))
        return 0;
        
    return cast(bool)(feq(r1, r2, 0.000001L));
} 
 
/* ***************************************
 * compare ireals with given precision
 */
private bool feq(in ireal r1, in ireal r2)
{
    real rx = cast(real)r1;
    real ry = cast(real)r2;

    if (rx == ry)
        return 1;
    
    if (isnan(rx)) 
        return cast(bool)isnan(ry);

    if (isnan(ry))
        return 0;
    
    return feq(rx, ry, 0.000001L);
} 

/* ***************************************
 * compare creals with given precision
 */
private bool feq(in creal r1, in creal r2)
{
    real r1a = fabs(cast(real)r1.re - cast(real)r2.re);
    real r2b = fabs(cast(real)r1.im - cast(real)r2.im);

    if ((cast(real)r1.re == cast(real)r2.re) &&
        (cast(real)r1.im == cast(real)r2.im))
        return 1;
    
    if (isnan(r1a))
        return cast(bool)isnan(r2b);

    if (isnan(r2b))
        return 0;

    return feq(r1a, r2b, 0.000001L);
}