summaryrefslogtreecommitdiff
path: root/ext/ffi_c/LongDouble.c
diff options
context:
space:
mode:
authorWayne Meissner <wmeissner@gmail.com>2012-02-28 09:58:08 +1000
committerWayne Meissner <wmeissner@gmail.com>2012-02-28 09:58:08 +1000
commitbc42a08504a0b1584d3b94f7f9ba66d9ac7aaaa3 (patch)
tree04fccfbd32325eede5e1738db094459786f89ec1 /ext/ffi_c/LongDouble.c
parent2631f9772bce0d15ea99271b803c9cc339bbbf26 (diff)
downloadffi-bc42a08504a0b1584d3b94f7f9ba66d9ac7aaaa3.tar.gz
Add experimental support for long double. Fixes issue #194
Diffstat (limited to 'ext/ffi_c/LongDouble.c')
-rw-r--r--ext/ffi_c/LongDouble.c59
1 files changed, 59 insertions, 0 deletions
diff --git a/ext/ffi_c/LongDouble.c b/ext/ffi_c/LongDouble.c
new file mode 100644
index 0000000..ae394c8
--- /dev/null
+++ b/ext/ffi_c/LongDouble.c
@@ -0,0 +1,59 @@
+#include "LongDouble.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <float.h>
+
+static VALUE rb_cBigDecimal = Qnil;
+static VALUE bigdecimal_load(VALUE unused);
+static VALUE bigdecimal_failed(VALUE value);
+
+VALUE
+rbffi_longdouble_new(long double ld)
+{
+ if (!RTEST(rb_cBigDecimal)) {
+ /* allow fallback if the bigdecimal library is unavailable in future ruby versions */
+ rb_cBigDecimal = rb_rescue(bigdecimal_load, Qnil, bigdecimal_failed, rb_cObject);
+ }
+
+ if (RTEST(rb_cBigDecimal) && rb_cBigDecimal != rb_cObject) {
+ char buf[128];
+ return rb_funcall(rb_cBigDecimal, rb_intern("new"), 1, rb_str_new(buf, sprintf(buf, "%.35Le", ld)));
+ }
+
+ /* Fall through to handling as a float */
+ return rb_float_new(ld);
+}
+
+long double
+rbffi_num2longdouble(VALUE value)
+{
+ if (TYPE(value) == T_FLOAT) {
+ return rb_num2dbl(value);
+ }
+
+ if (!RTEST(rb_cBigDecimal) && rb_const_defined(rb_cObject, rb_intern("BigDecimal"))) {
+ rb_cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
+ }
+
+ if (RTEST(rb_cBigDecimal) && rb_cBigDecimal != rb_cObject && RTEST(rb_obj_is_kind_of(value, rb_cBigDecimal))) {
+ VALUE s = rb_funcall(value, rb_intern("to_s"), 1, rb_str_new2("E"));
+ return strtold(RSTRING_PTR(s), NULL);
+ }
+
+ /* Fall through to handling as a float */
+ return rb_num2dbl(value);
+}
+
+
+static VALUE
+bigdecimal_load(VALUE unused)
+{
+ rb_require("bigdecimal");
+ return rb_const_get(rb_cObject, rb_intern("BigDecimal"));
+}
+
+static VALUE
+bigdecimal_failed(VALUE value)
+{
+ return value;
+}