bcalc

cli bitcoin unit calculator
git clone git://jb55.com/bcalc
Log | Files | Refs | README | LICENSE

num.c (5745B)


      1 
      2 #include <stdio.h>
      3 #include <assert.h>
      4 #include <stdlib.h>
      5 #include <math.h>
      6 #include <inttypes.h>
      7 #include <string.h>
      8 #include "num.h"
      9 
     10 void
     11 num_init(struct num *num) {
     12   num->type = TYPE_INT;
     13   num->unit = UNIT_BTC;
     14   num->intval = 0LL;
     15 }
     16 
     17 void
     18 error_any() {
     19   fprintf(stderr, "error: --price argument required when using arbitrary units\n");
     20   exit(1);
     21 }
     22 
     23 static int64_t
     24 num_to_msat(struct num *num) {
     25   double anyval = 1.0;
     26 
     27   if (num->type == TYPE_FLOAT) {
     28     double val = num->floatval;
     29     switch (num->unit) {
     30     case UNIT_OTHER:
     31       if (g_other.unit == UNIT_NONE) error_any();
     32       double other = 1.0/(g_other.type == TYPE_FLOAT ? g_other.floatval
     33                           : (double)g_other.intval);
     34       return (int64_t)(val * BTC * other);
     35     case UNIT_MSATOSHI:
     36       return (int64_t)val;
     37     case UNIT_SATOSHI:
     38       return (int64_t)(val * SATOSHI);
     39     case UNIT_FINNEY:
     40       return (int64_t)(val * FINNEY);
     41     case UNIT_BITS:
     42       return (int64_t)(val * BITS);
     43     case UNIT_MBTC:
     44       return (int64_t)(val * MBTC);
     45     case UNIT_BTC:
     46       return (int64_t)(val * BTC);
     47     case UNIT_NONE:
     48       assert(!"got UNIT_NONE in num_to_msat");
     49     }
     50   }
     51 
     52   int64_t val = num->intval;
     53   switch (num->unit) {
     54   case UNIT_MSATOSHI:
     55     return val;
     56   case UNIT_SATOSHI:
     57     return val * SATOSHI;
     58   case UNIT_FINNEY:
     59     return val * FINNEY;
     60   case UNIT_BITS:
     61     return val * BITS;
     62   case UNIT_MBTC:
     63     return val * MBTC;
     64   case UNIT_OTHER:
     65     if (g_other.unit != UNIT_OTHER) error_any();
     66     anyval = g_other.type == TYPE_FLOAT?
     67       g_other.floatval : (double)g_other.intval;
     68   case UNIT_BTC:
     69     return val * BTC*(1.0/anyval);
     70   case UNIT_NONE:
     71     assert(!"got UNIT_NONE in num_to_msat");
     72   }
     73 }
     74 
     75 #define num_op (op)
     76 
     77 void
     78 num_add(struct num *dst, struct num *a, struct num *b) {
     79   dst->type = TYPE_INT;
     80   dst->unit = UNIT_MSATOSHI;
     81   dst->intval = num_to_msat(a) + num_to_msat(b);
     82 }
     83 
     84 
     85 void
     86 num_sub(struct num *dst, struct num *a, struct num *b) {
     87   dst->type = TYPE_INT;
     88   dst->unit = UNIT_MSATOSHI;
     89   dst->intval = num_to_msat(a) - num_to_msat(b);
     90 }
     91 
     92 int64_t
     93 unit_msat_multiple(enum unit format) {
     94   switch (format) {
     95   case UNIT_MSATOSHI: return MSATOSHI;
     96   case UNIT_SATOSHI:  return SATOSHI;
     97   case UNIT_FINNEY:   return FINNEY;
     98   case UNIT_BITS:     return BITS;
     99   case UNIT_MBTC:     return MBTC;
    100   case UNIT_OTHER:
    101     if (g_other.unit != UNIT_OTHER) error_any();
    102     return BTC / (g_other.type == TYPE_FLOAT
    103       ? g_other.floatval
    104       : g_other.intval);
    105   case UNIT_BTC:      return BTC;
    106   case UNIT_NONE:     assert(!"got UNIT_NONE in num_to_msat");
    107   }
    108 }
    109 
    110 void
    111 num_mul(struct num *dst, struct num *a, struct num *b) {
    112   assert(b->unit == UNIT_NONE);
    113   dst->type = TYPE_INT;
    114   dst->unit = UNIT_MSATOSHI;
    115   int64_t num = num_to_msat(a);
    116   switch (b->type) {
    117   case TYPE_FLOAT:
    118     dst->intval = (int64_t)(num * b->floatval);
    119     double d = (double)num * b->floatval;
    120     // FIXME: floating point sucks
    121     if (d <= 1.000000000000001L && 0.99999999999999L <= d) dst->intval = 1LL;
    122     break;
    123   case TYPE_INT:
    124     dst->intval = num * b->intval;
    125     break;
    126   }
    127 }
    128 
    129 void
    130 num_div(struct num *dst, struct num *a, struct num *b) {
    131   dst->type = TYPE_INT;
    132   dst->unit = UNIT_MSATOSHI;
    133 
    134   if (b->unit == UNIT_NONE) {
    135     dst->intval = num_to_msat(a) / (b->type == TYPE_FLOAT? b->floatval
    136                                    : b->intval);
    137   } else if (a->unit == UNIT_NONE) {
    138     assert(!"not working yet");
    139     /* dst->type = TYPE_FLOAT; */
    140     /* dst->unit = b->unit; */
    141     /* double ad = a->type == TYPE_FLOAT? a->floatval : (double)a->intval; */
    142     /* int64_t bmsat = num_to_msat(b); */
    143   }
    144   else {
    145     assert(!"shouldnt happen");
    146   }
    147 }
    148 
    149 void
    150 num_init_float(struct num *num, double d, enum unit unit) {
    151   num_init(num);
    152   num->floatval = d;
    153   num->type = TYPE_FLOAT;
    154   num->unit = unit;
    155   if (unit != UNIT_NONE) num_assign(num, num);
    156 }
    157 
    158 void
    159 num_init_int(struct num *num, int64_t val, enum unit unit) {
    160   num_init(num);
    161   num->intval = val;
    162   num->type = TYPE_INT;
    163   num->unit = unit;
    164   if (unit != UNIT_NONE) num_assign(num, num);
    165 }
    166 
    167 void
    168 num_assign(struct num *dst, struct num *a) {
    169   struct num num;
    170   num.type = TYPE_INT;
    171   num.unit = UNIT_MSATOSHI;
    172   num.intval = num_to_msat(a);
    173   *dst = num;
    174 }
    175 
    176 static void
    177 trim_zeros (char *s, int n)
    178 {
    179   char *p;
    180   int count;
    181 
    182   p = strchr (s,'.');         // Find decimal point, if any.
    183   if (p != NULL) {
    184     count = n;              // Adjust for more or less decimals.
    185     while (count >= 0) {    // Maximum decimals allowed.
    186       count--;
    187       if (*p == '\0')    // If there's less than desired.
    188         break;
    189       p++;               // Next character.
    190     }
    191 
    192     *p-- = '\0';            // Truncate string.
    193     while (*p == '0')       // Remove trailing zeros.
    194       *p-- = '\0';
    195 
    196     if (*p == '.') {        // If all decimals were zeros, remove ".".
    197       *p = '\0';
    198     }
    199   }
    200 }
    201 
    202 void
    203 num_print(struct num *num, enum unit format, char *unitname, int print_unit) {
    204   static char buffer[255];
    205   int64_t msat_multiple = unit_msat_multiple(format);
    206 
    207 
    208   double d = num->type == TYPE_FLOAT? num->floatval : (double)num->intval;
    209   d /= (double)msat_multiple;
    210   sprintf (buffer, "%.11f", d);
    211   trim_zeros(buffer, 12);
    212   printf("%s", buffer);
    213 
    214   if (print_unit)
    215     printf(" %s", unitname? unitname : unit_name(format));
    216 
    217   printf("\n");
    218 }
    219 
    220 
    221 
    222 char *
    223 unit_name(enum unit unit) {
    224   switch (unit) {
    225   case UNIT_MSATOSHI: return "msat";
    226   case UNIT_SATOSHI:  return "sat";
    227   case UNIT_FINNEY:   return "finney";
    228   case UNIT_BITS:     return "bits";
    229   case UNIT_MBTC:     return "mBTC";
    230   case UNIT_BTC:      return "BTC";
    231   case UNIT_OTHER:    return g_other_name;
    232   case UNIT_NONE:     assert(!"got UNIT_NONE in num_to_msat");
    233   default:
    234     assert(!"missing unit");
    235   }
    236 }