-
Notifications
You must be signed in to change notification settings - Fork 95
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for long double and long double complex #475
Conversation
Thanks! I'd like to merge this, once the issues are worked out. First:
I've fixed this slightly differently in #478, filtering out empty directories from the |
3d8ed70
to
0876a68
Compare
I've done the rebase (my git-fu is weak here, but it seemed to work). I've tried to get the |
I've hand massaged the |
That |
The code in the OCaml distribution for hashing and serializing doubles should be a useful starting point, but it's complicated by the fact that Perhaps it's sufficient to conditionalize on |
I made a bash at hashing last night. I note that in some places the ocaml distribution goes to some lengths to make the differences in data types between platforms transparent. Do we need to go that far here? I think writing (and testing) all the required conversions for this would be quite difficult. |
One thing to worry about is that there are platforms where Here's an example program that illustrates the difference between accessing #include <stdio.h>
#include <string.h>
void print_hex_longdouble(const void *q)
{
const unsigned char *p = q;
for (size_t i = 0; i < sizeof(long double); i++)
printf("%.2x", (unsigned)p[i]);
}
int main()
{
long double x = 1.0, y = 1.0;
printf("x: %LA, y: %LA\n", x, y);
printf ("x == y: %d\n", x == y);
printf ("x: 0x"); print_hex_longdouble(&x);
printf ("\ny: 0x"); print_hex_longdouble(&y);
printf ("\n");
printf("memcmp(x,y): %d\n",
memcmp(&x, &y, sizeof(long double)));
} And here's the output of the program on my machine: $ gcc -std=c99 -pedantic -W -Wall ldbl.c
$ ./a.out
x: 0X8P-3, y: 0X8P-3
x == y: 1
x: 0x0000000000000080ff3f000000000000
y: 0x0000000000000080ff3f4ee94d560000
memcmp(x,y): -78 So a hashing implementation that uses all the bytes of a |
If it does turn out to be too much work, I think it's fine to leave long doubles as unhashable/unserialisable for now, and open an issue as a reminder to add implementations later. |
I think I'd like to try and get hashing and serialization working for the various supported platforms, but avoid dealing with any inter-platform compatibility stuff, as that seems overly complicated. I note a failure on mingw64 at the moment which I will try to investigate (some internet comments suggest long double, mingw and msvc runtime do not play well together, but we'll see). |
With the latest commits I am pretty happy with this patch.
|
Thanks! It's going to take me a little while to review this, but I'll aim to get to it over the next few days. Would you mind if I cherry-picked some of the unrelated changes (e.g. fixes to the |
I'll send a PR for the sizeof fix (there is no fix for useconds_t, just a moved list delimiter). If you like I could also squash all the commits except the 1st. This would split the patch into the bit related to ctypes internals and the Ldouble API. |
Adds two new types to ctypes - `ldouble` and `complexld`. `ldouble` is an alias for `Ldouble.t` which is implemented as a C custom_operation in `ldouble_stubs.c`. A tiny API to convert to/from floats is provided. `complexld` is an alias for `Ldouble.complex`. Unlike the other complex types it does not map to the standard Complex.t type (I dont think it would be very useful if it did as you would immediately loose the added precision). A tiny API to construct and extract the long double real and imaginary components is provided. A few tests have been updated - most importantly the test-complex one which shows long double complex in action with both the FFI and Cstubs. Issues; * no hash or (de)serialize for these types. Not sure how to implement these * The Ldouble APIs should probably be fleshed out for convenience. Basic arithmetic and to/of string. * No nice printing of values (need to update ctypes-top). * should complexld really live in Ldouble?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks -- this looks very good!
I'd like to merge once the suggested changes (which I hope aren't too onerous) are made.
@@ -1215,5 +1224,5 @@ test: build testlib tests-common $(TESTS) \ | |||
|
|||
run-%: $* | |||
@echo running $* | |||
@cd $(BUILDDIR) && CAML_LD_LIBRARY_PATH=. LD_LIBRARY_PATH=clib DYLD_LIBRARY_PATH=clib ./$*.$(BEST) -runner sequential | |||
@cd $(BUILDDIR) && CAML_LD_LIBRARY_PATH=. LD_LIBRARY_PATH=clib DYLD_LIBRARY_PATH=clib ./$*.$(BEST) -runner sequential #$($*.test_args) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line appears to be unchanged, except for a commented-out part. Can it be reverted?
@@ -32,6 +32,8 @@ let ident_of_ml_prim : type a. a Ctypes_primitive_types.ml_prim -> path = | |||
| ML_ullong -> path_of_string "Unsigned.ullong" | |||
| ML_ulong -> path_of_string "Unsigned.ulong" | |||
| ML_ushort -> path_of_string "Unsigned.ushort" | |||
| ML_ldouble -> path_of_string "Ldouble.t" | |||
| ML_complexld -> path_of_string "Ldouble.complex" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(These will need to change if the module name is changed.)
@@ -43,3 +43,4 @@ double complex ctypes_double_complex_val(value v) | |||
{ | |||
return Double_field(v, 0) + Double_field(v, 1) * I; | |||
} | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only change here is a blank line; please revert.
(** The type of long doubles. *) | ||
|
||
val to_float : t -> float | ||
(** Convert a long double to a float *) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should document the behaviour on overflow -- ideally to raise an exception.
(** Create a long double from a float *) | ||
|
||
val to_int : t -> int | ||
(** Convert a long double to an int *) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should document the behaviour on overflow -- ideally to raise an exception.
@@ -0,0 +1,258 @@ | |||
(* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A couple of changes to the naming and module organization would bring this in line with ctypes conventions.
First, calling the module LDouble
rather than Ldouble
would match ULong
, UInt32
, etc.
Second, it'd be better to have LDouble.Complex
at the same level as Ldouble
rather than nested underneath; it could perhaps be called ComplexL
or somesuch.
(I don't have a strong preference as to whether LDouble
and Complex
live at the top level (like Int32
and Int64
) or under a parent module such as Floats
(like Unsigned.ULong
)).
(* debugging only *) | ||
(*val to_hex_string : t -> string*) | ||
|
||
val add : t -> t -> t |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you're so inclined, it would be nice to have an LDouble.Infix
module with aliases (+
, -
, etc.) for the arithmetic functions (and similarly for the Complex
module).
|
||
type complex | ||
|
||
module Complex = struct |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this module please have a type t
member?
static void ldouble_serialize(value v, uintnat *wsize_32, uintnat *wsize_64) { | ||
long double *p = Data_custom_val(v); | ||
int size; | ||
caml_serialize_int_1(LDBL_MANT_DIG); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is a good idea.
(** The difference between [1.0] and the smallest exactly representable | ||
floating-point number greater than [1.0]. *) | ||
|
||
val nan : t |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There appear to be multiple nan
values for ldouble
, which isn't the case for from OCaml's float
type:
# Ldouble.(compare (neg nan) nan);;
- : int = 1
# (compare (-.nan) nan);;
- : int = 0
Regarding the
which would generate a C-format string like |
Yes, I like that approach better. A slightly nicer approach than generating a format string is to pass snprintf(buf, len, "%*.*Lf", width, precision, d) It's not quite as flexible (since you can't select between |
Regarding overflow for the various casting functions (to_float, to_int) I note that |
Yes, that's fine for now. |
I have split complex/long double into your suggested This properly needs a |
That sounds good to me. |
The revised patch looks very good. Thank you for all your work on this, @andrewray! I'm going to rebase/squash this a bit and then merge. |
Marshal tests added to test-ldouble. The implementation needs testing (and almost certainly fixing) for different endianness/ldouble sizes. Also added a to_hex_string function to show the underlying bytes, including unused ones, so I can check that the hash code is properly ignoring them.
- Seperate types into LDouble and ComplexL modules - document unspecified behaviour in conversion functions - make LDouble NANs work like floats - this also includes normalising NANs (and negative zeros) when serializing and hashing - tidy up LDouble to_string conversion - defend against bad of_string formats - (cheat) add a test to test-complex which passes/returns doubles Not addressed; Infix operators and dependancies are a bit broken. Dont use errno for error checking strtold This leads to problems with the Xen build on travis. Instead we perform the end pointer check and add a zero length string check.
This PR broke Android because there is no cexpl/clogl/cpowl there. |
Adds two new types to ctypes -
ldouble
andcomplexld
.ldouble
is an alias forLdouble.t
which is implemented asa C custom_operation in
ldouble_stubs.c
. A tiny API to convertto/from floats is provided.
complexld
is an alias forLdouble.complex
. Unlike the othercomplex types it does not map to the standard Complex.t type
(I dont think it would be very useful if it did as you would
immediately loose the added precision). A tiny API to construct
and extract the long double real and imaginary components is
provided.
A few tests have been updated - most importantly the test-complex
one which shows long double complex in action with both the FFI
and Cstubs.
Issues;
implement these
Basic arithmetic and to/of string.
make depend
but that seems to be broken sothere is a tiny hack in the makefile (see PROJECTS_DEP). Old/missing
project definitions are causes multiple iempty
-I -I -I ...
args toocamldep.