qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Tom Musta <tommusta@gmail.com>
To: QEMU Developers <qemu-devel@nongnu.org>
Cc: Stefan Weil <sw@weilnetz.de>, Richard Henderson <rth@twiddle.net>,
	"qemu-ppc@nongnu.org" <qemu-ppc@nongnu.org>,
	Alexander Graf <agraf@suse.de>,
	Peter Maydell <peter.maydell@linaro.org>
Subject: [Qemu-devel] [V3 PATCH] Fix float64_to_uint64
Date: Fri, 18 Oct 2013 15:05:12 -0500	[thread overview]
Message-ID: <52619478.4080703@gmail.com> (raw)
In-Reply-To: <526010E8.8090207@weilnetz.de>

The comment preceding the float64_to_uint64 routine suggests that
the implementation is broken.  And this is, indeed, the case.

This patch properly implements the conversion of a 64-bit floating
point number to an unsigned, 64 bit integer.

This contribution can be licensed under either the softfloat-2a or -2b
license.

V2: Added softfloat license statement.

V3: Modified to meet QEMU coding conventions.

Signed-off-by: Tom Musta <tommusta@gmail.com>
---
  fpu/softfloat.c |   95 ++++++++++++++++++++++++++++++++++++++++++++++++++-----
  1 files changed, 87 insertions(+), 8 deletions(-)

diff --git a/fpu/softfloat.c b/fpu/softfloat.c
index 7ba51b6..3070eaa 100644
--- a/fpu/softfloat.c
+++ b/fpu/softfloat.c
@@ -204,6 +204,47 @@ static int64 roundAndPackInt64( flag zSign, uint64_t absZ0, uint64_t absZ1 STATU
  }

  /*----------------------------------------------------------------------------
+| Takes the 128-bit fixed-point value formed by concatenating `absZ0' and
+| `absZ1', with binary point between bits 63 and 64 (between the input words),
+| and returns the properly rounded 64-bit unsigned integer corresponding to the
+| input.  Ordinarily, the fixed-point input is simply rounded to an integer,
+| with the inexact exception raised if the input cannot be represented exactly
+| as an integer.  However, if the fixed-point input is too large, the invalid
+| exception is raised and the largest unsigned integer is returned.
+*----------------------------------------------------------------------------*/
+
+static int64 roundAndPackUint64(uint64_t absZ0, uint64_t absZ1 STATUS_PARAM)
+{
+    int8 roundingMode;
+    flag roundNearestEven, increment;
+    int64_t z;
+
+    roundingMode = STATUS(float_rounding_mode);
+    roundNearestEven = (roundingMode == float_round_nearest_even);
+    increment = ((int64_t) absZ1 < 0);
+    if (!roundNearestEven) {
+        if (roundingMode == float_round_to_zero) {
+            increment = 0;
+        } else {
+            increment = (roundingMode == float_round_up) && absZ1;
+        }
+    }
+    if (increment) {
+        ++absZ0;
+        if (absZ0 == 0) {
+            float_raise(float_flag_invalid STATUS_VAR);
+            return LIT64(0xFFFFFFFFFFFFFFFF);
+        }
+        absZ0 &= ~(((uint64_t)(absZ1<<1) == 0) & roundNearestEven);
+    }
+    z = absZ0;
+    if (absZ1) {
+        STATUS(float_exception_flags) |= float_flag_inexact;
+    }
+    return z;
+}
+
+/*----------------------------------------------------------------------------
  | Returns the fraction bits of the single-precision floating-point value `a'.
  *----------------------------------------------------------------------------*/

@@ -6536,18 +6577,56 @@ uint_fast16_t float64_to_uint16_round_to_zero(float64 a STATUS_PARAM)
      return res;
  }

-/* FIXME: This looks broken.  */
-uint64_t float64_to_uint64 (float64 a STATUS_PARAM)
-{
-    int64_t v;
+/*----------------------------------------------------------------------------
+| Returns the result of converting the double-precision floating-point value
+| `a' to the 64-bit unsigned integer format.  The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic---which means in particular that the conversion is rounded
+| according to the current rounding mode.  If `a' is a NaN, the largest
+| positive integer is returned.  If the conversion overflows, the
+| largest unsigned integer is returned.  If 'a' is negative, zero is
+| returned.
+*----------------------------------------------------------------------------*/

-    v = float64_val(int64_to_float64(INT64_MIN STATUS_VAR));
-    v += float64_val(a);
-    v = float64_to_int64(make_float64(v) STATUS_VAR);
+uint64_t float64_to_uint64(float64 a STATUS_PARAM)
+{
+    flag aSign;
+    int_fast16_t aExp, shiftCount;
+    uint64_t aSig, aSigExtra;
+    a = float64_squash_input_denormal(a STATUS_VAR);

-    return v - INT64_MIN;
+    aSig = extractFloat64Frac(a);
+    aExp = extractFloat64Exp(a);
+    aSign = extractFloat64Sign(a);
+    if (aSign) {
+        if (aExp) {
+            float_raise(float_flag_invalid STATUS_VAR);
+        } else if (aSig) { /* negative denormalized */
+            float_raise(float_flag_inexact STATUS_VAR);
+        }
+        return 0;
+    }
+    if (aExp) {
+        aSig |= LIT64(0x0010000000000000);
+    }
+    shiftCount = 0x433 - aExp;
+    if (shiftCount <= 0) {
+        if (0x43E < aExp) {
+            if ((aSig != LIT64(0x0010000000000000)) ||
+                 (aExp == 0x7FF)) {
+                float_raise(float_flag_invalid STATUS_VAR);
+            }
+            return LIT64(0xFFFFFFFFFFFFFFFF);
+        }
+        aSigExtra = 0;
+        aSig <<= -shiftCount;
+    } else {
+        shift64ExtraRightJamming(aSig, 0, shiftCount, &aSig, &aSigExtra);
+    }
+    return roundAndPackUint64(aSig, aSigExtra STATUS_VAR);
  }

+
  uint64_t float64_to_uint64_round_to_zero (float64 a STATUS_PARAM)
  {
      int64_t v;
-- 
1.7.1

      parent reply	other threads:[~2013-10-18 20:05 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-10-16 14:19 [Qemu-devel] [PATCH] Fix float64_to_uint64 Tom Musta
2013-10-16 20:52 ` Richard Henderson
2013-10-16 21:01 ` Peter Maydell
2013-10-16 21:10   ` [Qemu-devel] [PATCH V2] " Tom Musta
2013-10-17  9:40     ` [Qemu-devel] [Qemu-ppc] " Alexander Graf
2013-10-17 16:31       ` Stefan Weil
2013-10-17 17:58         ` Tom Musta
2013-10-18 20:05         ` Tom Musta [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=52619478.4080703@gmail.com \
    --to=tommusta@gmail.com \
    --cc=agraf@suse.de \
    --cc=peter.maydell@linaro.org \
    --cc=qemu-devel@nongnu.org \
    --cc=qemu-ppc@nongnu.org \
    --cc=rth@twiddle.net \
    --cc=sw@weilnetz.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).