History

Merged in issue_11_fp (pull request #19)

Add support for floating point flags

Aaron R Robinson committed 9 years agomaster

Commit f1593fc2 parents 157f077 a50301e2 files · +201 −17Browse files

Unified · Split

2 files
  1. limbo/basic_limbo_tests.b +52 −2
  2. src/vm/math/Mathmod.cpp +149 −15
limbo/basic_limbo_tests.b+52−2
@@ -4,6 +4,9 @@ include "sys.m"; · 3 unmodified lines
4include "Exec.m";4include "Exec.m";
5include "external_module.m";5include "external_module.m";
6 6
7include "math.m";
8
9m : Math;
7sys : Sys;10sys : Sys;
8 11
9TEST_FAILED : exception;12TEST_FAILED : exception;
@@ -12,6 +15,8 @@ init(args: list of string) · 2 unmodified lines
12{15{
13 a := "foobar!";16 a := "foobar!";
14 sys = load Sys Sys->PATH;17 sys = load Sys Sys->PATH;
18 m = load Math Math->PATH;
19
15 big_val := big 16r12345;20 big_val := big 16r12345;
16 sys->print("Hello, %s %o %d %x %bd %f\n%s%%%% %H\n\n", "world!", 8, 10, 16, big_val, 0.1, "------------------\n", a);21 sys->print("Hello, %s %o %d %x %bd %f\n%s%%%% %H\n\n", "world!", 8, 10, 16, big_val, 0.1, "------------------\n", a);
17 22
@@ -22,6 +27,7 @@ init(args: list of string) · 4 unmodified lines
22 args = tl args;27 args = tl args;
23 }28 }
24 29
30 test_floating_point();
25 test_poly();31 test_poly();
26 test_function_refs();32 test_function_refs();
27 test_alt();33 test_alt();
@@ -40,6 +46,50 @@ init(args: list of string) · 12 unmodified lines
40 test_for_loop_function_call();46 test_for_loop_function_call();
41}47}
42 48
49test_floating_point()
50{
51 sys->print("test_floating_point()\n");
52
53 all_flags : con (Math->INVAL | Math->ZDIV | Math->OVFL | Math->UNFL | Math->INEX) | Math->RND_MASK;
54
55 R : real;
56 m->FPcontrol(Math->RND_NR, all_flags);
57 R = m->rint(12.1);
58 if (R >= 12.1) raise TEST_FAILED();
59
60 m->FPcontrol(Math->RND_NINF, all_flags);
61 R = m->rint(13.1);
62 if (R >= 13.1) raise TEST_FAILED();
63
64 R = m->rint(13.9);
65 if (R >= 13.9) raise TEST_FAILED();
66
67 m->FPcontrol(Math->RND_PINF, all_flags);
68 R = m->rint(14.1);
69 if (R <= 14.1) raise TEST_FAILED();
70
71 R = m->rint(14.9);
72 if (R <= 14.9) raise TEST_FAILED();
73
74 m->FPcontrol(Math->RND_Z, all_flags);
75 R = m->rint(-14.1);
76 if (R <= -14.1) raise TEST_FAILED();
77
78 R = m->rint(-14.9);
79 if (R <= -14.9) raise TEST_FAILED();
80
81 # Reset exception flags
82 m->FPstatus(0, all_flags);
83 status := m->getFPstatus();
84 if (status & Math->ZDIV) raise TEST_FAILED();
85 if (status != 0) raise TEST_FAILED();
86
87 R = 12. / real status;
88
89 status = m->getFPstatus();
90 if (!(status & Math->ZDIV)) raise TEST_FAILED();
91}
92
43reverse[T](l: list of T): list of T93reverse[T](l: list of T): list of T
44{94{
45 rl: list of T;95 rl: list of T;
@@ -829,9 +879,9 @@ test_string() · 783 unmodified lines
829 w_val := int s_w;879 w_val := int s_w;
830 if (w_val != 12345) raise TEST_FAILED();880 if (w_val != 12345) raise TEST_FAILED();
831 881
832 s_r := "3.14";882 s_r := "3.00";
833 r_val := real s_r;883 r_val := real s_r;
834 if (r_val != 3.14) raise TEST_FAILED();884 if (int m->rint(r_val * 100.) != int m->rint(3.00 * 100.)) raise TEST_FAILED();
835 885
836 s_b := "987532";886 s_b := "987532";
837 b_val := big s_b;887 b_val := big s_b;
src/vm/math/Mathmod.cpp+149−15
@@ -4,6 +4,16 @@ · 3 unmodified lines
4// Author: arr4// Author: arr
5//5//
6 6
7#ifdef _MSC_VER
8// Pragma for floating point support - https://msdn.microsoft.com/en-us/library/bfwa91s0.aspx
9#pragma fenv_access (on)
10#else
11// Pragma for floating point support - http://en.cppreference.com/w/cpp/numeric/fenv
12#pragma STDC FENV_ACCESS ON
13#endif
14
15#include <cfenv>
16
7#include <debug.h>17#include <debug.h>
8#include "Mathmod.h"18#include "Mathmod.h"
9 19
@@ -97,32 +107,160 @@ namespace · 87 unmodified lines
97 };107 };
98 108
99 const word_t Mathmodlen = 68;109 const word_t Mathmodlen = 68;
110
111 // Convert from std floating point round to limbo
112 word_t convert_to_limbo_fp_round(int r)
113 {
114 switch (r & FE_ROUND_MASK)
115 {
116 default:
117 assert(false && "Invalid floating point rounding flag");
118 case FE_TONEAREST:
119 return Math_RND_NR;
120 case FE_DOWNWARD:
121 return Math_RND_NINF;
122 case FE_UPWARD:
123 return Math_RND_PINF;
124 case FE_TOWARDZERO:
125 return Math_RND_Z;
126 }
127 }
128
129 // Convert from std floating point exception to limbo
130 word_t convert_to_limbo_fp_exception(std::fexcept_t e)
131 {
132 word_t e_flags{};
133 if (e & FE_INEXACT) e_flags |= Math_INEX;
134 if (e & FE_UNDERFLOW) e_flags |= Math_UNFL;
135 if (e & FE_OVERFLOW) e_flags |= Math_OVFL;
136 if (e & FE_DIVBYZERO) e_flags |= Math_ZDIV;
137 if (e & FE_INVALID) e_flags |= Math_INVAL;
138
139 return e_flags;
140 }
141
142 // Convert from limbo floating point round to std
143 int convert_to_std_fp_round(word_t r)
144 {
145 switch (r & Math_RND_MASK)
146 {
147 default:
148 assert(false && "Invalid floating point rounding flag");
149 case Math_RND_NR:
150 return FE_TONEAREST;
151 case Math_RND_NINF:
152 return FE_DOWNWARD;
153 case Math_RND_PINF:
154 return FE_UPWARD;
155 case Math_RND_Z:
156 return FE_TOWARDZERO;
157 }
158 }
159
160 // Convert from limbo floating point exception to std
161 std::fexcept_t convert_to_std_fp_exception(word_t e)
162 {
163 std::fexcept_t e_flags{};
164 if (e & Math_INEX) e_flags |= FE_INEXACT;
165 if (e & Math_UNFL) e_flags |= FE_UNDERFLOW;
166 if (e & Math_OVFL) e_flags |= FE_OVERFLOW;
167 if (e & Math_ZDIV) e_flags |= FE_DIVBYZERO;
168 if (e & Math_INVAL) e_flags |= FE_INVALID;
169
170 return e_flags;
171 }
172
173 // Get floating point exception flags
174 word_t get_fp_exception_flags()
175 {
176 std::fexcept_t e_flags;
177 int ec = std::fegetexceptflag(&e_flags, FE_ALL_EXCEPT);
178 if (ec != 0)
179 {
180 assert(false && "Failed to get except flag");
181 return 0;
182 }
183
184 return convert_to_limbo_fp_exception(e_flags);
185 }
186
187 // Get all floating point flags
188 word_t get_all_fp_flags()
189 {
190 const auto rnd_flag = std::fegetround();
191 const auto e_flags = get_fp_exception_flags();
192 return (convert_to_limbo_fp_round(rnd_flag) | e_flags);
193 }
194
195 // Set floating point control flags
196 void set_fp_control_flags(std::fexcept_t e_flags, std::fexcept_t e_mask, int rnd_flag, int rnd_mask)
197 {
198 int ec;
199
200 // [SPEC] No current mechanism to set floating point exception control flags
201
202 if (rnd_mask != 0)
203 {
204 ec = std::fesetround(rnd_flag & rnd_mask);
205 assert(ec == 0);
206 (void)ec;
207 }
208 }
100}209}
101 210
102void211void
103Mathmodinit(void)212Mathmodinit(void)
104{213{
105 disvm::runtime::builtin::register_module_exports(Math_PATH, Mathmodlen, Mathmodtab);214 disvm::runtime::builtin::register_module_exports(Math_PATH, Mathmodlen, Mathmodtab);
215
216 // Inferno floating point flags (http://www.vitanuova.com/inferno/man/2/math-fp.html)
217 set_fp_control_flags(
218 FE_INEXACT, // Inexact is fatal
219 FE_ALL_EXCEPT,
220 FE_TONEAREST, // Round to nearest even
221 FE_ROUND_MASK);
106}222}
107 223
108void224void
109Math_FPcontrol(vm_registers_t &r, vm_t &vm)225Math_FPcontrol(vm_registers_t &r, vm_t &)
110{226{
111 auto &fp = r.stack.peek_frame()->base<F_Math_FPcontrol>();227 auto &fp = r.stack.peek_frame()->base<F_Math_FPcontrol>();
112 228
113 // [PAL] Properly implement floating point control229 const auto prev_fp_flags = get_all_fp_flags();
114 disvm::debug::log_msg(component_trace_t::builtin, log_level_t::warning, "FPControl is not properly implemented");
115 230
116 *fp.ret = fp.mask;231 const auto e_flags = convert_to_std_fp_exception(fp.r & fp.mask);
232 const auto rnd_flag = convert_to_std_fp_round(fp.r & fp.mask);
233
234 set_fp_control_flags(
235 e_flags,
236 fp.mask & FE_ALL_EXCEPT,
237 rnd_flag,
238 fp.mask & FE_ROUND_MASK);
239
240 *fp.ret = (prev_fp_flags & fp.mask);
117}241}
118 242
119void243void
120Math_FPstatus(vm_registers_t &r, vm_t &vm)244Math_FPstatus(vm_registers_t &r, vm_t &)
121{245{
122 auto &fp = r.stack.peek_frame()->base<F_Math_FPstatus>();246 auto &fp = r.stack.peek_frame()->base<F_Math_FPstatus>();
123 247
124 // [PAL] Properly implement floating point control248 const auto prev_e_flags = get_fp_exception_flags();
125 throw vm_system_exception{ "Function not implemented" };249
250 // Clear all exceptions
251 int ec = std::feclearexcept(FE_ALL_EXCEPT);
252 assert(ec == 0);
253 (void)ec;
254
255 const auto e_flags = convert_to_std_fp_exception(fp.r);
256 const auto e_mask = convert_to_std_fp_exception(fp.mask);
257
258 // Set exception flags
259 ec = std::fesetexceptflag(&e_flags, static_cast<const int>(e_mask));
260 assert(ec == 0);
261 (void)ec;
262
263 *fp.ret = (prev_e_flags & e_mask);
126}264}
127 265
128void266void
@@ -501,21 +639,17 @@ Math_gemm(vm_registers_t &r, vm_t &vm) · 372 unmodified lines
501}639}
502 640
503void641void
504Math_getFPcontrol(vm_registers_t &r, vm_t &vm)642Math_getFPcontrol(vm_registers_t &r, vm_t &)
505{643{
506 auto &fp = r.stack.peek_frame()->base<F_Math_getFPcontrol>();644 auto &fp = r.stack.peek_frame()->base<F_Math_getFPcontrol>();
507 645 *fp.ret = get_all_fp_flags();
508 // [PAL] Properly implement floating point control
509 throw vm_system_exception{ "Function not implemented" };
510}646}
511 647
512void648void
513Math_getFPstatus(vm_registers_t &r, vm_t &vm)649Math_getFPstatus(vm_registers_t &r, vm_t &)
514{650{
515 auto &fp = r.stack.peek_frame()->base<F_Math_getFPstatus>();651 auto &fp = r.stack.peek_frame()->base<F_Math_getFPstatus>();
516 652 *fp.ret = get_fp_exception_flags();
517 // [PAL] Properly implement floating point control
518 throw vm_system_exception{ "Function not implemented" };
519}653}
520 654
521void655void