phobos/std/conv.d
2007-09-10 05:24:49 +00:00

1575 lines
28 KiB
D

/*
* 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("Error: 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')
{
uint v1 = v;
v = v * 10 + (c - '0');
if (cast(uint)v < v1)
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",
];
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')
{
uint v1 = v;
v = v * 10 + (c - '0');
if (v < v1)
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')
{
ulong v1 = cast(ulong)v;
v = v * 10 + (c - '0');
if (cast(ulong)v < v1)
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')
{
ulong v1 = v;
v = v * 10 + (c - '0');
if (v < v1)
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 || *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 || *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 || *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);
}