// Written by Walter Bright // Copyright (c) 2002-2006 Digital Mars // All Rights Reserved // www.digitalmars.com // Some parts contributed by David L. Davis // Conversion building blocks. These differ from the C equivalents by // checking for overflow and not allowing whitespace. 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 ****************/ class ConvError : Error { this(char[] s) { super("Error: conversion " ~ s); } } private void conv_error(char[] s) { throw new ConvError(s); } class ConvOverflowError : Error { this(char[] s) { super("Error: overflow " ~ s); } } private void conv_overflow(char[] s) { throw new ConvOverflowError(s); } /*************************************************************** * Convert character string to int. * Grammar: * ['+'|'-'] digit {digit} */ 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); } } /******************************************************* * Convert character string to uint. * Grammar: * digit {digit} */ 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); } } /*************************************************************** * Convert character string to long. * Grammar: * ['+'|'-'] digit {digit} */ 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 = 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); } } /******************************************************* * Convert character string to ulong. * Grammar: * digit {digit} */ 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); } } /*************************************************************** * Convert character string to short. * Grammar: * ['+'|'-'] digit {digit} */ 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); } } /******************************************************* * Convert character string to ushort. * Grammar: * digit {digit} */ 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); } } /*************************************************************** * Convert character string to byte. * Grammar: * ['+'|'-'] digit {digit} */ 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); } } /******************************************************* * Convert character string to ubyte. * Grammar: * digit {digit} */ 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); } } /*************************************************************** * Convert character string to float. */ 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"); } 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)); } /*************************************************************** * Convert character string to double. */ 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"); } 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); } /*************************************************************** * Convert character string to real. * Grammar: * ['+'|'-'] digit {digit} */ 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"); } 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? */ /*************************************************************** * Convert character string to ifloat. */ 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)); } /*************************************************************** * Convert character string to idouble. */ 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)); } /*************************************************************** * Convert character string to ireal. */ 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)); } /*************************************************************** * Convert character string to cfloat. * Grammar: * ['+'|'-'] digit {digit} */ 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)); } /*************************************************************** * Convert character string to cdouble. * Grammar: * ['+'|'-'] digit {digit} */ 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)); } /*************************************************************** * Convert character string to creal. * Grammar: * ['+'|'-'] digit {digit} */ 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); }