1 /*******************************************************************************
2 3 Module contains utility functions to convert floats to integer types.
4 Old conversion functions eg. ocean.math.Math.rndint or
5 ocean.math.Math.rndlong currently round x.5 to the nearest even integer. So
6 `rndint(4.5) == 4` and `rndint(5.5) == 6`. This is undesired behaviour for some
7 situations, so the functions in this module round to the nearest integer. So
8 `floatToInt(4.5, output)` sets `output == 5` and `floatToInt(5.5, output)` sets
9 output == 6 (this is round to nearest integer away from zero rounding see
10 `http://man7.org/linux/man-pages/man3/lround.3.html` for details on the
11 stdc lround, lroundf, llround, and llroundf functions).
12 13 To check for errors the functions feclearexcept and fetestexcept are used.
14 The feclearexcept(FE_ALL_EXCEPT) method is called before calling the
15 mathematical function and after the mathematical function has been called
16 the fetestexcept method is called to check for errors that occured in the
17 mathematical function $(LPAREN)for more details in these functions see
18 http://man7.org/linux/man-pages/man7/math_error.7.html$(RPAREN).
19 20 Copyright:
21 Copyright (c) 2009-2016 dunnhumby Germany GmbH.
22 All rights reserved.
23 24 License:
25 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
26 Alternatively, this file may be distributed under the terms of the Tango
27 3-Clause BSD License (see LICENSE_BSD.txt for details).
28 29 *******************************************************************************/30 31 moduleocean.math.Convert;
32 33 34 importcore.stdc.fenv;
35 36 importcore.stdc.math;
37 38 version (unittest) importocean.core.Test;
39 40 /*******************************************************************************
41 42 Rounds a float, double, or real value to the nearest (away from zero) int
43 value using lroundf.
44 45 Params:
46 input = the value to round - can be either a float, double, or real
47 output = the converted value
48 49 Returns:
50 true if the conversion has succeeded (no errors)
51 52 *******************************************************************************/53 54 publicboolroundToInt ( T )( Tinput, outintoutput )
55 {
56 feclearexcept(FE_ALL_EXCEPT);
57 staticif ( is(T == float) )
58 {
59 autotmp = lroundf(input);
60 }
61 elsestaticif ( is(T == double) )
62 {
63 autotmp = lround(input);
64 }
65 else66 {
67 staticassert (is(T == real), "roundToInt(): Input argument expected to"68 ~ " be of type float, double or real, not \"" ~ T.stringof ~ "\"");
69 autotmp = lroundl(input);
70 }
71 72 if (tmp > int.max || tmp < int.min)
73 returnfalse;
74 output = cast(int) tmp;
75 return !fetestexcept(FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW);
76 }
77 78 79 /*******************************************************************************
80 81 Rounds a float, double, or real value to the nearest (away from zero) long
82 value using llroundf.
83 84 Params:
85 input = the value to round - can be either a float, double, or real
86 output = the converted value
87 88 Returns:
89 true if the conversion has succeeded (no errors)
90 91 *******************************************************************************/92 93 publicboolroundToLong ( T )( Tinput, outlongoutput )
94 {
95 feclearexcept(FE_ALL_EXCEPT);
96 staticif ( is(T == float) )
97 {
98 output = llroundf(input);
99 }
100 elsestaticif ( is(T == double) )
101 {
102 output = llround(input);
103 }
104 else105 {
106 staticassert (is(T == real), "roundToLong(): Input argument expected to"107 ~ " be of type float, double or real, not \"" ~ T.stringof ~ "\"");
108 output = lroundl(input);
109 }
110 111 return !fetestexcept(FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW);
112 }
113 114 115 /*******************************************************************************
116 117 Method to test the conversions for the float, double, or real types.
118 119 Params:
120 T = the type of input value to test the conversions for
121 122 *******************************************************************************/123 124 privatevoidtestConversions ( T ) ( )
125 {
126 staticassert ( is(T == float) || is(T == double) || is(T == real),
127 "Type " ~ T.stringof ~ " unsupported for testConversions");
128 129 intint_result;
130 longlong_result;
131 132 // Check that converting a NaN always fails133 test(!roundToInt(T.nan, int_result), "Error converting NaN");
134 test(!roundToLong(T.nan, long_result), "Error converting NaN");
135 136 // Check conversion of a negative number (should fail for the unsigneds)137 test(roundToInt(cast(T)-4.2, int_result), "Error converting " ~ T.stringof);
138 test!("==")(int_result, -4, "Incorrect " ~ T.stringof ~ " conversion");
139 140 test(roundToLong(cast(T)-4.2, long_result), "Error converting " ~ T.stringof);
141 test!("==")(int_result, -4, "Incorrect " ~ T.stringof ~ " conversion");
142 143 // Check conversion of x.5, should round up144 test(roundToInt(cast(T)6.5, int_result), "Error converting " ~ T.stringof);
145 test!("==")(int_result, 7, "Incorrect " ~ T.stringof ~ " conversion");
146 147 test(roundToLong(cast(T)6.5, long_result), "Error converting " ~ T.stringof);
148 test!("==")(long_result, 7, "Incorrect " ~ T.stringof ~ " conversion");
149 150 // Check conversion of x.4 should round down151 test(roundToInt(cast(T)9.49, int_result), "Error converting " ~ T.stringof);
152 test!("==")(int_result, 9, "Incorrect " ~ T.stringof ~ " conversion");
153 154 test(roundToLong(cast(T)9.49, long_result), "Error converting " ~ T.stringof);
155 test!("==")(long_result, 9, "Incorrect " ~ T.stringof ~ " conversion");
156 }
157 158 159 /*******************************************************************************
160 161 Unittest; tests the float, double, and real conversions
162 163 *******************************************************************************/164 165 unittest166 {
167 testConversions!(float)();
168 testConversions!(double)();
169 testConversions!(real)();
170 }