浏览代码

minmea_rescale: handle big values without overflow

Kosma Moczek 11 年之前
父节点
当前提交
7543c35079
共有 2 个文件被更改,包括 14 次插入5 次删除
  1. 5 5
      minmea.h
  2. 9 0
      tests.c

+ 5 - 5
minmea.h

@@ -123,10 +123,7 @@ bool minmea_parse_gpgsa(struct minmea_gpgsa *frame, const char *sentence);
 int minmea_gettimeofday(struct timeval *tv, const struct minmea_date *date, const struct minmea_time *time);
 
 /**
- * Rescale signed integer value to a different full-scale value, with rounding.
- * The "from" value in the numerator cancels out with the denominator, leaving
- * just 1/2 - which makes integer truncation act as rounding. Returns zero for
- * invalid values.
+ * Rescale a fixed-point value to a different scale. Rounds towards zero.
  */
 static inline int minmea_rescale(int value, int from, int to)
 {
@@ -134,7 +131,10 @@ static inline int minmea_rescale(int value, int from, int to)
         return 0;
     if (from == to)
         return value;
-    return (value * to + from / 2) / from;
+    if (from > to)
+        return (value + ((value > 0) - (value < 0)) * from/to/2) / (from/to);
+    else
+        return value * (to/from);
 }
 
 /**

+ 9 - 0
tests.c

@@ -417,10 +417,19 @@ END_TEST
 
 START_TEST(test_minmea_rescale)
 {
+    /* basic and edge cases. */
     ck_assert_int_eq(minmea_rescale(42, 0, 3), 0);
     ck_assert_int_eq(minmea_rescale(1234, 10, 1), 123);
     ck_assert_int_eq(minmea_rescale(1235, 10, 1), 124);
     ck_assert_int_eq(minmea_rescale(1234, 10, 1000), 123400);
+
+    /* round towards zero. */
+    ck_assert_int_eq(minmea_rescale(-1234, 10, 1), -123);
+    ck_assert_int_eq(minmea_rescale(-1235, 10, 1), -124);
+    ck_assert_int_eq(minmea_rescale(-1236, 10, 1), -124);
+
+    /* shouldn't overflow on large numbers. */
+    ck_assert_int_eq(minmea_rescale(510693608, 100000, 10000), 51069361);
 }
 END_TEST