aboutsummaryrefslogblamecommitdiffstats
path: root/lib/hipe/test/basic_SUITE_data/basic_floats.erl
blob: ce28f6e156bc0ef4c77d4fb3ef017e1ef4b7ac4e (plain) (tree)
1
2
3
4
5
6
7
8
9








                                                                      
                                                                      








                                    
                                                                 
                               
                                 



















                                                                      
 









                                                                      

                           









































































                                                                                                                                                                                                                                                                                                                                                                                                              
          








                                                                      
                                                                      
                      
                                                                 

                                                                      
                                                    
                      
                                                           









                                                                      
                    
 






















                                                                      




























                                                                      
 







                                                                      

                            

                                                 

        
                     

                                            




















                                                        
%%% -*- erlang-indent-level: 2 -*-
%%%-------------------------------------------------------------------
%%% Author: Kostis Sagonas
%%%
%%% Contains tests that manipulate floating point numbers.
%%%-------------------------------------------------------------------
-module(basic_floats).

-export([test/0]).
-export([test_fmt_double_fpe_leak/0]).   % suppress the unused warning

test() ->
  ok = test_arith_ops(),
  ok = test_fp_ebb(),
  ok = test_fp_phi(),
  ok = test_big_bad_float(),
  ok = test_catch_bad_fp_arith(),
  ok = test_catch_fp_conv(),
  ok = test_fp_with_fp_exceptions(),
  %% ok = test_fmt_double_fpe_leak(),    % this requires printing
  ok = test_icode_type_crash(),
  ok = test_icode_type_crash_2(),
  ok.

%%--------------------------------------------------------------------

test_arith_ops() ->
  E = 2.5617,
  5.703200000000001  = add(E),
  0.5798000000000001 = sub(E),
  8.047580550000001 = mult(E),
  -6.023e23 = negate(6.023e23),
  ok.

add(X) ->
  3.1415 + X.

sub(X) ->
  3.1415 - X.

mult(X) ->
  3.1415 * X.

%% tests the translation of the fnegate BEAM instruction.
negate(X) ->
  - (X + 0.0).

%%--------------------------------------------------------------------
%% Test the construction of overlapping extended basic blocks where
%% BEAM has constructed one and hipe_icode_fp constructs the other.
%%--------------------------------------------------------------------

test_fp_ebb() ->
  1.0 = foo(2 * math:pi()),
  1.0 = bar(2 * math:pi()),
  ok.

foo(X) ->
  X / (2 * math:pi()).

bar(X) ->
  F = float_two(),
  case F < 3.0 of
    true -> (X * F) / ((2 * F) * math:pi());
    false -> weird
  end.

float_two() ->
  2.0.

%%--------------------------------------------------------------------

test_fp_phi() ->
  10 = fp_phi(10, 100),
  undefined = fp_phi(1.1e302, 0.000000001),
  ok.

fp_phi(A, B) ->
  case catch A / B of
    {'EXIT', _Reason} -> undefined;
    _ -> round(100 * (A / B))
  end.

%%--------------------------------------------------------------------

-define(BS, "93904329458954829589425849258998492384932849328493284932849328493284932389248329432932483294832949245827588578423578435783475834758375837580745807304258924584295924588459834958349589348589345934859384958349583945893458934859438593485995348594385943859438593458934589345938594385934859483958348934589435894859485943859438594594385938459438595034950439504395043950495043593485943758.0").

test_big_bad_float() ->
  ok = try f2l(?BS) catch error:badarg -> ok end,
  ok = case catch f2l(?BS) of {'EXIT', {badarg, _}} -> ok end,
  ok.

f2l(F) ->
  float_to_list(list_to_float(F)).

%%--------------------------------------------------------------------
%% Tests catching of floating point bad arithmetic.

test_catch_bad_fp_arith() ->
 5.7 = f(2.56),
 {'EXIT', {badarith, _}} = bad_arith(9.9),
 ok.

f(F) when is_float(F) -> F + 3.14.

bad_arith(F) when is_float(F) ->
  catch F * 1.70000e+308.

%%--------------------------------------------------------------------
%% Tests proper catching of exceptions due to illegal convertion of
%% bignums to floating point numbers.

test_catch_fp_conv() ->
  F = 1.7e308, %% F is a number very close to a maximum float.
  ok = big_arith(F),
  ok = big_const_float(F),
  ok.

big_arith(F) ->
  I = trunc(F),
  {'EXIT', {badarith, _}} = big_int_arith(I),
  ok.

big_int_arith(I) when is_integer(I) ->
  catch(3.0 + 2*I).

big_const_float(F) ->
  I = trunc(F),
  badarith = try (1/(2*I)) catch error:Err -> Err end,
  _ = 2/I,
  {'EXIT', _} = (catch 4/(2*I)),
  ok.

%%--------------------------------------------------------------------
%% Forces floating point exceptions and tests that subsequent, legal,
%% operations are calculated correctly.

test_fp_with_fp_exceptions() ->
  0.0 = math:log(1.0),
  badarith = try math:log(float_minus_one()) catch error:E1 -> E1 end,
  0.0 = math:log(1.0),
  badarith = try math:log(float_zero()) catch error:E2 -> E2 end,
  0.0 = math:log(1.0),
  %% An old-fashioned exception here just so as to test this case also
  {'EXIT', _} = (catch fp_mult(3.23e133, 3.57e257)),
  0.0 = math:log(1.0),
  badarith = try fp_div(5.0, 0.0) catch error:E3 -> E3 end,
  0.0 = math:log(1.0),
  ok.

fp_mult(X, Y) -> X * Y.

fp_div(X, Y) -> X / Y.

%% The following two function definitions appear here just to shut
%% off 'expression will fail with a badarg' warnings from the compiler

float_zero() -> 0.0.

float_minus_one() -> -1.0.

%%--------------------------------------------------------------------
%% Test that erl_printf_format.c:fmt_double() does not leak pending FP
%% exceptions to subsequent code.  This used to break x87 FP code on
%% 32-bit x86.  Based on a problem report from Richard Carlsson.

test_fmt_double_fpe_leak() ->
  test_fmt_double_fpe_leak(float_zero(), int_two()),
  ok.

%% We need the specific sequence of erlang:display/1 on a float that
%% triggers faulting ops in fmt_double() followed by a simple FP BIF.
%% We also need to repeat this at least three times.
test_fmt_double_fpe_leak(X, Y) ->
  erlang:display(X), _ = math:log10(Y),
  erlang:display(X), _ = math:log10(Y),
  erlang:display(X), _ = math:log10(Y),
  erlang:display(X), _ = math:log10(Y),
  erlang:display(X),
  math:log10(Y).

int_two() -> 2.

%%--------------------------------------------------------------------
%% Contains code which confuses the icode_type analysis and results
%% in a compiler crash.  Stipped down from code sent by Paul Guyot.
%% Compiles alright with the option 'no_icode_type' but that defeats
%% the purpose of the test.

test_icode_type_crash() ->
  Fun = f(1, 2, 3),
  42.0 = Fun(),
  ok.

f(A, B, C) ->
  fun () ->
      X = case A of
	    0 -> 1 / B;
	    _ -> A / C
	  end,
      Y = case B of
	    a -> 1.0;
	    b -> 2.0;
	    _ -> 6.0
	  end,
      Z = case C of
	    c -> 0.1 * X;
	    _ -> 7.0
	  end,
      Y * Z
  end.

%%--------------------------------------------------------------------
%% Contains another case that crashed hipe_icode_fp. This sample was
%% sent by Mattias Jansson (25 Nov 2015). It compiled alright with the
%% option 'no_icode_type' but that defeats the purpose of the test.
%% Unfortunately, the execution of this code goes into an infinite
%% loop, even if the map in the second argument of eat_what/2 gets the
%% appropriate key-value pairs. Still, it is retained as a test
%% because it exposed a different crash than test_icode_type_crash/0.

test_icode_type_crash_2() ->
  {'EXIT', {function_clause, _}} = (catch eat()),
  ok.

eat() ->
  eat_what(1.0, #{}).

eat_what(Factor, #{rat_type := LT} = Rat) ->
  #{cheese := Cheese} = Rat,
  UnitCheese = Cheese / 2,
  RetA = case eat() of
	   {full, RetA1} ->
	     CheeseB2 = min(RetA1, UnitCheese) * Factor,
	     case eat() of
	       full -> {win, RetA1};
	       hungry -> {partial, RetA1 - CheeseB2}
	     end;
	   AOther -> AOther
	 end,
  RetB = case eat() of
	   {full, RetB1} ->
	     CheeseA2 = min(RetB1, UnitCheese) * Factor,
	     rat:init(single, LT, CheeseA2),
	     case eat() of
	       full -> {full, RetB1};
	       hungry -> {hungry, RetB1 - CheeseA2}
	     end
	 end,
  {RetA, RetB}.