1: %%
    2: %% %CopyrightBegin%
    3: %% 
    4: %% Copyright Ericsson AB 1997-2013. All Rights Reserved.
    5: %% 
    6: %% The contents of this file are subject to the Erlang Public License,
    7: %% Version 1.1, (the "License"); you may not use this file except in
    8: %% compliance with the License. You should have received a copy of the
    9: %% Erlang Public License along with this software. If not, it can be
   10: %% retrieved online at http://www.erlang.org/.
   11: %% 
   12: %% Software distributed under the License is distributed on an "AS IS"
   13: %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
   14: %% the License for the specific language governing rights and limitations
   15: %% under the License.
   16: %% 
   17: %% %CopyrightEnd%
   18: %%
   19: 
   20: -module(num_bif_SUITE).
   21: 
   22: -include_lib("test_server/include/test_server.hrl").
   23: 
   24: %% Tests the BIFs:
   25: %% 	abs/1
   26: %%	float/1
   27: %%	float_to_list/1
   28: %%  float_to_list/2
   29: %%	integer_to_list/1
   30: %%	list_to_float/1
   31: %%	list_to_integer/1
   32: %%	round/1
   33: %%	trunc/1
   34: %%	integer_to_binary/1
   35: %%	integer_to_binary/2
   36: %%	binary_to_integer/1
   37: 
   38: -export([all/0, suite/0, groups/0, init_per_suite/1, end_per_suite/1, 
   39: 	 init_per_group/2, end_per_group/2, t_abs/1, t_float/1,
   40: 	 t_float_to_string/1, t_integer_to_string/1,
   41: 	 t_string_to_integer/1,
   42: 	 t_string_to_float_safe/1, t_string_to_float_risky/1,
   43: 	 t_round/1, t_trunc/1
   44:      ]).
   45: 
   46: suite() -> [{ct_hooks,[ts_install_cth]}].
   47: 
   48: all() -> 
   49:     [t_abs, t_float, t_float_to_string, t_integer_to_string,
   50:      {group, t_string_to_float}, t_string_to_integer, t_round,
   51:      t_trunc].
   52: 
   53: groups() -> 
   54:     [{t_string_to_float, [],
   55:       [t_string_to_float_safe, t_string_to_float_risky]}].
   56: 
   57: init_per_suite(Config) ->
   58:     Config.
   59: 
   60: end_per_suite(_Config) ->
   61:     ok.
   62: 
   63: init_per_group(_GroupName, Config) ->
   64:     Config.
   65: 
   66: end_per_group(_GroupName, Config) ->
   67:     Config.
   68: 
   69: 
   70: t_abs(Config) when is_list(Config) ->
   71:     %% Floats.
   72:     5.5 = abs(id(5.5)),
   73:     0.0 = abs(id(0.0)),
   74:     100.0 = abs(id(-100.0)),
   75:     
   76:     %% Integers.
   77:     5 = abs(id(5)),
   78:     0 = abs(id(0)),
   79:     100 = abs(id(-100)),
   80: 
   81:     %% The largest smallnum. OTP-3190.
   82:     X = id((1 bsl 27) - 1),
   83:     X = abs(X),
   84:     X = abs(X-1)+1,
   85:     X = abs(X+1)-1,
   86:     X = abs(-X),
   87:     X = abs(-X-1)-1,
   88:     X = abs(-X+1)+1,
   89: 
   90:     %% Bignums.
   91:     BigNum = id(13984792374983749),
   92:     BigNum = abs(BigNum),
   93:     BigNum = abs(-BigNum),
   94:     ok.
   95:     
   96: t_float(Config) when is_list(Config) ->
   97:     0.0 = float(id(0)),
   98:     2.5 = float(id(2.5)),
   99:     0.0 = float(id(0.0)),
  100:     -100.55 = float(id(-100.55)),
  101:     42.0 = float(id(42)),
  102:     -100.0 = float(id(-100)),
  103: 
  104:     %% Bignums.
  105:     4294967305.0 = float(id(4294967305)),
  106:     -4294967305.0 = float(id(-4294967305)),
  107: 
  108:     %% Extremly big bignums.
  109:     Big = id(list_to_integer(id(lists:duplicate(2000, $1)))),
  110:     {'EXIT', {badarg, _}} = (catch float(Big)),
  111:     
  112:     ok.
  113: 
  114: 
  115: %% Tests float_to_list/1, float_to_list/2, float_to_binary/1, float_to_binary/2
  116: 
  117: t_float_to_string(Config) when is_list(Config) ->
  118:     test_fts("0.00000000000000000000e+00", 0.0),
  119:     test_fts("2.50000000000000000000e+01", 25.0),
  120:     test_fts("2.50000000000000000000e+00", 2.5),
  121:     test_fts("2.50000000000000000000e-01", 0.25),
  122:     test_fts("-3.50000000000000000000e+17", -350.0e15),
  123:     test_fts("1.00000000000000000000e+00",1.0),
  124:     test_fts("1.00000000000000000000e+00",1.0,  []),
  125:     test_fts("-1.00000000000000000000e+00",-1.0, []),
  126:     test_fts("-1.00000000000000000000",-1.0, [{decimals, 20}]),
  127:     {'EXIT', {badarg, _}} = (catch float_to_list(1.0,  [{decimals, -1}])),
  128:     {'EXIT', {badarg, _}} = (catch float_to_list(1.0,  [{decimals, 254}])),
  129:     {'EXIT', {badarg, _}} = (catch float_to_list(1.0,  [{scientific, 250}])),
  130:     {'EXIT', {badarg, _}} = (catch float_to_list(1.0e+300, [{decimals, 1}])),
  131:     {'EXIT', {badarg, _}} = (catch float_to_binary(1.0,  [{decimals, -1}])),
  132:     {'EXIT', {badarg, _}} = (catch float_to_binary(1.0,  [{decimals, 254}])),
  133:     {'EXIT', {badarg, _}} = (catch float_to_binary(1.0,  [{scientific, 250}])),
  134:     {'EXIT', {badarg, _}} = (catch float_to_binary(1.0e+300, [{decimals, 1}])),
  135:     test_fts("1.0e+300",1.0e+300, [{scientific, 1}]),
  136:     test_fts("1.0",1.0,  [{decimals,   249}, compact]),
  137:     test_fts("1",1.0,[{decimals,0}]),
  138:     test_fts("2",1.9,[{decimals,0}]),
  139:     test_fts("123456789012345680.0",123456789012345678.0,
  140: 	     [{decimals, 236}, compact]),
  141:     {'EXIT', {badarg, _}} = (catch float_to_list(
  142: 				     123456789012345678.0, [{decimals, 237}])),
  143:     {'EXIT', {badarg, _}} = (catch float_to_binary(
  144: 				     123456789012345678.0, [{decimals, 237}])),
  145:     test_fts("1." ++ string:copies("0", 249) ++ "e+00",
  146: 	     1.0,  [{scientific, 249}, compact]),
  147: 
  148:     X1 = float_to_list(1.0),
  149:     X2 = float_to_list(1.0, [{scientific, 20}]),
  150:     X1 = X2,
  151: 
  152:     Y1 = float_to_binary(1.0),
  153:     Y2 = float_to_binary(1.0, [{scientific, 20}]),
  154:     Y1 = Y2,
  155: 
  156:     test_fts("1.000e+00",1.0,   [{scientific, 3}]),
  157:     test_fts("1.000",1.0,   [{decimals,   3}]),
  158:     test_fts("1.0",1.0, [{decimals, 1}]),
  159:     test_fts("1.0",1.0, [{decimals, 3}, compact]),
  160:     test_fts("1.12",1.123, [{decimals, 2}]),
  161:     test_fts("1.123",1.123, [{decimals, 3}]),
  162:     test_fts("1.123",1.123, [{decimals, 3}, compact]),
  163:     test_fts("1.1230",1.123, [{decimals, 4}]),
  164:     test_fts("1.12300",1.123, [{decimals, 5}]),
  165:     test_fts("1.123",1.123, [{decimals, 5}, compact]),
  166:     test_fts("1.1234",1.1234,[{decimals, 6}, compact]),
  167:     test_fts("1.01",1.005, [{decimals, 2}]),
  168:     test_fts("-1.01",-1.005,[{decimals, 2}]),
  169:     test_fts("0.999",0.999, [{decimals, 3}]),
  170:     test_fts("-0.999",-0.999,[{decimals, 3}]),
  171:     test_fts("1.0",0.999, [{decimals, 2}, compact]),
  172:     test_fts("-1.0",-0.999,[{decimals, 2}, compact]),
  173:     test_fts("0.5",0.5,   [{decimals, 1}]),
  174:     test_fts("-0.5",-0.5,  [{decimals, 1}]),
  175:     "2.333333"  = erlang:float_to_list(7/3, [{decimals, 6}, compact]),
  176:     "2.333333"  = erlang:float_to_list(7/3, [{decimals, 6}]),
  177:     <<"2.333333">>  = erlang:float_to_binary(7/3, [{decimals, 6}, compact]),
  178:     <<"2.333333">>  = erlang:float_to_binary(7/3, [{decimals, 6}]),
  179:     test_fts("0.00000000000000000000e+00",0.0, [compact]),
  180:     test_fts("0.0",0.0,   [{decimals, 10}, compact]),
  181:     test_fts("123000000000000000000.0",1.23e20, [{decimals,   10}, compact]),
  182:     test_fts("1.2300000000e+20",1.23e20, [{scientific, 10}, compact]),
  183:     test_fts("1.23000000000000000000e+20",1.23e20, []),
  184:     ok.
  185:     
  186: test_fts(Expect, Float) ->
  187:     Expect = float_to_list(Float),
  188:     BinExpect = list_to_binary(Expect),
  189:     BinExpect = float_to_binary(Float).
  190: 
  191: test_fts(Expect, Float, Args) ->
  192:     Expect = float_to_list(Float,Args),
  193:     BinExpect = list_to_binary(Expect),
  194:     BinExpect = float_to_binary(Float,Args).
  195: 
  196: 
  197: %% Tests list_to_float/1.
  198: 
  199: t_string_to_float_safe(Config) when is_list(Config) ->
  200:     test_stf(0.0,"0.0"),
  201:     test_stf(0.0,"-0.0"),
  202:     test_stf(0.5,"0.5"),
  203:     test_stf(-0.5,"-0.5"),
  204:     test_stf(100.0,"1.0e2"),
  205:     test_stf(127.5,"127.5"),
  206:     test_stf(-199.5,"-199.5"),
  207: 
  208:     {'EXIT',{badarg,_}} = (catch list_to_float(id("0"))),
  209:     {'EXIT',{badarg,_}} = (catch list_to_float(id("0..0"))),
  210:     {'EXIT',{badarg,_}} = (catch list_to_float(id("0e12"))),
  211:     {'EXIT',{badarg,_}} = (catch list_to_float(id("--0.0"))),
  212:     {'EXIT',{badarg,_}} = (catch binary_to_float(id(<<"0">>))),
  213:     {'EXIT',{badarg,_}} = (catch binary_to_float(id(<<"0..0">>))),
  214:     {'EXIT',{badarg,_}} = (catch binary_to_float(id(<<"0e12">>))),
  215:     {'EXIT',{badarg,_}} = (catch binary_to_float(id(<<"--0.0">>))),
  216: 
  217:     UBin = <<0:3,(id(<<"0.0">>))/binary,0:5>>,
  218:     <<_:3,UnAlignedBin:3/binary,0:5>> = id(UBin),
  219:     0.0 = binary_to_float(UnAlignedBin),
  220: 
  221:     ABin = <<0:8,(id(<<"1.0">>))/binary,0:8>>,
  222:     <<_:8,AlignedBin:3/binary,0:8>> = id(ABin),
  223:     1.0 = binary_to_float(AlignedBin),
  224: 
  225:     ok.
  226: 
  227: %% This might crash the emulator...
  228: %% (Known to crash the Unix version of Erlang 4.4.1)
  229: 
  230: t_string_to_float_risky(Config) when is_list(Config) ->
  231:     Many_Ones = lists:duplicate(25000, id($1)),
  232:     id(list_to_float("2."++Many_Ones)),
  233:     {'EXIT', {badarg, _}} = (catch list_to_float("2"++Many_Ones)),
  234: 
  235:     id(binary_to_float(list_to_binary("2."++Many_Ones))),
  236:     {'EXIT', {badarg, _}} = (catch binary_to_float(
  237: 				     list_to_binary("2"++Many_Ones))),
  238:     ok.
  239: 
  240: test_stf(Expect,List) ->
  241:     Expect = list_to_float(List),
  242:     Bin = list_to_binary(List),
  243:     Expect = binary_to_float(Bin).
  244: 
  245: %% Tests round/1.
  246: 
  247: t_round(Config) when is_list(Config) ->
  248:     0 = round(id(0.0)),
  249:     0 = round(id(0.4)),
  250:     1 = round(id(0.5)),
  251:     0 = round(id(-0.4)),
  252:     -1 = round(id(-0.5)),
  253:     255 = round(id(255.3)),
  254:     256 = round(id(255.6)),
  255:     -1033 = round(id(-1033.3)),
  256:     -1034 = round(id(-1033.6)),
  257:     
  258:     % OTP-3722:
  259:     X = id((1 bsl 27) - 1),
  260:     MX = -X,
  261:     MXm1 = -X-1,
  262:     MXp1 = -X+1,
  263:     F = id(X + 0.0),
  264:     X = round(F),
  265:     X = round(F+1)-1,
  266:     X = round(F-1)+1,
  267:     MX = round(-F),
  268:     MXm1 = round(-F-1),
  269:     MXp1 = round(-F+1),
  270: 
  271:     X = round(F+0.1),
  272:     X = round(F+1+0.1)-1,
  273:     X = round(F-1+0.1)+1,
  274:     MX = round(-F+0.1),
  275:     MXm1 = round(-F-1+0.1),
  276:     MXp1 = round(-F+1+0.1),
  277: 
  278:     X = round(F-0.1),
  279:     X = round(F+1-0.1)-1,
  280:     X = round(F-1-0.1)+1,
  281:     MX = round(-F-0.1),
  282:     MXm1 = round(-F-1-0.1),
  283:     MXp1 = round(-F+1-0.1),
  284: 
  285:     0.5 = abs(round(F+0.5)-(F+0.5)),
  286:     0.5 = abs(round(F-0.5)-(F-0.5)),
  287:     0.5 = abs(round(-F-0.5)-(-F-0.5)),
  288:     0.5 = abs(round(-F+0.5)-(-F+0.5)),
  289: 
  290:     %% Bignums.
  291:     4294967296 = round(id(4294967296.1)),
  292:     4294967297 = round(id(4294967296.9)),
  293:     -4294967296 = -round(id(4294967296.1)),
  294:     -4294967297 = -round(id(4294967296.9)),
  295:     ok.
  296: 
  297: t_trunc(Config) when is_list(Config) ->
  298:     0 = trunc(id(0.0)),
  299:     5 = trunc(id(5.3333)),
  300:     -10 = trunc(id(-10.978987)),
  301: 
  302:     % The largest smallnum, converted to float (OTP-3722):
  303:     X = id((1 bsl 27) - 1),
  304:     F = id(X + 0.0),
  305:     io:format("X = ~p/~w/~w, F = ~p/~w/~w, trunc(F) = ~p/~w/~w~n",
  306: 	      [X, X, binary_to_list(term_to_binary(X)),
  307: 	       F, F, binary_to_list(term_to_binary(F)),
  308: 	       trunc(F), trunc(F), binary_to_list(term_to_binary(trunc(F)))]),
  309:     X = trunc(F),
  310:     X = trunc(F+1)-1,
  311:     X = trunc(F-1)+1,
  312:     X = -trunc(-F),
  313:     X = -trunc(-F-1)-1,
  314:     X = -trunc(-F+1)+1,
  315: 
  316:     %% Bignums.
  317:     4294967305 = trunc(id(4294967305.7)),
  318:     -4294967305 = trunc(id(-4294967305.7)),
  319:     ok.
  320: 
  321: 
  322: %% Tests integer_to_binary/1.
  323: 
  324: t_integer_to_string(Config) when is_list(Config) ->
  325:     test_its("0",0),
  326:     test_its("42",42),
  327:     test_its("-42",-42),
  328:     test_its("32768",32768),
  329:     test_its("268435455",268435455),
  330:     test_its("-268435455",-268435455),
  331:     test_its("123456932798748738738",123456932798748738738),
  332: 
  333:     %% 1 bsl 33, just beyond 32 bit
  334:     test_its("8589934592",8589934592),
  335:     test_its("-8589934592",-8589934592),
  336:     %% 1 bsl 65, just beyond 64 bit
  337:     test_its("36893488147419103232",36893488147419103232),
  338:     test_its("-36893488147419103232",-36893488147419103232),
  339: 
  340:     %% Bignums.
  341:     BigBin = id(list_to_binary(lists:duplicate(2000, id($1)))),
  342:     Big    = erlang:binary_to_integer(BigBin),
  343:     BigBin = erlang:integer_to_binary(Big),
  344: 
  345:     %% Invalid types
  346:     lists:foreach(fun(Value) ->
  347: 			  {'EXIT', {badarg, _}} = 
  348: 			      (catch erlang:integer_to_binary(Value)),
  349: 			  {'EXIT', {badarg, _}} = 
  350: 			      (catch erlang:integer_to_list(Value))
  351: 		  end,[atom,1.2,0.0,[$1,[$2]]]),
  352: 
  353:     %% Base-2 integers
  354:     test_its("0", 0, 2),
  355:     test_its("1", 1, 2),
  356:     test_its("110110", 54, 2),
  357:     test_its("-1000000", -64, 2),
  358:     %% Base-16 integers
  359:     test_its("0", 0, 16),
  360:     test_its("A", 10, 16),
  361:     test_its("D4BE", 54462, 16),
  362:     test_its("-D4BE", -54462, 16),
  363: 
  364:     lists:foreach(fun(Value) ->
  365: 			  {'EXIT', {badarg, _}} =
  366: 			      (catch erlang:integer_to_binary(Value, 8)),
  367: 			  {'EXIT', {badarg, _}} =
  368: 			      (catch erlang:integer_to_list(Value, 8))
  369: 		  end,[atom,1.2,0.0,[$1,[$2]]]),
  370: 
  371:     ok.
  372: 
  373: test_its(List,Int) ->
  374:     Int = list_to_integer(List),
  375:     Int = binary_to_integer(list_to_binary(List)).
  376: 
  377: test_its(List,Int,Base) ->
  378:     Int = list_to_integer(List, Base),
  379:     Int = binary_to_integer(list_to_binary(List), Base).
  380: 
  381: %% Tests binary_to_integer/1.
  382: 
  383: t_string_to_integer(Config) when is_list(Config) ->
  384:     0 = erlang:binary_to_integer(id(<<"00">>)),
  385:     0 = erlang:binary_to_integer(id(<<"-0">>)),
  386:     0 = erlang:binary_to_integer(id(<<"+0">>)),
  387: 
  388:     test_sti(0),
  389:     test_sti(1),
  390:     test_sti(-1),
  391:     test_sti(42),
  392:     test_sti(-12),
  393:     test_sti(32768),
  394:     test_sti(268435455),
  395:     test_sti(-268435455),
  396: 
  397:     %% 1 bsl 28 - 1, just before 32 bit bignum
  398:     test_sti(1 bsl 28 - 1),
  399:     %% 1 bsl 28, just beyond 32 bit small
  400:     test_sti(1 bsl 28),
  401:     %% 1 bsl 33, just beyond 32 bit
  402:     test_sti(1 bsl 33),
  403:     %% 1 bsl 60 - 1, just before 64 bit bignum
  404:     test_sti(1 bsl 60 - 1),
  405:     %% 1 bsl 60, just beyond 64 bit small
  406:     test_sti(1 bsl 60),
  407:     %% 1 bsl 65, just beyond 64 bit
  408:     test_sti(1 bsl 65),
  409:     %% Bignums.
  410:     test_sti(123456932798748738738,16),
  411:     test_sti(list_to_integer(lists:duplicate(2000, $1))),
  412: 
  413:     %% unalign string
  414:     Str = <<"10">>,
  415:     UnalignStr = <<0:3, (id(Str))/binary, 0:5>>,
  416:     <<_:3, SomeStr:2/binary, _:5>> = id(UnalignStr),
  417:     10 = erlang:binary_to_integer(SomeStr),
  418: 
  419:     %% Invalid types
  420:     lists:foreach(fun(Value) ->
  421: 			  {'EXIT', {badarg, _}} = 
  422: 			      (catch binary_to_integer(Value)),
  423: 			  {'EXIT', {badarg, _}} = 
  424: 			      (catch erlang:list_to_integer(Value))
  425: 		  end,[atom,1.2,0.0,[$1,[$2]]]),
  426:     
  427:     % Default base error cases
  428:     lists:foreach(fun(Value) ->
  429: 			  {'EXIT', {badarg, _}} = 
  430: 			      (catch erlang:binary_to_integer(
  431: 				       list_to_binary(Value))),
  432: 			  {'EXIT', {badarg, _}} = 
  433: 			      (catch erlang:list_to_integer(Value))
  434: 		  end,["1.0"," 1"," -1",""]),
  435:     
  436:     % Custom base error cases
  437:     lists:foreach(fun({Value,Base}) ->
  438: 			  {'EXIT', {badarg, _}} = 
  439: 			      (catch binary_to_integer(
  440: 				       list_to_binary(Value),Base)),
  441: 			  {'EXIT', {badarg, _}} = 
  442: 			      (catch erlang:list_to_integer(Value,Base))
  443: 		  end,[{" 1",1},{" 1",37},{"2",2},{"C",11},
  444: 		       {"1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111z",16},
  445: 		       {"1z111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111",16},
  446: 		       {"111z11111111",16}]),
  447:     
  448:     ok.
  449: 
  450: test_sti(Num) ->
  451:     [begin
  452: 	 io:format("Testing ~p:~p",[Num,Base]),
  453: 	 test_sti(Num,Base) 
  454:      end|| Base <- lists:seq(2,36)].
  455: 
  456: test_sti(Num,Base) ->
  457:     Num  = list_to_integer(int2list(Num,Base),Base),
  458:     Num = -1*list_to_integer(int2list(Num*-1,Base),Base),
  459:     Num  = binary_to_integer(int2bin(Num,Base),Base),
  460:     Num = -1*binary_to_integer(int2bin(Num*-1,Base),Base).
  461: 
  462: % Calling this function (which is not supposed to be inlined) prevents
  463: % the compiler from calculating the answer, so we don't test the compiler
  464: % instead of the newest runtime system.
  465: id(X) -> X.
  466: 
  467: %% Uses the printing library to to integer_to_binary conversions.
  468: int2bin(Int,Base) when Base < 37 ->
  469:     iolist_to_binary(int2list(Int,Base)).
  470: 
  471: int2list(Int,Base) when Base < 37 ->
  472:     lists:flatten(io_lib:format("~."++integer_to_list(Base)++"B",[Int])).