diff options
author | Wayne Meissner <wmeissner@gmail.com> | 2012-02-28 09:58:08 +1000 |
---|---|---|
committer | Wayne Meissner <wmeissner@gmail.com> | 2012-02-28 09:58:08 +1000 |
commit | bc42a08504a0b1584d3b94f7f9ba66d9ac7aaaa3 (patch) | |
tree | 04fccfbd32325eede5e1738db094459786f89ec1 /ext/ffi_c/LongDouble.c | |
parent | 2631f9772bce0d15ea99271b803c9cc339bbbf26 (diff) | |
download | ffi-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.c | 59 |
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; +} |