Skip to content

Commit ccc71d5

Browse files
authored
Merge pull request #3156 from BuckleScript/hex_float
part1: add hex float notation support and basic tests around pervasives.ml constant
2 parents 7dc4938 + 45b3b25 commit ccc71d5

File tree

8 files changed

+163
-73
lines changed

8 files changed

+163
-73
lines changed

jscomp/core/js_dump.ml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -631,11 +631,11 @@ and expression_desc cxt (level:int) f x : cxt =
631631
| Number v ->
632632
let s =
633633
match v with
634-
| Float {f = v} ->
635-
Js_number.caml_float_literal_to_js_string v
634+
| Float {f} ->
635+
Js_number.caml_float_literal_to_js_string f
636636
(* attach string here for float constant folding?*)
637-
| Int { i = v; _}
638-
-> Int32.to_string v (* check , js convention with ocaml lexical convention *)
637+
| Int { i; _}
638+
-> Int32.to_string i (* check , js convention with ocaml lexical convention *)
639639
| Uint i
640640
-> Format.asprintf "%lu" i
641641
| Nint i -> Nativeint.to_string i in
@@ -645,7 +645,8 @@ and expression_desc cxt (level:int) f x : cxt =
645645
else level = 15 (* Parenthesize as well when followed by a dot. *)
646646
&& s.[0] <> 'I' (* Infinity *)
647647
&& s.[0] <> 'N' (* NaN *) in
648-
let action = fun _ -> P.string f s in
648+
let action =
649+
fun _ -> P.string f s in
649650
(
650651
if need_paren
651652
then P.paren f action

jscomp/core/js_number.ml

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -72,19 +72,25 @@ let to_string v =
7272
else Printf.sprintf "%.18g" v
7373

7474

75+
let rec is_hex_format_aux (v : string) cur =
76+
if v.[cur] = '-' || v.[cur]= '+' then
77+
is_hex_format_ox v (cur + 1)
78+
else is_hex_format_ox v cur
79+
and is_hex_format_ox v cur =
80+
v.[cur] = '0' &&
81+
(v.[cur + 1] = 'x' || v.[cur + 1] = 'X')
7582

76-
let caml_float_literal_to_js_string v =
83+
let is_hex_format (v : string) =
84+
try is_hex_format_aux v 0 with _ -> false
85+
86+
87+
let caml_float_literal_to_js_string (v : string) : string =
7788
let len = String.length v in
78-
if len >= 2 &&
79-
v.[0] = '0' &&
80-
(v.[1] = 'x' || v.[1] = 'X') then
81-
assert false
82-
(* TODO: catchup when upgraded to 4.3
83-
it does not make sense too much since js dos not
84-
support it natively
85-
*)
89+
#if OCAML_VERSION =~ ">4.03.0" then
90+
if len >= 2 && is_hex_format v then
91+
to_string (float_of_string v)
8692
else
87-
93+
#end
8894
let rec aux buf i =
8995
if i >= len then buf
9096
else

jscomp/core/js_number.mli

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
type t = float
3232

3333

34-
val to_string : t -> string
34+
(* val to_string : t -> string *)
3535

3636

3737
val caml_float_literal_to_js_string : string -> string

jscomp/runtime/caml_format.ml

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,6 @@ let caml_format_float fmt x =
570570
end;
571571
finish_formatting f !s
572572

573-
574573
(**
575574
external float_of_string : string -> float = "caml_float_of_string"
576575
pervasives.ml
@@ -584,35 +583,55 @@ let caml_format_float fmt x =
584583
FIXME: arity of float_of_string is not inferred correctly
585584
*)
586585

587-
let float_of_string : string -> (string -> 'a) -> float = [%bs.raw {|
588-
function (s, caml_failwith) {
586+
let float_of_string : string -> exn -> float =
587+
#if OCAML_VERSION =~ ">4.3.0" then
588+
fun%raw s exn -> {|
589+
{
589590
var res = +s;
590591
if ((s.length > 0) && (res === res))
591592
return res;
592593
s = s.replace(/_/g, "");
593594
res = +s;
594595
if (((s.length > 0) && (res === res)) || /^[+-]?nan$/i.test(s)) {
595596
return res;
596-
}
597-
;
598-
if (/^ *0x[0-9a-f_]+p[+-]?[0-9_]+/i.test(s)) {
599-
var pidx = s.indexOf('p');
600-
pidx = (pidx == -1) ? s.indexOf('P') : pidx;
601-
var exp = +s.substring(pidx + 1);
602-
res = +s.substring(0, pidx);
603-
return res * Math.pow(2, exp);
597+
};
598+
var m = /^ *([+-]?)0x([0-9a-f]+)\.?([0-9a-f]*)p([+-]?[0-9]+)/i.exec(s);
599+
// 1 2 3 4
600+
if(m){
601+
var m3 = m[3].replace(/0+$/,'');
602+
var mantissa = parseInt(m[1] + m[2] + m3, 16);
603+
var exponent = (m[4]|0) - 4*m3.length;
604+
res = mantissa * Math.pow(2, exponent);
605+
return res;
604606
}
605607
if (/^\+?inf(inity)?$/i.test(s))
606608
return Infinity;
607609
if (/^-inf(inity)?$/i.test(s))
608610
return -Infinity;
609-
caml_failwith("float_of_string");
611+
throw exn;
612+
}
613+
|}
614+
#else
615+
fun%raw s exn -> {|
616+
{
617+
var res = +s;
618+
if ((s.length > 0) && (res === res))
619+
return res;
620+
s = s.replace(/_/g, "");
621+
res = +s;
622+
if (((s.length > 0) && (res === res)) || /^[+-]?nan$/i.test(s)) {
623+
return res;
624+
};
625+
if (/^\+?inf(inity)?$/i.test(s))
626+
return Infinity;
627+
if (/^-inf(inity)?$/i.test(s))
628+
return -Infinity;
629+
throw exn;
610630
}
611-
612631
|}
613-
]
632+
#end
614633

615-
let caml_float_of_string s = float_of_string s caml_failwith
634+
let caml_float_of_string s = float_of_string s (Failure "float_of_string")
616635

617636
let caml_nativeint_format = caml_format_int
618637
let caml_int32_format = caml_format_int

jscomp/stdlib-406/pervasives.ml

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -201,19 +201,13 @@ external float_of_bits : int64 -> float
201201
[@@unboxed] [@@noalloc]
202202
#end
203203
#if BS then
204-
external infinity : float = "POSITIVE_INFINITY"
205-
[@@bs.val] [@@bs.scope "Number"]
206-
external neg_infinity : float = "NEGATIVE_INFINITY"
207-
[@@bs.val] [@@bs.scope "Number"]
204+
let infinity = 0x1p2047
205+
let neg_infinity = -0x1p2047
208206
external nan : float = "NaN"
209207
[@@bs.val] [@@bs.scope "Number"]
210-
external max_float : float = "MAX_VALUE"
211-
[@@bs.val] [@@bs.scope "Number"]
212-
external min_float : float = "MIN_VALUE"
213-
[@@bs.val] [@@bs.scope "Number"]
214-
(* external epsilon_float : float = "EPSILON" (* ES 2015 *)
215-
[@@bs.val] [@@bs.scope "Number"] *)
216-
let epsilon_float = 2.220446049250313e-16
208+
let max_float = 0x1.ffff_ffff_ffff_fp+1023
209+
let min_float = 0x1p-1022
210+
let epsilon_float = 0x1p-52
217211
#else
218212
let infinity =
219213
float_of_bits 0x7F_F0_00_00_00_00_00_00L

jscomp/stdlib-406/pervasives.mli

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -614,29 +614,18 @@ external int_of_float : float -> int = "%intoffloat"
614614
The result is unspecified if the argument is [nan] or falls outside the
615615
range of representable integers. *)
616616

617-
#if BS then
618-
external infinity : float = "POSITIVE_INFINITY"
619-
[@@bs.val] [@@bs.scope "Number"]
620-
external neg_infinity : float = "NEGATIVE_INFINITY"
621-
[@@bs.val] [@@bs.scope "Number"]
622-
external nan : float = "NaN"
623-
[@@bs.val] [@@bs.scope "Number"]
624-
external max_float : float = "MAX_VALUE"
625-
[@@bs.val] [@@bs.scope "Number"]
626-
external min_float : float = "MIN_VALUE"
627-
[@@bs.val] [@@bs.scope "Number"]
628-
(* external epsilon_float : float = "EPSILON" (* ES 2015 *)
629-
[@@bs.val] [@@bs.scope "Number"] *)
630-
val epsilon_float : float
631-
#else
632-
633617
val infinity : float
634618
(** Positive infinity. *)
635619

636620
val neg_infinity : float
637621
(** Negative infinity. *)
638622

623+
#if BS then
624+
external nan : float = "NaN" [@@bs.val] [@@bs.scope "Number"]
625+
(* we could also use [0. /. 0.] *)
626+
#else
639627
val nan : float
628+
#end
640629
(** A special floating-point value denoting the result of an
641630
undefined operation such as [0.0 /. 0.0]. Stands for
642631
'not a number'. Any floating-point operation with [nan] as
@@ -653,7 +642,7 @@ val min_float : float
653642
val epsilon_float : float
654643
(** The difference between [1.0] and the smallest exactly representable
655644
floating-point number greater than [1.0]. *)
656-
#end
645+
657646

658647
type fpclass =
659648
FP_normal (** Normal number, none of the below *)

jscomp/test/format_test.ml

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,96 @@ let eq loc x y =
55
suites :=
66
(loc ^" id " ^ (string_of_int !test_id), (fun _ -> Mt.Eq(x,y))) :: !suites
77

8+
let eq3 loc a b c =
9+
eq loc a b ;
10+
eq loc b c ;
11+
eq loc a c
812

913
let u () = "xx %s" ^^ "yy"
1014

15+
module M = struct
16+
external infinity : float = "POSITIVE_INFINITY"
17+
[@@bs.val] [@@bs.scope "Number"]
18+
external neg_infinity : float = "NEGATIVE_INFINITY"
19+
[@@bs.val] [@@bs.scope "Number"]
20+
external nan : float = "NaN"
21+
[@@bs.val] [@@bs.scope "Number"]
22+
external max_float : float = "MAX_VALUE"
23+
[@@bs.val] [@@bs.scope "Number"]
24+
end
1125
let () =
1226
eq __LOC__ (Format.asprintf (u ()) "x") ("xx x" ^ "yy")
27+
1328

29+
#if OCAML_VERSION =~ ">4.03.0" then
30+
let () =
31+
eq __LOC__ (0x3.fp+1) (7.875);
32+
33+
34+
eq __LOC__ (-0x3.fp+1) (-7.875);
35+
(* in standard, it is still infinity, but
36+
ideally should give an warning
37+
*)
38+
eq3 __LOC__ 0x1p2047 M.infinity infinity;
39+
eq3 __LOC__ (-0x1p2047) M.neg_infinity neg_infinity;
40+
eq3 __LOC__ max_float 0x1.ffff_ffff_ffff_fp+1023 M.max_float;
41+
eq __LOC__ (classify_float 0x1.2p2047) FP_infinite;
42+
eq __LOC__ (classify_float 0x1.1p2047) FP_infinite;
43+
44+
45+
eq __LOC__ min_float 0x1p-1022;
46+
eq __LOC__ epsilon_float 0x1p-52;
47+
eq __LOC__ 0x0.0000_0000_0000_1p-1022 5e-324;
48+
eq __LOC__
49+
(0x1.0000_0000_0000_1 -. 1.) epsilon_float;
50+
eq __LOC__
51+
(0x1p-1023 /. 0x1p-1022) 0x1p-1;
52+
eq __LOC__ (classify_float 0x1p-1023) FP_subnormal;
53+
eq __LOC__
54+
0x1p-1023 0x0.8p-1022;
55+
eq __LOC__
56+
0x0.ffff_ffff_ffff_ffff_ffp-1022
57+
0x1p-1022;
58+
59+
eq __LOC__
60+
((1. +. 0xffp0 /. 0x100p0 ) *. 8.)
61+
(0x1.ffp3);
62+
eq __LOC__
63+
((1. +. 0xfffp0 /. 0x1000p0) *. 8.)
64+
0x1.fffp3;
65+
eq __LOC__
66+
((1. +. 0xffffp0 /. 0x10000p0) *. 8.)
67+
0x1.ffffp3
68+
;;
69+
#end
70+
(*TODO: add scanf example *)
71+
72+
let f loc ls =
73+
List.iter (fun (a,b) ->
74+
eq loc (float_of_string a) b ) ls
75+
76+
#if OCAML_VERSION=~ ">4.03.0" then
77+
let () =
78+
f __LOC__ [
79+
"0x3.fp+1", 0x3.fp+1 ;
80+
" 0x3.fp2", 0x3.fp2;
81+
" 0x4.fp2", 0x4.fp2
82+
];
83+
84+
#end
85+
;;
86+
87+
#if
88+
(* OCAML_VERSION =~ ">4.03.0" *) 0
89+
then
90+
91+
92+
93+
94+
let () = eq __LOC__ (Printf.sprintf "%h" 0x3.fp+1) "0x1.f8p+2"
95+
let () = eq __LOC__ (Printf.sprintf "%H" 0x3.fp+1) "0x1.F8P+2"
96+
97+
let () = eq __LOC__ (Printf.sprintf "%h" 0.3) "0x1.3333333333333p-2"
98+
99+
#end
14100
let () = Mt.from_pair_suites __FILE__ !suites

lib/js/caml_format.js

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,10 @@
11
'use strict';
22

3-
var Curry = require("./curry.js");
43
var Caml_int32 = require("./caml_int32.js");
54
var Caml_int64 = require("./caml_int64.js");
65
var Caml_utils = require("./caml_utils.js");
76
var Caml_builtin_exceptions = require("./caml_builtin_exceptions.js");
87

9-
function caml_failwith(s) {
10-
throw [
11-
Caml_builtin_exceptions.failure,
12-
s
13-
];
14-
}
15-
168
function parse_digit(c) {
179
if (c >= 65) {
1810
if (c >= 97) {
@@ -778,8 +770,8 @@ function caml_format_float(fmt, x) {
778770
return finish_formatting(f, s);
779771
}
780772

781-
var float_of_string = (
782-
function (s, caml_failwith) {
773+
var float_of_string = function (s,exn){
774+
{
783775
var res = +s;
784776
if ((s.length > 0) && (res === res))
785777
return res;
@@ -800,13 +792,16 @@ var float_of_string = (
800792
return Infinity;
801793
if (/^-inf(inity)?$/i.test(s))
802794
return -Infinity;
803-
caml_failwith("float_of_string");
795+
throw exn;
804796
}
805797

806-
);
798+
};
807799

808800
function caml_float_of_string(s) {
809-
return Curry._2(float_of_string, s, caml_failwith);
801+
return float_of_string(s, [
802+
Caml_builtin_exceptions.failure,
803+
"float_of_string"
804+
]);
810805
}
811806

812807
var caml_nativeint_format = caml_format_int;
@@ -827,4 +822,4 @@ exports.caml_int_of_string = caml_int_of_string;
827822
exports.caml_int32_of_string = caml_int32_of_string;
828823
exports.caml_int64_of_string = caml_int64_of_string;
829824
exports.caml_nativeint_of_string = caml_nativeint_of_string;
830-
/* float_of_string Not a pure module */
825+
/* Caml_utils Not a pure module */

0 commit comments

Comments
 (0)