| CODENOTIFIER | HelpYou are not signed inSign in |
Project: erlyweb
Revision: 249
Author: yarivvv
Date: 25 Jun 2008 03:04:46
Changes:fixed float formatting by using mochinum.erl
Files:| ... | ...@@ -0,0 +1,289 @@ | |
| 1 | %% @copyright 2007 Mochi Media, Inc. | |
| 2 | %% @author Bob Ippolito <bob@mochimedia.com> | |
| 3 | ||
| 4 | %% @doc Useful numeric algorithms for floats that cover some deficiencies | |
| 5 | %% in the math module. More interesting is digits/1, which implements | |
| 6 | %% the algorithm from: | |
| 7 | %% http://www.cs.indiana.edu/~burger/fp/index.html | |
| 8 | %% See also "Printing Floating-Point Numbers Quickly and Accurately" | |
| 9 | %% in Proceedings of the SIGPLAN '96 Conference on Programming Language | |
| 10 | %% Design and Implementation. | |
| 11 | ||
| 12 | -module(mochinum). | |
| 13 | -author("Bob Ippolito <bob@mochimedia.com>"). | |
| 14 | -export([digits/1, frexp/1, int_pow/2, int_ceil/1, test/0]). | |
| 15 | ||
| 16 | %% IEEE 754 Float exponent bias | |
| 17 | -define(FLOAT_BIAS, 1022). | |
| 18 | -define(MIN_EXP, -1074). | |
| 19 | -define(BIG_POW, 4503599627370496). | |
| 20 | ||
| 21 | %% External API | |
| 22 | ||
| 23 | %% @spec digits(number()) -> string() | |
| 24 | %% @doc Returns a string that accurately represents the given integer or float | |
| 25 | %% using a conservative amount of digits. Great for generating | |
| 26 | %% human-readable output, or compact ASCII serializations for floats. | |
| 27 | digits(N) when is_integer(N) -> | |
| 28 | integer_to_list(N); | |
| 29 | digits(0.0) -> | |
| 30 | "0.0"; | |
| 31 | digits(Float) -> | |
| 32 | {Frac, Exp} = frexp(Float), | |
| 33 | Exp1 = Exp - 53, | |
| 34 | Frac1 = trunc(abs(Frac) * (1 bsl 53)), | |
| 35 | [Place | Digits] = digits1(Float, Exp1, Frac1), | |
| 36 | R = insert_decimal(Place, [$0 + D || D <- Digits]), | |
| 37 | case Float < 0 of | |
| 38 | true -> | |
| 39 | [$- | R]; | |
| 40 | _ -> | |
| 41 | R | |
| 42 | end. | |
| 43 | ||
| 44 | %% @spec frexp(F::float()) -> {Frac::float(), Exp::float()} | |
| 45 | %% @doc Return the fractional and exponent part of an IEEE 754 double, | |
| 46 | %% equivalent to the libc function of the same name. | |
| 47 | %% F = Frac * pow(2, Exp). | |
| 48 | frexp(F) -> | |
| 49 | frexp1(unpack(F)). | |
| 50 | ||
| 51 | %% @spec int_pow(X::integer(), N::integer()) -> Y::integer() | |
| 52 | %% @doc Moderately efficient way to exponentiate integers. | |
| 53 | %% int_pow(10, 2) = 100. | |
| 54 | int_pow(_X, 0) -> | |
| 55 | 1; | |
| 56 | int_pow(X, N) when N > 0 -> | |
| 57 | int_pow(X, N, 1). | |
| 58 | ||
| 59 | %% @spec int_ceil(F::float()) -> integer() | |
| 60 | %% @doc Return the ceiling of F as an integer. The ceiling is defined as | |
| 61 | %% F when F == trunc(F); | |
| 62 | %% trunc(F) when F < 0; | |
| 63 | %% trunc(F) + 1 when F > 0. | |
| 64 | int_ceil(X) -> | |
| 65 | T = trunc(X), | |
| 66 | case (X - T) of | |
| 67 | Neg when Neg < 0 -> T; | |
| 68 | Pos when Pos > 0 -> T + 1; | |
| 69 | _ -> T | |
| 70 | end. | |
| 71 | ||
| 72 | ||
| 73 | %% Internal API | |
| 74 | ||
| 75 | int_pow(X, N, R) when N < 2 -> | |
| 76 | R * X; | |
| 77 | int_pow(X, N, R) -> | |
| 78 | int_pow(X * X, N bsr 1, case N band 1 of 1 -> R * X; 0 -> R end). | |
| 79 | ||
| 80 | insert_decimal(0, S) -> | |
| 81 | "0." ++ S; | |
| 82 | insert_decimal(Place, S) when Place > 0 -> | |
| 83 | L = length(S), | |
| 84 | case Place - L of | |
| 85 | 0 -> | |
| 86 | S ++ ".0"; | |
| 87 | N when N < 0 -> | |
| 88 | {S0, S1} = lists:split(L + N, S), | |
| 89 | S0 ++ "." ++ S1; | |
| 90 | N when N < 6 -> | |
| 91 | %% More places than digits | |
| 92 | S ++ lists:duplicate(N, $0) ++ ".0"; | |
| 93 | _ -> | |
| 94 | insert_decimal_exp(Place, S) | |
| 95 | end; | |
| 96 | insert_decimal(Place, S) when Place > -6 -> | |
| 97 | "0." ++ lists:duplicate(abs(Place), $0) ++ S; | |
| 98 | insert_decimal(Place, S) -> | |
| 99 | insert_decimal_exp(Place, S). | |
| 100 | ||
| 101 | insert_decimal_exp(Place, S) -> | |
| 102 | [C | S0] = S, | |
| 103 | S1 = case S0 of | |
| 104 | [] -> | |
| 105 | "0"; | |
| 106 | _ -> | |
| 107 | S0 | |
| 108 | end, | |
| 109 | Exp = case Place < 0 of | |
| 110 | true -> | |
| 111 | "e-"; | |
| 112 | false -> | |
| 113 | "e+" | |
| 114 | end, | |
| 115 | [C] ++ "." ++ S1 ++ Exp ++ integer_to_list(abs(Place - 1)). | |
| 116 | ||
| 117 | ||
| 118 | digits1(Float, Exp, Frac) -> | |
| 119 | Round = ((Frac band 1) =:= 0), | |
| 120 | case Exp >= 0 of | |
| 121 | true -> | |
| 122 | BExp = 1 bsl Exp, | |
| 123 | case (Frac /= ?BIG_POW) of | |
| 124 | true -> | |
| 125 | scale((Frac * BExp * 2), 2, BExp, BExp, | |
| 126 | Round, Round, Float); | |
| 127 | false -> | |
| 128 | scale((Frac * BExp * 4), 4, (BExp * 2), BExp, | |
| 129 | Round, Round, Float) | |
| 130 | end; | |
| 131 | false -> | |
| 132 | case (Exp == ?MIN_EXP) orelse (Frac /= ?BIG_POW) of | |
| 133 | true -> | |
| 134 | scale((Frac * 2), 1 bsl (1 - Exp), 1, 1, | |
| 135 | Round, Round, Float); | |
| 136 | false -> | |
| 137 | scale((Frac * 4), 1 bsl (2 - Exp), 2, 1, | |
| 138 | Round, Round, Float) | |
| 139 | end | |
| 140 | end. | |
| 141 | ||
| 142 | scale(R, S, MPlus, MMinus, LowOk, HighOk, Float) -> | |
| 143 | Est = int_ceil(math:log10(abs(Float)) - 1.0e-10), | |
| 144 | %% Note that the scheme implementation uses a 326 element look-up table | |
| 145 | %% for int_pow(10, N) where we do not. | |
| 146 | case Est >= 0 of | |
| 147 | true -> | |
| 148 | fixup(R, S * int_pow(10, Est), MPlus, MMinus, Est, | |
| 149 | LowOk, HighOk); | |
| 150 | false -> | |
| 151 | Scale = int_pow(10, -Est), | |
| 152 | fixup(R * Scale, S, MPlus * Scale, MMinus * Scale, Est, | |
| 153 | LowOk, HighOk) | |
| 154 | end. | |
| 155 | ||
| 156 | fixup(R, S, MPlus, MMinus, K, LowOk, HighOk) -> | |
| 157 | TooLow = case HighOk of | |
| 158 | true -> | |
| 159 | (R + MPlus) >= S; | |
| 160 | false -> | |
| 161 | (R + MPlus) > S | |
| 162 | end, | |
| 163 | case TooLow of | |
| 164 | true -> | |
| 165 | [(K + 1) | generate(R, S, MPlus, MMinus, LowOk, HighOk)]; | |
| 166 | false -> | |
| 167 | [K | generate(R * 10, S, MPlus * 10, MMinus * 10, LowOk, HighOk)] | |
| 168 | end. | |
| 169 | ||
| 170 | generate(R0, S, MPlus, MMinus, LowOk, HighOk) -> | |
| 171 | D = R0 div S, | |
| 172 | R = R0 rem S, | |
| 173 | TC1 = case LowOk of | |
| 174 | true -> | |
| 175 | R =< MMinus; | |
| 176 | false -> | |
| 177 | R < MMinus | |
| 178 | end, | |
| 179 | TC2 = case HighOk of | |
| 180 | true -> | |
| 181 | (R + MPlus) >= S; | |
| 182 | false -> | |
| 183 | (R + MPlus) > S | |
| 184 | end, | |
| 185 | case TC1 of | |
| 186 | false -> | |
| 187 | case TC2 of | |
| 188 | false -> | |
| 189 | [D | generate(R * 10, S, MPlus * 10, MMinus * 10, | |
| 190 | LowOk, HighOk)]; | |
| 191 | true -> | |
| 192 | [D + 1] | |
| 193 | end; | |
| 194 | true -> | |
| 195 | case TC2 of | |
| 196 | false -> | |
| 197 | [D]; | |
| 198 | true -> | |
| 199 | case R * 2 < S of | |
| 200 | true -> | |
| 201 | [D]; | |
| 202 | false -> | |
| 203 | [D + 1] | |
| 204 | end | |
| 205 | end | |
| 206 | end. | |
| 207 | ||
| 208 | unpack(Float) -> | |
| 209 | <<Sign:1, Exp:11, Frac:52>> = <<Float:64/float>>, | |
| 210 | {Sign, Exp, Frac}. | |
| 211 | ||
| 212 | frexp1({_Sign, 0, 0}) -> | |
| 213 | {0.0, 0}; | |
| 214 | frexp1({Sign, 0, Frac}) -> | |
| 215 | Exp = log2floor(Frac), | |
| 216 | <<Frac1:64/float>> = <<Sign:1, ?FLOAT_BIAS:11, (Frac-1):52>>, | |
| 217 | {Frac1, -(?FLOAT_BIAS) - 52 + Exp}; | |
| 218 | frexp1({Sign, Exp, Frac}) -> | |
| 219 | <<Frac1:64/float>> = <<Sign:1, ?FLOAT_BIAS:11, Frac:52>>, | |
| 220 | {Frac1, Exp - ?FLOAT_BIAS}. | |
| 221 | ||
| 222 | log2floor(Int) -> | |
| 223 | log2floor(Int, 0). | |
| 224 | ||
| 225 | log2floor(0, N) -> | |
| 226 | N; | |
| 227 | log2floor(Int, N) -> | |
| 228 | log2floor(Int bsr 1, 1 + N). | |
| 229 | ||
| 230 | ||
| 231 | test() -> | |
| 232 | ok = test_frexp(), | |
| 233 | ok = test_int_ceil(), | |
| 234 | ok = test_int_pow(), | |
| 235 | ok = test_digits(), | |
| 236 | ok. | |
| 237 | ||
| 238 | test_int_ceil() -> | |
| 239 | 1 = int_ceil(0.0001), | |
| 240 | 0 = int_ceil(0.0), | |
| 241 | 1 = int_ceil(0.99), | |
| 242 | 1 = int_ceil(1.0), | |
| 243 | -1 = int_ceil(-1.5), | |
| 244 | -2 = int_ceil(-2.0), | |
| 245 | ok. | |
| 246 | ||
| 247 | test_int_pow() -> | |
| 248 | 1 = int_pow(1, 1), | |
| 249 | 1 = int_pow(1, 0), | |
| 250 | 1 = int_pow(10, 0), | |
| 251 | 10 = int_pow(10, 1), | |
| 252 | 100 = int_pow(10, 2), | |
| 253 | 1000 = int_pow(10, 3), | |
| 254 | ok. | |
| 255 | ||
| 256 | test_digits() -> | |
| 257 | "0" = digits(0), | |
| 258 | "0.0" = digits(0.0), | |
| 259 | "1.0" = digits(1.0), | |
| 260 | "-1.0" = digits(-1.0), | |
| 261 | "0.1" = digits(0.1), | |
| 262 | "0.01" = digits(0.01), | |
| 263 | "0.001" = digits(0.001), | |
| 264 | ok. | |
| 265 | ||
| 266 | test_frexp() -> | |
| 267 | %% zero | |
| 268 | {0.0, 0} = frexp(0.0), | |
| 269 | %% one | |
| 270 | {0.5, 1} = frexp(1.0), | |
| 271 | %% negative one | |
| 272 | {-0.5, 1} = frexp(-1.0), | |
| 273 | %% small denormalized number | |
| 274 | %% 4.94065645841246544177e-324 | |
| 275 | <<SmallDenorm/float>> = <<0,0,0,0,0,0,0,1>>, | |
| 276 | {0.5, -1073} = frexp(SmallDenorm), | |
| 277 | %% large denormalized number | |
| 278 | %% 2.22507385850720088902e-308 | |
| 279 | <<BigDenorm/float>> = <<0,15,255,255,255,255,255,255>>, | |
| 280 | {0.99999999999999978, -1022} = frexp(BigDenorm), | |
| 281 | %% small normalized number | |
| 282 | %% 2.22507385850720138309e-308 | |
| 283 | <<SmallNorm/float>> = <<0,16,0,0,0,0,0,0>>, | |
| 284 | {0.5, -1021} = frexp(SmallNorm), | |
| 285 | %% large normalized number | |
| 286 | %% 1.79769313486231570815e+308 | |
| 287 | <<LargeNorm/float>> = <<127,239,255,255,255,255,255,255>>, | |
| 288 | {0.99999999999999989, 1024} = frexp(LargeNorm), | |
| 289 | ok. |
| ... | ...@@ -1,4 +1,4 @@ | |
| 1 | {"src/erlyweb/*", [debug_info, {outdir, "ebin"}, {i,"/opt/local/lib/yaws/include"}]}. | |
| 2 | %{"src/erlyweb/*", [debug_info, {outdir, "ebin"}, {i,"/Users/yariv/yaws/include"}]}. | |
| 1 | %{"src/erlyweb/*", [debug_info, {outdir, "ebin"}, {i,"/opt/local/lib/yaws/include"}]}. | |
| 2 | {"src/erlyweb/*", [debug_info, {outdir, "ebin"}, {i,"/Users/yariv/yaws/include"}]}. | |
| 3 | 3 | {"src/erlydb/*", [debug_info, {outdir, "ebin"}]}. |
| 4 | 4 | {"src/erlsql/*", [debug_info, {outdir, "ebin"}]}. |
| ... | ...@@ -7,3 +7,4 @@ | |
| 7 | 7 | {"src/erlang-mysql-driver/*", [debug_info, {outdir, "ebin"}]}. |
| 8 | 8 | {"src/erlang-psql-driver/*", [debug_info, strict_record_tests, {outdir, "ebin"}]}. |
| 9 | {"src/lib/*", [debug_info, strict_record_tests, {outdir, "ebin"}]}. | |
| 9 | 10 |
| ... | ...@@ -200,9 +200,11 @@ | |
| 200 | 200 | Ewc |
| 201 | 201 | end, |
| 202 | case is_redirect(A, Req) of | |
| 203 | {true, Val} -> Val; | |
| 204 | _ -> | |
| 205 | handle_request(A, AppController, Req, AppData) | |
| 206 | end. | |
| 202 | Res = case is_redirect(A, Req) of | |
| 203 | {true, Val} -> | |
| 204 | Val; | |
| 205 | _ -> | |
| 206 | handle_request(A, AppController, Req, AppData) | |
| 207 | end, | |
| 208 | Res. | |
| 207 | 209 | |
| 208 | 210 | %% checks that at least 3 seconds have passed since the last compilation |
| ... | ...@@ -125,6 +125,5 @@ | |
| 125 | 125 | integer_to_list(Val); |
| 126 | 126 | encode(Val, false) when is_float(Val) -> |
| 127 | [Res] = io_lib:format("~w", [Val]), | |
| 128 | Res; | |
| 127 | mochinum:digits(Val); | |
| 129 | 128 | encode({datetime, Val}, AsBinary) -> |
| 130 | 129 | encode(Val, AsBinary); |