diff --git a/src/plugins/wp/Cfloat.ml b/src/plugins/wp/Cfloat.ml
index 2dfe89528a82bccef033b2254a533c0e8ba259d9..05803bf20200ba35b968f4e3a9c805e738c91d3c 100644
--- a/src/plugins/wp/Cfloat.ml
+++ b/src/plugins/wp/Cfloat.ml
@@ -146,34 +146,49 @@ let fmake ulp value = match ulp with
   | Float64 -> F.e_fun ~result:t64 fq64 [F.e_float value]
 
 let qmake ulp q = fmake ulp (Q.to_float q)
-let re_mantissa = "\\([-+]?[0-9]*\\)"
-let re_comma = "\\(.\\(\\(0*[1-9]\\)*\\)0*\\)?"
-let re_exponent = "\\([eE]\\([-+]?[0-9]*\\)\\)?"
-let re_suffix = "\\([flFL]\\)?"
-let re_real =
-  Str.regexp (re_mantissa ^ re_comma ^ re_exponent ^ re_suffix ^ "$")
+
+let re_float = Str.regexp ".*[fldFLD]$"
+let re_mantissa = Str.regexp "[-+]?[0-9]*"
+let re_decimal = Str.regexp "\\.[0-9]*"
+let re_exponent = Str.regexp "[eE][-+]?[0-9]*"
 
 let parse_literal ~model v r =
   try
-    if Str.string_match re_real r 0 then
-      let has_suffix =
-        try ignore (Str.matched_group 7 r) ; true
-        with Not_found -> false in
-      if has_suffix && model = Float then
-        Q.of_float v
-      else
-        let ma = Str.matched_group 1 r in
-        let mb = try Str.matched_group 3 r with Not_found -> "" in
-        let me = try Str.matched_group 6 r with Not_found -> "0" in
-        let n = int_of_string me - String.length mb in
-        let d n =
-          let s = Bytes.make (succ n) '0' in
-          Bytes.set s 0 '1' ; Q.of_string (Bytes.to_string s) in
-        let m = Q.of_string (ma ^ mb) in
-        if n < 0 then Q.div m (d (-n)) else
-        if n > 0 then Q.mul m (d n) else m
-    else Q.of_float v
-  with Failure _ ->
+    if model = Float && Str.string_match re_float r 0 then Q.of_float v
+    else
+      begin
+        if not @@ Str.string_match re_mantissa r 0 then
+          raise Not_found ;
+        let mantissa = Str.matched_string r in
+        let p = Str.match_end () in (* length mantissa = p *)
+        let q, decimal =
+          if Str.string_match re_decimal r p then
+            let q = Str.match_end () in
+            q, String.sub r (p + 1) (q - p - 1)
+          else
+            p, "" in
+        let n, exponent =
+          if Str.string_match re_exponent r q then
+            let n = Str.match_end () in
+            n, String.sub r (q + 1) (n - q - 1)
+          else
+            q, "0" in
+        if n <> String.length r && not @@ Str.string_match re_float r n then
+          raise Not_found ;
+        let digits = Z.of_string @@ mantissa ^ decimal in
+        let decades = int_of_string exponent - String.length decimal in
+        let power n =
+          let s = Bytes.make (n+1) '0' in Bytes.set s 0 '1' ;
+          Z.of_string (String.of_bytes s) in
+        if decades < 0 then
+          Q.make digits @@ power (-decades)
+        else
+        if decades > 0 then
+          Q.of_bigint (Z.mul digits @@ power decades)
+        else
+          Q.of_bigint digits
+      end
+  with Failure _ | Not_found | Invalid_argument _ ->
     Warning.error "Unexpected constant literal %S" r
 
 let acsl_lit l =
diff --git a/src/plugins/wp/Changelog b/src/plugins/wp/Changelog
index fd106bfdd805723459da066371407fc91ed51114..303cbaecda2f4dd3c6fdcb11de0957c2eab4c1c8 100644
--- a/src/plugins/wp/Changelog
+++ b/src/plugins/wp/Changelog
@@ -24,6 +24,8 @@
 Plugin WP <next-release>
 ###############################################################################
 
+* WP          [2024-07-02] Fixed parsing of decimal literals
+
 ###############################################################################
 Plugin WP 29.0 (Copper)
 ###############################################################################
diff --git a/src/plugins/wp/tests/wp_acsl/oracle/real.res.oracle b/src/plugins/wp/tests/wp_acsl/oracle/real.res.oracle
new file mode 100644
index 0000000000000000000000000000000000000000..6078845907f28839066740cc1b008c16b9e35618
--- /dev/null
+++ b/src/plugins/wp/tests/wp_acsl/oracle/real.res.oracle
@@ -0,0 +1,46 @@
+# frama-c -wp [...]
+[kernel] Parsing real.c (with preprocessing)
+[wp] Running WP plugin...
+[wp] [Valid] Goal add_exits (Cfg) (Unreachable)
+[wp] [Valid] Goal add_terminates (Cfg) (Trivial)
+[wp] Warning: Missing RTE guards
+------------------------------------------------------------
+  Function add
+------------------------------------------------------------
+
+Goal Check 'A' (file real.c, line 6):
+Prove: P_EQ(1234567.0, to_f64(1234567.0)).
+
+------------------------------------------------------------
+
+Goal Check 'B' (file real.c, line 8):
+Prove: P_EQ((1234567.0/100000), to_f64((6949994351454889.0/562949953421312))).
+
+------------------------------------------------------------
+
+Goal Check 'C' (file real.c, line 10):
+Prove: P_EQ((1234567.0/10), to_f64((8483879823553331.0/68719476736))).
+
+------------------------------------------------------------
+
+Goal Check 'C' (file real.c, line 12):
+Prove: P_EQ((1234567.0/1000000000),
+         to_f64((5693435372711845.0/4611686018427387904))).
+
+------------------------------------------------------------
+
+Goal Check 'D' (file real.c, line 14):
+Prove: P_EQ((1234567.0/1000), to_f64((1357420771768533.0/1099511627776))).
+
+------------------------------------------------------------
+
+Goal Check 'E' (file real.c, line 16):
+Prove: P_EQ((1234567.0/100000000000),
+         to_f64((3643798638535581.0/295147905179352825856))).
+
+------------------------------------------------------------
+
+Goal Assigns nothing in 'add':
+Prove: true.
+
+------------------------------------------------------------
diff --git a/src/plugins/wp/tests/wp_acsl/oracle_qualif/real.res.oracle b/src/plugins/wp/tests/wp_acsl/oracle_qualif/real.res.oracle
new file mode 100644
index 0000000000000000000000000000000000000000..caf23e8b061177dc25022f1cf0fa576faaa19d5d
--- /dev/null
+++ b/src/plugins/wp/tests/wp_acsl/oracle_qualif/real.res.oracle
@@ -0,0 +1,23 @@
+# frama-c -wp [...]
+[kernel] Parsing real.c (with preprocessing)
+[wp] Running WP plugin...
+[wp] [Valid] Goal add_exits (Cfg) (Unreachable)
+[wp] [Valid] Goal add_terminates (Cfg) (Trivial)
+[wp] Warning: Missing RTE guards
+[wp] 7 goals scheduled
+[wp] [Valid] typed_add_check_A (Alt-Ergo) (Cached)
+[wp] [Valid] typed_add_check_B (Alt-Ergo) (Cached)
+[wp] [Valid] typed_add_check_C (Alt-Ergo) (Cached)
+[wp] [Valid] typed_add_check_C_2 (Alt-Ergo) (Cached)
+[wp] [Valid] typed_add_check_D (Alt-Ergo) (Cached)
+[wp] [Valid] typed_add_check_E (Alt-Ergo) (Cached)
+[wp] [Valid] typed_add_assigns (Qed)
+[wp] Proved goals:    9 / 9
+  Terminating:     1
+  Unreachable:     1
+  Qed:             1
+  Alt-Ergo:        6
+------------------------------------------------------------
+ Functions                 WP     Alt-Ergo  Total   Success
+  add                       1        6        7       100%
+------------------------------------------------------------
diff --git a/src/plugins/wp/tests/wp_acsl/real.c b/src/plugins/wp/tests/wp_acsl/real.c
new file mode 100644
index 0000000000000000000000000000000000000000..992b46f548b2794a9138b5e310cfa7944ba948a6
--- /dev/null
+++ b/src/plugins/wp/tests/wp_acsl/real.c
@@ -0,0 +1,19 @@
+//@ predicate EQ(real a, double b) = \abs(a-b) < \max(a,b) * 1e-16 ;
+
+/*@ assigns \nothing; */
+void add(double x, double y)
+{
+  /*@ check A:  EQ(    1234567,
+                       1234567.0d ); */
+  /*@ check B:  EQ(  12.345670,
+                     12.345670d   ); */
+  /*@ check C:  EQ(  12.345670e4,
+                     12.345670e4d ); */
+  /*@ check C:  EQ(  12.345670e-4,
+                     12.345670e-4d ); */
+  /*@ check D:  EQ( .1234567e4,
+                    .1234567e4d ); */
+  /*@ check E:  EQ( .1234567e-4,
+                    .1234567e-4d ); */
+  return ;
+}