[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[tyndur-devel] [PATCH] libm: Rundungsfunktionen



+ libm: floor, ceil, round, trunc, nearbyint, fesetround, fegetround

Signed-off-by: Kevin Wolf <kevin@xxxxxxxxxx>
---
 src/modules/include/math.h           |   25 ++++
 src/modules/lib/stdlibc/math/round.c |  212 ++++++++++++++++++++++++++++++++++
 2 files changed, 237 insertions(+), 0 deletions(-)
 create mode 100644 src/modules/lib/stdlibc/math/round.c

diff --git a/src/modules/include/math.h b/src/modules/include/math.h
index 49083ab..138fff3 100644
--- a/src/modules/include/math.h
+++ b/src/modules/include/math.h
@@ -49,6 +49,12 @@
 #define M_SQRT2     1.41421356237309504880
 #define M_SQRT1_2   0.70710678118654752440
 
+enum {
+    FE_TONEAREST,
+    FE_UPWARD,
+    FE_DOWNWARD,
+    FE_TOWARDZERO,
+};
 
 
 double      tan(double);
@@ -94,5 +100,24 @@ double      acos(double);
 float       acosf(float);
 long double acosl(long double);
 
+
+double      floor(double x);
+float       floorf(float x);
+long double floorl(long double x);
+double      ceil(double x);
+float       ceilf(float x);
+long double ceill(long double x);
+double      trunc(double x);
+float       truncf(float x);
+long double truncl(long double x);
+double      round(double x);
+float       roundf(float x);
+long double roundl(long double x);
+int         fegetround(void);
+int         fesetround(int rounding_mode);
+double      nearbyint(double x);
+float       nearbyintf(float x);
+long double nearbyintl(long double x);
+
 #endif
 
diff --git a/src/modules/lib/stdlibc/math/round.c b/src/modules/lib/stdlibc/math/round.c
new file mode 100644
index 0000000..b4fd618
--- /dev/null
+++ b/src/modules/lib/stdlibc/math/round.c
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2009 The tyndur Project. All rights reserved.
+ *
+ * This code is derived from software contributed to the tyndur Project
+ * by Kevin Wolf.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <math.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#define ROUND_NEAR  "$0x000"
+#define ROUND_DOWN  "$0x400"
+#define ROUND_UP    "$0x800"
+#define ROUND_TRUNC "$0xc00"
+
+static int rounding_mode = FE_TONEAREST;
+
+/*
+ * Achtung:
+ *
+ * Die folgenden Funktionen basieren auf der Annahme, dass das RC-Feld im FPU
+ * Control Word normalerweise immer auf 0 gesetzt ist, also der neue Wert durch
+ * ein einfaches or eingetragen werden kann.
+ */
+
+long double floorl(long double x)
+{
+    long double res;
+    uint16_t control_word;
+
+    asm(
+        // Rundungsmodus setzen
+        "fnstcw %2;"
+        "mov %2, %%eax;"
+        "orl " ROUND_DOWN ", %2;"
+        "fldcw %2;"
+
+        // Zahl auf dem Stack runden
+        "frndint;"
+
+        // Wieder den Standardrundungsmodus setzen
+        "mov %%eax, %2;"
+        "fldcw %2;"
+        : "=t" (res) : "0" (x), "m" (control_word) : "eax");
+
+    return res;
+}
+
+double floor(double x)
+{
+    return floorl(x);
+}
+
+float floorf(float x)
+{
+    return floorl(x);
+}
+
+
+long double ceill(long double x)
+{
+    long double res;
+    uint16_t control_word;
+
+    asm(
+        // Rundungsmodus setzen
+        "fnstcw %2;"
+        "mov %2, %%eax;"
+        "orl " ROUND_UP ", %2;"
+        "fldcw %2;"
+
+        // Zahl auf dem Stack runden
+        "frndint;"
+
+        // Wieder den Standardrundungsmodus setzen
+        "mov %%eax, %2;"
+        "fldcw %2;"
+        : "=t" (res) : "0" (x), "m" (control_word) : "eax");
+
+    return res;
+}
+
+double ceil(double x)
+{
+    return ceill(x);
+}
+
+float ceilf(float x)
+{
+    return ceill(x);
+}
+
+long double truncl(long double x)
+{
+    long double res;
+    uint16_t control_word;
+
+    asm(
+        // Rundungsmodus setzen
+        "fnstcw %2;"
+        "mov %2, %%eax;"
+        "orl " ROUND_TRUNC ", %2;"
+        "fldcw %2;"
+
+        // Zahl auf dem Stack runden
+        "frndint;"
+
+        // Wieder den Standardrundungsmodus setzen
+        "mov %%eax, %2;"
+        "fldcw %2;"
+        : "=t" (res) : "0" (x), "m" (control_word) : "eax");
+
+    return res;
+}
+
+double trunc(double x)
+{
+    return truncl(x);
+}
+
+float truncf(float x)
+{
+    return truncl(x);
+}
+
+long double roundl(long double x)
+{
+    long double res;
+
+    asm("frndint" : "=t" (res) : "0" (x));
+
+    return res;
+}
+
+double round(double x)
+{
+    return roundl(x);
+}
+
+float roundf(float x)
+{
+    return roundl(x);
+}
+
+long double nearbyintl(long double x)
+{
+    switch (rounding_mode) {
+        case FE_TONEAREST:
+            return roundl(x);
+        case FE_TOWARDZERO:
+            return truncl(x);
+        case FE_DOWNWARD:
+            return floorl(x);
+        case FE_UPWARD:
+            return ceill(x);
+    }
+
+    abort();
+    return x;
+}
+
+double nearbyint(double x)
+{
+    return nearbyintl(x);
+}
+
+float nearbyintf(float x)
+{
+    return nearbyintl(x);
+}
+
+int fegetround(void)
+{
+    return rounding_mode;
+}
+
+int fesetround(int mode)
+{
+    switch(mode) {
+        case FE_TONEAREST:
+        case FE_TOWARDZERO:
+        case FE_DOWNWARD:
+        case FE_UPWARD:
+            rounding_mode = mode;
+            return 0;
+
+        default:
+            return -1;
+    }
+}
-- 
1.6.0.2