mirror of
https://github.com/dlang/phobos.git
synced 2025-04-28 14:10:30 +03:00
1575 lines
28 KiB
D
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);
|
|
}
|
|
|