-8

I need to send data to The Things Network and the data need to in bytes

To send data back and forth over The Things Network you’ll need to use bytes

Some functions return a float with 2 decimal

23.56 or 4.32

I have big difficulties to convert my float and save it to a varaible uint8_t my data. I have to find how to convert my float variable

I have a loop like this:

/*
NOTE:
uinit8_t mydata[64];
byte nbMaxCapteurs = 10;
mesMesures[i][mesCapteur[i].idDuCapteur].valeur => this is a float
*/

memset(mydata, 0xFF, 64);
uint8_t * ptr = mydata;
for (byte i = 0; i < nbMaxCapteurs; i++) {
  if (mesCapteur[i].actif) {
    *ptr = mesMesures[i][mesCapteur[i].idDuCapteur].valeur;
    ptr += 15; // How to know the number of step to move the pointer
    if (i < nbCapteurActif + 1) {
      *ptr = 0x2C; // virgule
      ptr += sizeof(char);
    }
  } else {
    *ptr = 0x2C; // virgule pour les capteurs inactifs
    ptr += sizeof(char);
  }
}
*ptr = '00'; // Close \0
ptr += sizeof(char);
printData();

I am very new to such conversions. The real problem is here:

*ptr = mesMesures[i][mesCapteur[i].idDuCapteur].valeur;

printData prints this:

04 FF FF FF FF FF FF FF FF FF FF FF FF FF FF 2C 18 FF FF FF FF FF FF FF FF FF FF FF FF FF FF 2C CB FF FF FF FF FF FF FF FF FF FF FF FF FF FF 2C A8 FF FF FF FF FF FF FF FF FF FF FF FF FF FF 2C

But it should print at least this:

34 2E 33 32 FF FF FF FF FF FF FF FF FF FF FF 2C 18 FF FF FF FF FF FF FF FF FF FF FF FF FF FF 2C CB FF FF FF FF FF FF FF FF FF FF FF FF FF FF 2C A8 FF FF FF FF FF FF FF FF FF FF FF FF FF FF 2C

Because 34 2e 33 32 is equals to 4.32.

I don't understand and don't know how I can "merge" and convert a float value. And then could you help me move the pointer following the size of the float?

I'm really stuck with that topic and I would really appreciate your help.

dda
  • 6,030
  • 2
  • 25
  • 34
martin10
  • 187
  • 3
  • 16
  • 5
    How many distinct values do you think a `uint8_t` can represent? How many values are there for two decimal digits each before and after the decimal separator? – EOF Sep 19 '17 at 19:27
  • 3
    uint8_t is only 8 bits, so only 256 possible values. A double with 1 digit + 2 decimals has 1000 possible values, so it is not possible. – stark Sep 19 '17 at 19:29
  • 1
    Are you simply asking how to round a number to 2 decimal places? Or are you really asking to place it in a `uint8_t`? – ikegami Sep 19 '17 at 19:31
  • `((int)(luminosity * 100))/100.0` should do it for you – Ajay Brahmakshatriya Sep 19 '17 at 19:31
  • As @ikegami is implying, I smell the presence of the [X/Y problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) here. – Tim Čas Sep 19 '17 at 19:34
  • 1
    "*I gess that uint8 does not keep the decimal*". Well it's an [integer](https://en.wikipedia.org/wiki/Integer) for starters. It has no decimals by definition. – gre_gor Sep 19 '17 at 19:44
  • Search keyword: Fixed point. – user4581301 Sep 19 '17 at 19:46
  • @user4581301, Fixed point requires knowing the possible range of luminosities. If you know that, you can get better precision than fixed point. (See my Answer for how.) The advantage of fixed point is that you can do arithmetic (add/sub/mul/div) with fixed point numbers. – ikegami Sep 19 '17 at 20:50
  • `32 33 2e 34 35` is a 5-byte **character string**, not an integer. – Remy Lebeau Sep 19 '17 at 22:48
  • " Some functions return a double to 6 decimal places" . No, they don't, as you can see by printing that value to 15 decimal places. If your hypothesis would be true, the last 9 digits would be zeroes. – MSalters Sep 20 '17 at 08:29
  • I am sorry, I corrected/adapted my description of my problem and I did see that people provided me answers. – martin10 Sep 20 '17 at 21:51
  • BTW, you do know that `sizeof (char)` is 1 by definition, right? – Toby Speight Sep 21 '17 at 15:38

2 Answers2

3
  • If you want to round towards zero after two decimal places, you can use the following:

    double lum = ((int)(luminosity * 100))/100.0;
    
  • If you want to round to nearest (round half away from zero) after two decimal places, you can use the following:

    #include <math.h>
    double lum = round(luminosity * 100)/100.0;
    

  • If, on the other hand, you want to store the luminosity into 8 bits as efficiently as possible, you can use the following algorithm.

    uint8_t packed_luminosity;
    if (luminosity >= MAX_LUMINOSITY)       // Just in case
       packed_luminosity = 255;
    else if (luminosity <= MIN_LUMINOSITY)  // Just in case
       packed_luminosity = 0;
    else
       packed_luminosity = (uint8_t)(
           ( luminosity - MIN_LUMINOSITY ) / ( MAX_LUMINOSITY - MIN_LUMINOSITY + 1 ) * 256 );
    

    You can perform comparisons and certain operations on the luminosity in this format.

  • Another way to pack a decimal number into an integer is as a fixed-point number. This works by picking a scaling for all of your luminosities when you write your program. Here are the ranges supported by a some scalings:

    uint8_t
    =======
    Scaling  Supported range (binary)         Supported range (decimal)
    -------  -------------------------------  -----------------------------------------
    ...
    B8       [00000000.    .. 111111110.   ]  [0 .. 512)  No odd numbers
    B7       [ 0000000.    ..  11111111.   ]  [0 .. 256)  No decimals
    B6       [  000000.0   ..   1111111.1  ]  [0 .. 128)  .0, .5
    B5       [   00000.00  ..    111111.11 ]  [0 ..  64)  .0, .25, .5, .75
    B4       [    0000.000 ..     11111.111]  [0 ..  32)  Almost one full decimal place
    ...
    
    int8_t
    ======
    Scaling  Supported range (binary)        Supported range (decimal)
    -------  ------------------------------  --------------------------------------------
    ...
    B8       [10000000.   .. 011111110.   ]  [-256 .. 256)  No odd numbers
    B7       [ 1000000.   ..  01111111.   ]  [-128 .. 128)  No decimals
    B6       [  100000.0  ..   0111111.1  ]  [ -64 ..  64)  .0, .5
    B5       [   10000.00 ..    011111.11 ]  [ -32 ..  32)  .0, .25, .5, .75
    B4       [    1000.000 ..    01111.111]  [ -16 ..  16)  Almost one full decimal place
    ...
    

    As you can see, only using 8 bits is very limiting, and you don't get the same flexibility of picking the ranges as the earlier method. The advantage of fixed point numbers is that you can perform arithmetic functions (addition, subtraction, multiplication and division) on pairs of them.

ikegami
  • 367,544
  • 15
  • 269
  • 518
  • C11 draft standard n1570: *6.3 Conversions 6.3.1.4 Real floating and integer 1 When a finite value of real floating type is converted to an integer type other than _Bool, the fractional part is discarded (i.e., the value is truncated toward zero).* Note the "truncated" rather than "rounded" here. – EOF Sep 19 '17 at 19:58
  • The OP doesn't want to truncate; the result should still have decimal places. – ikegami Sep 19 '17 at 21:01
0

(I know nothing about Arduino)

The minimal change that will get you close to your objectives is:

double luminosity;
uint32_t lum;
lum = (uint32_t)(luminosity * 100);

Now, lum will contain your value multiplied by a hundred, rounded down.

Instead of getting 23.45, you'll get 2345. Using uint8_t would limit you too much, as the range is too small (0-255). Whether you have uint16_t and uint32_t available, I do not know.

This technique is called fixed point arithmetic.

Jeffrey
  • 11,063
  • 1
  • 21
  • 42
  • arduino uses 8 bit micro & 32 bit arthmetics is a bit too expensive for it. uint16_t is enough I think (multiplications and divisions of 32bit integers are almost same expensive as operations on floats) – 0___________ Sep 19 '17 at 20:39