| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944 | /******************************************************************************* copyright: Copyright (c) 2004 Kris Bell. All rights reserved license: BSD style: $(LICENSE) version: Nov 2005: Initial release Jan 2010: added internal ecvt() author: Kris A set of functions for converting between string and floating- point values. Applying the D "import alias" mechanism to this module is highly recommended, in order to limit namespace pollution: --- import Float = tango.text.convert.Float; auto f = Float.parse ("3.14159"); --- *******************************************************************************/ module tango.text.convert.Float; private import tango.core.Exception; /****************************************************************************** select an internal version ******************************************************************************/ version = float_internal; private alias real NumType; /****************************************************************************** optional math functions ******************************************************************************/ private extern (C) { double log10 (double x); double ceil (double num); double modf (double num, double *i); double pow (double base, double exp); real log10l (real x); real ceill (real num); real modfl (real num, real *i); real powl (real base, real exp); int printf (char*, ...); version (float_lib) { version (Windows) { alias ecvt econvert; alias ecvt fconvert; char* ecvt (double d, int digits, int* decpt, int* sign); char* fcvt (double d, int digits, int* decpt, int* sign); } else { alias ecvtl econvert; alias ecvtl fconvert; char* ecvtl (real d, int digits, int* decpt, int* sign); char* fcvtl (real d, int digits, int* decpt, int* sign); } } } /****************************************************************************** Constants ******************************************************************************/ private enum { Pad = 0, // default trailing decimal zero Dec = 2, // default decimal places Exp = 10, // default switch to scientific notation } /****************************************************************************** Convert a formatted string of digits to a floating-point number. Throws an exception where the input text is not parsable in its entirety. ******************************************************************************/ NumType toFloat(T) (const(T[]) src) { size_t len; auto x = parse (src, &len); if (len < src.length || len == 0) throw new IllegalArgumentException ("Float.toFloat :: invalid number"); return x; } /****************************************************************************** Template wrapper to make life simpler. Returns a text version of the provided value. See format() for details ******************************************************************************/ char[] toString (NumType d, uint decimals=Dec, int e=Exp) { char[64] tmp = void; return format (tmp, d, cast(int)decimals, e).dup; } /****************************************************************************** Template wrapper to make life simpler. Returns a text version of the provided value. See format() for details ******************************************************************************/ wchar[] toString16 (NumType d, uint decimals=Dec, int e=Exp) { wchar[64] tmp = void; return format (tmp, d, cast(int)decimals, e).dup; } /****************************************************************************** Template wrapper to make life simpler. Returns a text version of the provided value. See format() for details ******************************************************************************/ dchar[] toString32 (NumType d, uint decimals=Dec, int e=Exp) { dchar[64] tmp = void; return format (tmp, d, cast(int)decimals, e).dup; } /****************************************************************************** Truncate trailing '0' and '.' from a string, such that 200.000 becomes 200, and 20.10 becomes 20.1 Returns a potentially shorter slice of what you give it. ******************************************************************************/ T[] truncate(T) (T[] s) { auto tmp = s; int i = tmp.length; foreach (int idx, T c; tmp) if (c is '.') while (--i >= idx) if (tmp[i] != '0') { if (tmp[i] is '.') --i; s = tmp [0 .. i+1]; while (--i >= idx) if (tmp[i] is 'e') return tmp; break; } return s; } /****************************************************************************** Extract a sign-bit ******************************************************************************/ private bool negative (NumType x) { static if (NumType.sizeof is 4) return ((*cast(uint *)&x) & 0x8000_0000) != 0; else static if (NumType.sizeof is 8) return ((*cast(ulong *)&x) & 0x8000_0000_0000_0000) != 0; else { auto pe = cast(ubyte *)&x; return (pe[9] & 0x80) != 0; } } /****************************************************************************** Convert a floating-point number to a string. The e parameter controls the number of exponent places emitted, and can thus control where the output switches to the scientific notation. For example, setting e=2 for 0.01 or 10.0 would result in normal output. Whereas setting e=1 would result in both those values being rendered in scientific notation instead. Setting e to 0 forces that notation on for everything. Parameter pad will append trailing '0' decimals when set ~ otherwise trailing '0's will be elided ******************************************************************************/ T[] format(T) (T[] dst, NumType x, int decimals=Dec, int e=Exp, bool pad=Pad) { char* end, str; int exp, sign, mode=5; char[32] buf = void; // test exponent to determine mode exp = (x is 0) ? 1 : cast(int) log10l (x < 0 ? -x : x); if (exp <= -e || exp >= e) mode = 2, ++decimals; version (float_internal) str = convertl (buf.ptr, x, decimals, &exp, &sign, mode is 5); version (float_dtoa) str = dtoa (x, mode, decimals, &exp, &sign, &end); version (float_lib) { if (mode is 5) str = fconvert (x, decimals, &exp, &sign); else str = econvert (x, decimals, &exp, &sign); } auto p = dst.ptr; if (sign) *p++ = '-'; if (exp is 9999) while (*str) *p++ = *str++; else { if (mode is 2) { --exp; *p++ = *str++; if (*str || pad) { auto d = p; *p++ = '.'; while (*str) *p++ = *str++; if (pad) while (p-d < decimals) *p++ = '0'; } *p++ = 'e'; if (exp < 0) *p++ = '-', exp = -exp; else *p++ = '+'; if (exp >= 1000) { *p++ = cast(T)((exp/1000) + '0'); exp %= 1000; } if (exp >= 100) { *p++ = cast(char)(exp / 100 + '0'); exp %= 100; } *p++ = cast(char)(exp / 10 + '0'); *p++ = cast(char)(exp % 10 + '0'); } else { if (exp <= 0) *p++ = '0'; else for (; exp > 0; --exp) *p++ = (*str) ? *str++ : '0'; if (*str || pad) { *p++ = '.'; auto d = p; for (; exp < 0; ++exp) *p++ = '0'; while (*str) *p++ = *str++; if (pad) while (p-d < decimals) *p++ = '0'; } } } // stuff a C terminator in there too ... *p = 0; return dst[0..(p - dst.ptr)]; } /****************************************************************************** ecvt() and fcvt() for 80bit FP, which DMD does not include. Based upon the following: Copyright (c) 2009 Ian Piumarta All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, provided that the above copyright notice(s) and this permission notice appear in all copies of the Software. ******************************************************************************/ version (float_internal) { private char *convertl (char* buf, real value, int ndigit, int *decpt, int *sign, int fflag) { if ((*sign = negative(value)) != 0) value = -value; *decpt = 9999; if (value !<>= value) return cast(char*)"nan\0"; if (value is value.infinity) return cast(char*)"inf\0"; int exp10 = (value is 0) ? !fflag : cast(int) ceill(log10l(value)); if (exp10 < -4931) exp10 = -4931; value *= powl (10.0, -exp10); if (value) { while (value < 0.1) { value *= 10; --exp10; } while (value >= 1.0) { value /= 10; ++exp10; } } assert(value is 0 || (0.1 <= value && value < 1.0)); //auto zero = pad ? int.max : 1; auto zero = 1; if (fflag) { // if (! pad) zero = exp10; if (ndigit + exp10 < 0) { *decpt= -ndigit; return cast(char*)"\0"; } ndigit += exp10; } *decpt = exp10; int ptr = 1; if (ndigit > real.dig) ndigit = real.dig; //printf ("< flag %d, digits %d, exp10 %d, decpt %d\n", fflag, ndigit, exp10, *decpt); while (ptr <= ndigit) { real i = void; value = modfl (value * 10, &i); buf [ptr++]= cast(char)('0' + cast(int) i); } if (value >= 0.5) while (--ptr && ++buf[ptr] > '9') buf[ptr] = (ptr > zero) ? '\0' : '0'; else for (auto i=ptr; i && --i > zero && buf[i] is '0';) buf[i] = '\0'; if (ptr) { buf [ndigit + 1] = '\0'; return buf + 1; } if (fflag) { ++ndigit; } buf[0]= '1'; ++*decpt; buf[ndigit]= '\0'; return buf; } } /****************************************************************************** David Gay's extended conversions between string and floating-point numeric representations. Use these where you need extended accuracy for convertions. Note that this class requires the attendent file dtoa.c be compiled and linked to the application ******************************************************************************/ version (float_dtoa) { private extern(C) { // these should be linked in via dtoa.c double strtod (const(char*) s00, const(char*)* se); char* dtoa (double d, int mode, int ndigits, int* decpt, int* sign, char** rve); } /********************************************************************** Convert a formatted string of digits to a floating- point number. **********************************************************************/ NumType parse (const(char[]) src, size_t* ate=null) { const(char)* end; auto value = strtod (src.ptr, &end); assert (end <= src.ptr + src.length); if (ate) *ate = end - src.ptr; return value; } /********************************************************************** Convert a formatted string of digits to a floating- point number. **********************************************************************/ NumType parse (const(wchar[]) src, size_t* ate=null) { // cheesy hack to avoid pre-parsing :: max digits == 100 char[100] tmp = void; auto p = tmp.ptr; auto e = p + tmp.length; foreach (c; src) if (p < e && (c & 0x80) is 0) *p++ = c; else break; return parse (tmp[0..p-tmp.ptr], ate); } /********************************************************************** Convert a formatted string of digits to a floating- point number. **********************************************************************/ NumType parse (const(dchar[]) src, size_t* ate=null) { // cheesy hack to avoid pre-parsing :: max digits == 100 char[100] tmp = void; auto p = tmp.ptr; auto e = p + tmp.length; foreach (c; src) if (p < e && (c & 0x80) is 0) *p++ = c; else break; return parse (tmp[0..p-tmp.ptr], ate); } } else { private import Integer = tango.text.convert.Integer; /****************************************************************************** Convert a formatted string of digits to a floating-point number. Good for general use, but use David Gay's dtoa package if serious rounding adjustments should be applied. ******************************************************************************/ NumType parse(T) (const(T[]) src, size_t* ate=null) { T c; const(T)* p; int exp; bool sign; uint radix; NumType value = 0.0; static bool match (const(T)* aa, const(T[]) bb) { foreach (b; bb) { auto a = cast(T)*aa++; if (a >= 'A' && a <= 'Z') a += 'a' - 'A'; if (a != b) return false; } return true; } // remove leading space, and sign p = src.ptr + Integer.trim (src, sign, radix); // bail out if the string is empty if (src.length is 0 || p > &src[$-1]) return NumType.nan; c = *p; // handle non-decimal representations if (radix != 10) { long v = Integer.parse (src, radix, ate); return cast(NumType) v; } // set begin and end checks auto begin = p; auto end = src.ptr + src.length; // read leading digits; note that leading // zeros are simply multiplied away while (c >= '0' && c <= '9' && p < end) { value = value * 10 + (c - '0'); c = *++p; } // gobble up the point if (c is '.' && p < end) c = *++p; // read fractional digits; note that we accumulate // all digits ... very long numbers impact accuracy // to a degree, but perhaps not as much as one might // expect. A prior version limited the digit count, // but did not show marked improvement. For maximum // accuracy when reading and writing, use David Gay's // dtoa package instead while (c >= '0' && c <= '9' && p < end) { value = value * 10 + (c - '0'); c = *++p; --exp; } // did we get something? if (p > begin) { // parse base10 exponent? if ((c is 'e' || c is 'E') && p < end ) { size_t eaten; exp += Integer.parse (src[(++p-src.ptr) .. $], 0, &eaten); p += eaten; } // adjust mantissa; note that the exponent has // already been adjusted for fractional digits if (exp < 0) value /= pow10 (-exp); else value *= pow10 (exp); } else if (end - p >= 3) switch (*p) { case 'I': case 'i': if (match (p+1, "nf")) { value = value.infinity; p += 3; if (end - p >= 5 && match (p, "inity")) p += 5; } break; case 'N': case 'n': if (match (p+1, "an")) { value = value.nan; p += 3; } break; default: break; } // set parse length, and return value if (ate) *ate = p - src.ptr; if (sign) value = -value; return value; } /****************************************************************************** Internal function to convert an exponent specifier to a floating point value. ******************************************************************************/ private NumType pow10 (uint exp) { static NumType[] Powers = [ 1.0e1L, 1.0e2L, 1.0e4L, 1.0e8L, 1.0e16L, 1.0e32L, 1.0e64L, 1.0e128L, 1.0e256L, 1.0e512L, 1.0e1024L, 1.0e2048L, 1.0e4096L, 1.0e8192L, ]; if (exp >= 16384) throw new IllegalArgumentException ("Float.pow10 :: exponent too large"); NumType mult = 1.0; foreach (NumType power; Powers) { if (exp & 1) mult *= power; if ((exp >>= 1) is 0) break; } return mult; } } version (float_old) { /****************************************************************************** Convert a float to a string. This produces pretty good results for the most part, though one should use David Gay's dtoa package for best accuracy. Note that the approach first normalizes a base10 mantissa, then pulls digits from the left side whilst emitting them (rightward) to the output. The e parameter controls the number of exponent places emitted, and can thus control where the output switches to the scientific notation. For example, setting e=2 for 0.01 or 10.0 would result in normal output. Whereas setting e=1 would result in both those values being rendered in scientific notation instead. Setting e to 0 forces that notation on for everything. TODO: this should be replaced, as it is not sufficiently accurate ******************************************************************************/ T[] format(T, D=double, U=uint) (T[] dst, D x, U decimals=Dec, int e=Exp, bool pad=Pad) {return format!(T)(dst, x, decimals, e, pad);} T[] format(T) (T[] dst, NumType x, uint decimals=Dec, int e=Exp, bool pad=Pad) { enum immutable(T)[] inf = "-inf"; enum immutable(T)[] nan = "-nan"; // strip digits from the left of a normalized base-10 number static int toDigit (ref NumType v, ref int count) { int digit; // Don't exceed max digits storable in a real // (-1 because the last digit is not always storable) if (--count <= 0) digit = 0; else { // remove leading digit, and bump digit = cast(int) v; v = (v - digit) * 10.0; } return digit + '0'; } // extract the sign bool sign = negative (x); if (sign) x = -x; if (x !<>= x) return sign ? nan : nan[1..$]; if (x is x.infinity) return sign ? inf : inf[1..$]; // assume no exponent int exp = 0; int abs = 0; // don't scale if zero if (x > 0.0) { // extract base10 exponent exp = cast(int) log10l (x); // round up a bit auto d = decimals; if (exp < 0) d -= exp; x += 0.5 / pow10 (d); // normalize base10 mantissa (0 < m < 10) abs = exp = cast(int) log10l (x); if (exp > 0) x /= pow10 (exp); else abs = -exp; // switch to exponent display as necessary if (abs >= e) e = 0; } T* p = dst.ptr; int count = NumType.dig; // emit sign if (sign) *p++ = '-'; // are we doing +/-exp format? if (e is 0) { assert (dst.length > decimals + 7); if (exp < 0) x *= pow10 (abs+1); // emit first digit, and decimal point *p++ = cast(T) toDigit (x, count); if (decimals) { *p++ = '.'; // emit rest of mantissa while (decimals-- > 0) *p++ = cast(T) toDigit (x, count); if (pad is false) { while (*(p-1) is '0') --p; if (*(p-1) is '.') --p; } } // emit exponent, if non zero if (abs) { *p++ = 'e'; *p++ = (exp < 0) ? '-' : '+'; if (abs >= 1000) { *p++ = cast(T)((abs/1000) + '0'); abs %= 1000; *p++ = cast(T)((abs/100) + '0'); abs %= 100; } else if (abs >= 100) { *p++ = cast(T)((abs/100) + '0'); abs %= 100; } *p++ = cast(T)((abs/10) + '0'); *p++ = cast(T)((abs%10) + '0'); } } else { assert (dst.length >= (((exp < 0) ? 0 : exp) + decimals + 1)); // if fraction only, emit a leading zero if (exp < 0) { x *= pow10 (abs); *p++ = '0'; } else // emit all digits to the left of point for (; exp >= 0; --exp) *p++ = cast(T )toDigit (x, count); // emit point if (decimals) { *p++ = '.'; // emit leading fractional zeros? for (++exp; exp < 0 && decimals > 0; --decimals, ++exp) *p++ = '0'; // output remaining digits, if any. Trailing // zeros are also returned from toDigit() while (decimals-- > 0) *p++ = cast(T) toDigit (x, count); if (pad is false) { while (*(p-1) is '0') --p; if (*(p-1) is '.') --p; } } } return dst [0..(p - dst.ptr)]; } } /****************************************************************************** ******************************************************************************/ debug (UnitTest) { import tango.io.Console; unittest { char[164] tmp; auto f = parse ("nan"); assert (format(tmp, f) == "nan"); f = parse ("inf"); assert (format(tmp, f) == "inf"); f = parse ("-nan"); assert (format(tmp, f) == "-nan"); f = parse (" -inf"); assert (format(tmp, f) == "-inf"); assert (format (tmp, 3.14159, 6) == "3.14159"); assert (format (tmp, 3.14159, 4) == "3.1416"); assert (parse ("3.5".dup) == 3.5); assert (format(tmp, parse ("3.14159".dup), 6) == "3.14159"); assert (format(tmp, 0.09999, 2, 0, true) == "1.00e-01"); } } debug (Float) { import tango.io.Console; void main() { char[500] tmp; /+ Cout (format(tmp, NumType.max)).newline; Cout (format(tmp, -NumType.nan)).newline; Cout (format(tmp, -NumType.infinity)).newline; Cout (format(tmp, toFloat("nan"w))).newline; Cout (format(tmp, toFloat("-nan"d))).newline; Cout (format(tmp, toFloat("inf"))).newline; Cout (format(tmp, toFloat("-inf"))).newline; +/ Cout (format(tmp, toFloat ("0.000000e+00"))).newline; Cout (format(tmp, toFloat("0x8000000000000000"))).newline; Cout (format(tmp, 1)).newline; Cout (format(tmp, -0)).newline; Cout (format(tmp, 0.000001)).newline.newline; Cout (format(tmp, 3.14159, 6, 0)).newline; Cout (format(tmp, 3.0e10, 6, 3)).newline; Cout (format(tmp, 314159, 6)).newline; Cout (format(tmp, 314159123213, 6, 15)).newline; Cout (format(tmp, 3.14159, 6, 2)).newline; Cout (format(tmp, 3.14159, 3, 2)).newline; Cout (format(tmp, 0.00003333, 6, 2)).newline; Cout (format(tmp, 0.00333333, 6, 3)).newline; Cout (format(tmp, 0.03333333, 6, 2)).newline; Cout.newline; Cout (format(tmp, -3.14159, 6, 0)).newline; Cout (format(tmp, -3e100, 6, 3)).newline; Cout (format(tmp, -314159, 6)).newline; Cout (format(tmp, -314159123213, 6, 15)).newline; Cout (format(tmp, -3.14159, 6, 2)).newline; Cout (format(tmp, -3.14159, 2, 2)).newline; Cout (format(tmp, -0.00003333, 6, 2)).newline; Cout (format(tmp, -0.00333333, 6, 3)).newline; Cout (format(tmp, -0.03333333, 6, 2)).newline; Cout.newline; Cout (format(tmp, -0.9999999, 7, 3)).newline; Cout (format(tmp, -3.0e100, 6, 3)).newline; Cout ((format(tmp, 1.0, 6))).newline; Cout ((format(tmp, 30, 6))).newline; Cout ((format(tmp, 3.14159, 6, 0))).newline; Cout ((format(tmp, 3e100, 6, 3))).newline; Cout ((format(tmp, 314159, 6))).newline; Cout ((format(tmp, 314159123213.0, 3, 15))).newline; Cout ((format(tmp, 3.14159, 6, 2))).newline; Cout ((format(tmp, 3.14159, 4, 2))).newline; Cout ((format(tmp, 0.00003333, 6, 2))).newline; Cout ((format(tmp, 0.00333333, 6, 3))).newline; Cout ((format(tmp, 0.03333333, 6, 2))).newline; Cout (format(tmp, NumType.min, 6)).newline; Cout (format(tmp, -1)).newline; Cout (format(tmp, toFloat(format(tmp, -1)))).newline; Cout.newline; } } |