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
44 include "Exec.m";
55 include "external_module.m";
66
7+include "math.m";
8+
9+m : Math;
710 sys : Sys;
811
912 TEST_FAILED : exception;
@@ -12,6 +15,8 @@ init(args: list of string) · 2 unmodified lines
1215 {
1316 a := "foobar!";
1417 sys = load Sys Sys->PATH;
18+ m = load Math Math->PATH;
19+
1520 big_val := big 16r12345;
1621 sys->print("Hello, %s %o %d %x %bd %f\n%s%%%% %H\n\n", "world!", 8, 10, 16, big_val, 0.1, "------------------\n", a);
1722
@@ -22,6 +27,7 @@ init(args: list of string) · 4 unmodified lines
2227 args = tl args;
2328 }
2429
30+ test_floating_point();
2531 test_poly();
2632 test_function_refs();
2733 test_alt();
@@ -40,6 +46,50 @@ init(args: list of string) · 12 unmodified lines
4046 test_for_loop_function_call();
4147 }
4248
49+test_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+
4393 reverse[T](l: list of T): list of T
4494 {
4595 rl: list of T;
@@ -829,9 +879,9 @@ test_string() · 783 unmodified lines
829879 w_val := int s_w;
830880 if (w_val != 12345) raise TEST_FAILED();
831881
832− s_r := "3.14";
882+ s_r := "3.00";
833883 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();
835885
836886 s_b := "987532";
837887 b_val := big s_b;
src/vm/math/Mathmod.cpp+149−15
@@ -4,6 +4,16 @@ · 3 unmodified lines
44 // Author: arr
55 //
66
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+
717 #include <debug.h>
818 #include "Mathmod.h"
919
@@ -97,32 +107,160 @@ namespace · 87 unmodified lines
97107 };
98108
99109 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+ }
100209 }
101210
102211 void
103212 Mathmodinit(void)
104213 {
105214 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);
106222 }
107223
108224 void
109−Math_FPcontrol(vm_registers_t &r, vm_t &vm)
225+Math_FPcontrol(vm_registers_t &r, vm_t &)
110226 {
111227 auto &fp = r.stack.peek_frame()->base<F_Math_FPcontrol>();
112228
113− // [PAL] Properly implement floating point control
114− disvm::debug::log_msg(component_trace_t::builtin, log_level_t::warning, "FPControl is not properly implemented");
229+ const auto prev_fp_flags = get_all_fp_flags();
115230
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);
117241 }
118242
119243 void
120−Math_FPstatus(vm_registers_t &r, vm_t &vm)
244+Math_FPstatus(vm_registers_t &r, vm_t &)
121245 {
122246 auto &fp = r.stack.peek_frame()->base<F_Math_FPstatus>();
123247
124− // [PAL] Properly implement floating point control
125− throw vm_system_exception{ "Function not implemented" };
248+ const auto prev_e_flags = get_fp_exception_flags();
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);
126264 }
127265
128266 void
@@ -501,21 +639,17 @@ Math_gemm(vm_registers_t &r, vm_t &vm) · 372 unmodified lines
501639 }
502640
503641 void
504−Math_getFPcontrol(vm_registers_t &r, vm_t &vm)
642+Math_getFPcontrol(vm_registers_t &r, vm_t &)
505643 {
506644 auto &fp = r.stack.peek_frame()->base<F_Math_getFPcontrol>();
507−
508− // [PAL] Properly implement floating point control
509− throw vm_system_exception{ "Function not implemented" };
645+ *fp.ret = get_all_fp_flags();
510646 }
511647
512648 void
513−Math_getFPstatus(vm_registers_t &r, vm_t &vm)
649+Math_getFPstatus(vm_registers_t &r, vm_t &)
514650 {
515651 auto &fp = r.stack.peek_frame()->base<F_Math_getFPstatus>();
516−
517− // [PAL] Properly implement floating point control
518− throw vm_system_exception{ "Function not implemented" };
652+ *fp.ret = get_fp_exception_flags();
519653 }
520654
521655 void